Vendor at Purchase Form
This commit is contained in:
parent
20203942d2
commit
0b51b29474
22
src/services/mutations/purchaseOrder.ts
Normal file
22
src/services/mutations/purchaseOrder.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { toast } from 'react-toastify'
|
||||
import { api } from '../api'
|
||||
import { PurchaseOrderRequest } from '@/types/services/purchaseOrder'
|
||||
|
||||
export const useVendorsMutation = () => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const createVendor = useMutation({
|
||||
mutationFn: async (newVendor: PurchaseOrderRequest) => {
|
||||
const response = await api.post('/vendors', newVendor)
|
||||
return response.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success('Vendor created successfully!')
|
||||
queryClient.invalidateQueries({ queryKey: ['vendors'] })
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast.error(error.response?.data?.errors?.[0]?.cause || 'Create failed')
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -35,6 +35,16 @@ export function useVendors(params: VendorQueryParams = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
export function useVendorActive() {
|
||||
return useQuery<Vendor[]>({
|
||||
queryKey: ['vendors/active'],
|
||||
queryFn: async () => {
|
||||
const res = await api.get(`/vendors/active`)
|
||||
return res.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function useVendorById(id: string) {
|
||||
return useQuery<Vendor>({
|
||||
queryKey: ['vendors', id],
|
||||
|
||||
19
src/types/services/purchaseOrder.ts
Normal file
19
src/types/services/purchaseOrder.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export interface PurchaseOrderRequest {
|
||||
vendor_id: string // uuid.UUID
|
||||
po_number: string
|
||||
transaction_date: string // ISO date string
|
||||
due_date: string // ISO date string
|
||||
reference?: string
|
||||
status?: 'draft' | 'sent' | 'approved' | 'received' | 'cancelled'
|
||||
message?: string
|
||||
items: PurchaseOrderItemRequest[]
|
||||
attachment_file_ids?: string[] // uuid.UUID[]
|
||||
}
|
||||
|
||||
export interface PurchaseOrderItemRequest {
|
||||
ingredient_id: string // uuid.UUID
|
||||
description?: string
|
||||
quantity: number
|
||||
unit_id: string // uuid.UUID
|
||||
amount: number
|
||||
}
|
||||
121
src/views/apps/purchase/purchase-form copy/PurchaseAddForm.tsx
Normal file
121
src/views/apps/purchase/purchase-form copy/PurchaseAddForm.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState } from 'react'
|
||||
import { Card, CardContent } from '@mui/material'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import { IngredientItem, PurchaseOrderFormData } from '@/types/apps/purchaseOrderTypes'
|
||||
import PurchaseBasicInfo from './PurchaseBasicInfo'
|
||||
import PurchaseIngredientsTable from './PurchaseIngredientsTable'
|
||||
import PurchaseSummary from './PurchaseSummary'
|
||||
|
||||
const PurchaseAddForm: React.FC = () => {
|
||||
const [formData, setFormData] = useState<PurchaseOrderFormData>({
|
||||
vendor: null,
|
||||
nomor: 'PO/00043',
|
||||
tglTransaksi: '2025-09-09',
|
||||
tglJatuhTempo: '2025-09-10',
|
||||
referensi: '',
|
||||
termin: null,
|
||||
hargaTermasukPajak: true,
|
||||
// Shipping info
|
||||
showShippingInfo: false,
|
||||
tanggalPengiriman: '',
|
||||
ekspedisi: null,
|
||||
noResi: '',
|
||||
// Bottom section toggles
|
||||
showPesan: false,
|
||||
showAttachment: false,
|
||||
showTambahDiskon: false,
|
||||
showBiayaPengiriman: false,
|
||||
showBiayaTransaksi: false,
|
||||
showUangMuka: false,
|
||||
pesan: '',
|
||||
// Ingredient items (updated from productItems)
|
||||
ingredientItems: [
|
||||
{
|
||||
id: 1,
|
||||
ingredient: null,
|
||||
deskripsi: '',
|
||||
kuantitas: 1,
|
||||
satuan: null,
|
||||
discount: '0',
|
||||
harga: 0,
|
||||
pajak: null,
|
||||
waste: null,
|
||||
total: 0
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const handleInputChange = (field: keyof PurchaseOrderFormData, value: any): void => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}))
|
||||
}
|
||||
|
||||
const handleIngredientChange = (index: number, field: keyof IngredientItem, value: any): void => {
|
||||
setFormData(prev => {
|
||||
const newItems = [...prev.ingredientItems]
|
||||
newItems[index] = { ...newItems[index], [field]: value }
|
||||
|
||||
// Auto-calculate total if price or quantity changes
|
||||
if (field === 'harga' || field === 'kuantitas') {
|
||||
const item = newItems[index]
|
||||
item.total = item.harga * item.kuantitas
|
||||
}
|
||||
|
||||
return { ...prev, ingredientItems: newItems }
|
||||
})
|
||||
}
|
||||
|
||||
const addIngredientItem = (): void => {
|
||||
const newItem: IngredientItem = {
|
||||
id: Date.now(),
|
||||
ingredient: null,
|
||||
deskripsi: '',
|
||||
kuantitas: 1,
|
||||
satuan: null,
|
||||
discount: '0%',
|
||||
harga: 0,
|
||||
pajak: null,
|
||||
waste: null,
|
||||
total: 0
|
||||
}
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
ingredientItems: [...prev.ingredientItems, newItem]
|
||||
}))
|
||||
}
|
||||
|
||||
const removeIngredientItem = (index: number): void => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
ingredientItems: prev.ingredientItems.filter((_, i) => i !== index)
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Grid container spacing={3}>
|
||||
{/* Basic Info Section */}
|
||||
<PurchaseBasicInfo formData={formData} handleInputChange={handleInputChange} />
|
||||
|
||||
{/* Ingredients Table Section */}
|
||||
<PurchaseIngredientsTable
|
||||
formData={formData}
|
||||
handleIngredientChange={handleIngredientChange}
|
||||
addIngredientItem={addIngredientItem}
|
||||
removeIngredientItem={removeIngredientItem}
|
||||
/>
|
||||
|
||||
{/* Summary Section */}
|
||||
<PurchaseSummary formData={formData} handleInputChange={handleInputChange} />
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default PurchaseAddForm
|
||||
197
src/views/apps/purchase/purchase-form copy/PurchaseBasicInfo.tsx
Normal file
197
src/views/apps/purchase/purchase-form copy/PurchaseBasicInfo.tsx
Normal file
@ -0,0 +1,197 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { Button, Switch, FormControlLabel } from '@mui/material'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import CustomAutocomplete from '@/@core/components/mui/Autocomplete'
|
||||
import CustomTextField from '@/@core/components/mui/TextField'
|
||||
import { DropdownOption, PurchaseOrderFormData } from '@/types/apps/purchaseOrderTypes'
|
||||
|
||||
interface PurchaseBasicInfoProps {
|
||||
formData: PurchaseOrderFormData
|
||||
handleInputChange: (field: keyof PurchaseOrderFormData, value: any) => void
|
||||
}
|
||||
|
||||
const PurchaseBasicInfo: React.FC<PurchaseBasicInfoProps> = ({ formData, handleInputChange }) => {
|
||||
// Sample data for dropdowns
|
||||
const vendorOptions: DropdownOption[] = [
|
||||
{ label: 'Vendor A', value: 'vendor_a' },
|
||||
{ label: 'Vendor B', value: 'vendor_b' },
|
||||
{ label: 'Vendor C', value: 'vendor_c' }
|
||||
]
|
||||
|
||||
const terminOptions: DropdownOption[] = [
|
||||
{ label: 'Net 30', value: 'net_30' },
|
||||
{ label: 'Net 15', value: 'net_15' },
|
||||
{ label: 'Net 60', value: 'net_60' },
|
||||
{ label: 'Cash on Delivery', value: 'cod' }
|
||||
]
|
||||
|
||||
const ekspedisiOptions: DropdownOption[] = [
|
||||
{ label: 'JNE', value: 'jne' },
|
||||
{ label: 'J&T Express', value: 'jnt' },
|
||||
{ label: 'SiCepat', value: 'sicepat' },
|
||||
{ label: 'Pos Indonesia', value: 'pos' },
|
||||
{ label: 'TIKI', value: 'tiki' }
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Row 1 - Vendor dan Nomor */}
|
||||
<Grid size={{ xs: 12, sm: 6, md: 6 }}>
|
||||
<CustomAutocomplete
|
||||
fullWidth
|
||||
options={vendorOptions}
|
||||
value={formData.vendor}
|
||||
onChange={(event, newValue) => handleInputChange('vendor', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} label='Vendor' placeholder='Pilih kontak' fullWidth />}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, md: 6 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Nomor'
|
||||
value={formData.nomor}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange('nomor', e.target.value)}
|
||||
InputProps={{
|
||||
readOnly: true
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Row 2 - Tgl. Transaksi, Tgl. Jatuh Tempo, Termin */}
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Tgl. Transaksi'
|
||||
type='date'
|
||||
value={formData.tglTransaksi}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange('tglTransaksi', e.target.value)}
|
||||
InputLabelProps={{
|
||||
shrink: true
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Tgl. Jatuh Tempo'
|
||||
type='date'
|
||||
value={formData.tglJatuhTempo}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange('tglJatuhTempo', e.target.value)}
|
||||
InputLabelProps={{
|
||||
shrink: true
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomAutocomplete
|
||||
fullWidth
|
||||
options={terminOptions}
|
||||
value={formData.termin}
|
||||
onChange={(event, newValue) => handleInputChange('termin', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} label='Termin' placeholder='Net 30' fullWidth />}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* Row 3 - Tampilkan Informasi Pengiriman */}
|
||||
<Grid size={12}>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
onClick={() => handleInputChange('showShippingInfo', !formData.showShippingInfo)}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
padding: '8px 0',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1
|
||||
}}
|
||||
>
|
||||
{formData.showShippingInfo ? '−' : '+'} {formData.showShippingInfo ? 'Sembunyikan' : 'Tampilkan'} Informasi
|
||||
Pengiriman
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
{/* Shipping Information - Conditional */}
|
||||
{formData.showShippingInfo && (
|
||||
<>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Tanggal Pengiriman'
|
||||
type='date'
|
||||
placeholder='Pilih tanggal'
|
||||
value={formData.tanggalPengiriman}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleInputChange('tanggalPengiriman', e.target.value)
|
||||
}
|
||||
InputLabelProps={{
|
||||
shrink: true
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomAutocomplete
|
||||
fullWidth
|
||||
options={ekspedisiOptions}
|
||||
value={formData.ekspedisi}
|
||||
onChange={(event, newValue) => handleInputChange('ekspedisi', newValue)}
|
||||
renderInput={params => (
|
||||
<CustomTextField {...params} label='Ekspedisi' placeholder='Pilih ekspedisi' fullWidth />
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='No. Resi'
|
||||
placeholder='No. Resi'
|
||||
value={formData.noResi}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange('noResi', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Row 4 - Referensi, SKU, Switch Pajak */}
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='Referensi'
|
||||
placeholder='Referensi'
|
||||
value={formData.referensi}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange('referensi', e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }}>
|
||||
<CustomTextField fullWidth label='SKU' placeholder='Scan Barcode/SKU' variant='outlined' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 4 }} sx={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={formData.hargaTermasukPajak}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleInputChange('hargaTermasukPajak', e.target.checked)
|
||||
}
|
||||
color='primary'
|
||||
/>
|
||||
}
|
||||
label='Harga termasuk pajak'
|
||||
sx={{
|
||||
marginLeft: 0,
|
||||
'& .MuiFormControlLabel-label': {
|
||||
fontSize: '14px',
|
||||
color: 'text.secondary'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PurchaseBasicInfo
|
||||
@ -0,0 +1,225 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
IconButton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper
|
||||
} from '@mui/material'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import CustomAutocomplete from '@/@core/components/mui/Autocomplete'
|
||||
import CustomTextField from '@/@core/components/mui/TextField'
|
||||
import { IngredientItem, PurchaseOrderFormData } from '@/types/apps/purchaseOrderTypes'
|
||||
|
||||
interface PurchaseIngredientsTableProps {
|
||||
formData: PurchaseOrderFormData
|
||||
handleIngredientChange: (index: number, field: keyof IngredientItem, value: any) => void
|
||||
addIngredientItem: () => void
|
||||
removeIngredientItem: (index: number) => void
|
||||
}
|
||||
|
||||
const PurchaseIngredientsTable: React.FC<PurchaseIngredientsTableProps> = ({
|
||||
formData,
|
||||
handleIngredientChange,
|
||||
addIngredientItem,
|
||||
removeIngredientItem
|
||||
}) => {
|
||||
const ingredientOptions = [
|
||||
{ label: 'Tepung Terigu Premium', value: 'tepung_terigu_premium' },
|
||||
{ label: 'Gula Pasir Halus', value: 'gula_pasir_halus' },
|
||||
{ label: 'Mentega Unsalted', value: 'mentega_unsalted' },
|
||||
{ label: 'Telur Ayam Grade A', value: 'telur_ayam_grade_a' },
|
||||
{ label: 'Vanilla Extract', value: 'vanilla_extract' },
|
||||
{ label: 'Coklat Chips', value: 'coklat_chips' }
|
||||
]
|
||||
|
||||
const satuanOptions = [
|
||||
{ label: 'KG', value: 'kg' },
|
||||
{ label: 'GRAM', value: 'gram' },
|
||||
{ label: 'LITER', value: 'liter' },
|
||||
{ label: 'ML', value: 'ml' },
|
||||
{ label: 'PCS', value: 'pcs' },
|
||||
{ label: 'PACK', value: 'pack' }
|
||||
]
|
||||
|
||||
const pajakOptions = [
|
||||
{ label: 'PPN 11%', value: 'ppn_11' },
|
||||
{ label: 'PPN 0%', value: 'ppn_0' },
|
||||
{ label: 'Bebas Pajak', value: 'tax_free' }
|
||||
]
|
||||
|
||||
const wasteOptions = [
|
||||
{ label: '2%', value: '2' },
|
||||
{ label: '5%', value: '5' },
|
||||
{ label: '10%', value: '10' },
|
||||
{ label: '15%', value: '15' },
|
||||
{ label: 'Custom', value: 'custom' }
|
||||
]
|
||||
|
||||
return (
|
||||
<Grid size={{ xs: 12 }} sx={{ mt: 4 }}>
|
||||
<Typography variant='h6' sx={{ mb: 2, fontWeight: 600 }}>
|
||||
Bahan Baku / Ingredients
|
||||
</Typography>
|
||||
|
||||
<TableContainer component={Paper} variant='outlined'>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow sx={{ backgroundColor: 'grey.50' }}>
|
||||
<TableCell sx={{ fontWeight: 'bold', minWidth: 180 }}>Bahan Baku</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', minWidth: 150 }}>Deskripsi</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 100 }}>Kuantitas</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 120 }}>Satuan</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 100 }}>Discount</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 120 }}>Harga</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 120 }}>Pajak</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 120 }}>Waste</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold', width: 100, textAlign: 'right' }}>Total</TableCell>
|
||||
<TableCell sx={{ width: 50 }}></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{formData.ingredientItems.map((item: IngredientItem, index: number) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell>
|
||||
<CustomAutocomplete
|
||||
size='small'
|
||||
options={ingredientOptions}
|
||||
value={item.ingredient}
|
||||
onChange={(event, newValue) => handleIngredientChange(index, 'ingredient', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} placeholder='Pilih Bahan Baku' />}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
size='small'
|
||||
value={item.deskripsi}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleIngredientChange(index, 'deskripsi', e.target.value)
|
||||
}
|
||||
placeholder='Deskripsi'
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
size='small'
|
||||
type='number'
|
||||
value={item.kuantitas}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleIngredientChange(index, 'kuantitas', parseInt(e.target.value) || 1)
|
||||
}
|
||||
inputProps={{ min: 1 }}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomAutocomplete
|
||||
size='small'
|
||||
options={satuanOptions}
|
||||
value={item.satuan}
|
||||
onChange={(event, newValue) => handleIngredientChange(index, 'satuan', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} placeholder='Pilih...' />}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
size='small'
|
||||
value={item.discount}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleIngredientChange(index, 'discount', e.target.value)
|
||||
}
|
||||
placeholder='0%'
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
size='small'
|
||||
type='number'
|
||||
value={item.harga === 0 ? '' : item.harga?.toString() || ''}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
|
||||
if (value === '') {
|
||||
handleIngredientChange(index, 'harga', null)
|
||||
return
|
||||
}
|
||||
|
||||
const numericValue = parseFloat(value)
|
||||
handleIngredientChange(index, 'harga', isNaN(numericValue) ? 0 : numericValue)
|
||||
}}
|
||||
inputProps={{ min: 0, step: 'any' }}
|
||||
placeholder='0'
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomAutocomplete
|
||||
size='small'
|
||||
options={pajakOptions}
|
||||
value={item.pajak}
|
||||
onChange={(event, newValue) => handleIngredientChange(index, 'pajak', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} placeholder='...' />}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomAutocomplete
|
||||
size='small'
|
||||
options={wasteOptions}
|
||||
value={item.waste}
|
||||
onChange={(event, newValue) => handleIngredientChange(index, 'waste', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} placeholder='...' />}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
size='small'
|
||||
value={item.total}
|
||||
InputProps={{ readOnly: true }}
|
||||
sx={{
|
||||
'& .MuiInputBase-input': {
|
||||
textAlign: 'right'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
size='small'
|
||||
color='error'
|
||||
onClick={() => removeIngredientItem(index)}
|
||||
disabled={formData.ingredientItems.length === 1}
|
||||
>
|
||||
<i className='tabler-trash' />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
{/* Add New Item Button */}
|
||||
<Button
|
||||
startIcon={<i className='tabler-plus' />}
|
||||
onClick={addIngredientItem}
|
||||
variant='outlined'
|
||||
size='small'
|
||||
sx={{ mt: 1 }}
|
||||
>
|
||||
Tambah bahan baku
|
||||
</Button>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default PurchaseIngredientsTable
|
||||
589
src/views/apps/purchase/purchase-form copy/PurchaseSummary.tsx
Normal file
589
src/views/apps/purchase/purchase-form copy/PurchaseSummary.tsx
Normal file
@ -0,0 +1,589 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { Button, Typography, Box, ToggleButton, ToggleButtonGroup, InputAdornment, IconButton } from '@mui/material'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import CustomTextField from '@/@core/components/mui/TextField'
|
||||
import { PurchaseOrderFormData, TransactionCost } from '@/types/apps/purchaseOrderTypes'
|
||||
import CustomAutocomplete from '@/@core/components/mui/Autocomplete'
|
||||
import ImageUpload from '@/components/ImageUpload'
|
||||
|
||||
interface PurchaseSummaryProps {
|
||||
formData: PurchaseOrderFormData
|
||||
handleInputChange: (field: keyof PurchaseOrderFormData, value: any) => void
|
||||
}
|
||||
|
||||
const PurchaseSummary: React.FC<PurchaseSummaryProps> = ({ formData, handleInputChange }) => {
|
||||
// Initialize transaction costs if not exist
|
||||
const transactionCosts = formData.transactionCosts || []
|
||||
|
||||
// Options for transaction cost types
|
||||
const transactionCostOptions = [
|
||||
{ label: 'Biaya Admin', value: 'admin' },
|
||||
{ label: 'Pajak', value: 'pajak' },
|
||||
{ label: 'Materai', value: 'materai' },
|
||||
{ label: 'Lainnya', value: 'lainnya' }
|
||||
]
|
||||
|
||||
// Add new transaction cost
|
||||
const addTransactionCost = () => {
|
||||
const newCost: TransactionCost = {
|
||||
id: Date.now().toString(),
|
||||
type: '',
|
||||
name: '',
|
||||
amount: ''
|
||||
}
|
||||
handleInputChange('transactionCosts', [...transactionCosts, newCost])
|
||||
}
|
||||
|
||||
// Remove transaction cost
|
||||
const removeTransactionCost = (id: string) => {
|
||||
const filtered = transactionCosts.filter((cost: TransactionCost) => cost.id !== id)
|
||||
handleInputChange('transactionCosts', filtered)
|
||||
}
|
||||
|
||||
// Update transaction cost
|
||||
const updateTransactionCost = (id: string, field: keyof TransactionCost, value: string) => {
|
||||
const updated = transactionCosts.map((cost: TransactionCost) =>
|
||||
cost.id === id ? { ...cost, [field]: value } : cost
|
||||
)
|
||||
handleInputChange('transactionCosts', updated)
|
||||
}
|
||||
|
||||
// Calculate discount amount based on percentage or fixed amount
|
||||
const calculateDiscount = () => {
|
||||
if (!formData.discountValue) return 0
|
||||
|
||||
const subtotal = formData.subtotal || 0
|
||||
if (formData.discountType === 'percentage') {
|
||||
return (subtotal * parseFloat(formData.discountValue)) / 100
|
||||
}
|
||||
return parseFloat(formData.discountValue)
|
||||
}
|
||||
|
||||
const discountAmount = calculateDiscount()
|
||||
const shippingCost = parseFloat(formData.shippingCost || '0')
|
||||
|
||||
// Calculate total transaction costs
|
||||
const totalTransactionCost = transactionCosts.reduce((sum: number, cost: TransactionCost) => {
|
||||
return sum + parseFloat(cost.amount || '0')
|
||||
}, 0)
|
||||
|
||||
const downPayment = parseFloat(formData.downPayment || '0')
|
||||
|
||||
// Calculate total (subtotal - discount + shipping + transaction costs)
|
||||
const total = (formData.subtotal || 0) - discountAmount + shippingCost + totalTransactionCost
|
||||
|
||||
// Calculate remaining balance (total - down payment)
|
||||
const remainingBalance = total - downPayment
|
||||
|
||||
const handleUpload = async (file: File): Promise<string> => {
|
||||
// Simulate upload
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve(URL.createObjectURL(file))
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid size={12} sx={{ mt: 4 }}>
|
||||
<Grid container spacing={3}>
|
||||
{/* Left Side - Pesan and Attachment */}
|
||||
<Grid size={{ xs: 12, md: 7 }}>
|
||||
{/* Pesan Section */}
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Button
|
||||
variant='text'
|
||||
color='inherit'
|
||||
onClick={() => handleInputChange('showPesan', !formData.showPesan)}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
padding: '12px 16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
backgroundColor: '#f5f5f5',
|
||||
border: '1px solid #e0e0e0',
|
||||
borderRadius: '4px',
|
||||
color: 'text.primary',
|
||||
'&:hover': {
|
||||
backgroundColor: '#eeeeee'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box component='span' sx={{ mr: 1 }}>
|
||||
{formData.showPesan ? (
|
||||
<i className='tabler-chevron-down w-4 h-4' />
|
||||
) : (
|
||||
<i className='tabler-chevron-right w-4 h-4' />
|
||||
)}
|
||||
</Box>
|
||||
Pesan
|
||||
</Button>
|
||||
{formData.showPesan && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
multiline
|
||||
rows={3}
|
||||
placeholder='Tambahkan pesan...'
|
||||
value={formData.pesan || ''}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleInputChange('pesan', e.target.value)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Attachment Section */}
|
||||
<Box>
|
||||
<Button
|
||||
variant='text'
|
||||
color='inherit'
|
||||
onClick={() => handleInputChange('showAttachment', !formData.showAttachment)}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontSize: '14px',
|
||||
fontWeight: 500,
|
||||
padding: '12px 16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
backgroundColor: '#f5f5f5',
|
||||
border: '1px solid #e0e0e0',
|
||||
borderRadius: '4px',
|
||||
color: 'text.primary',
|
||||
'&:hover': {
|
||||
backgroundColor: '#eeeeee'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box component='span' sx={{ mr: 1 }}>
|
||||
{formData.showAttachment ? (
|
||||
<i className='tabler-chevron-down w-4 h-4' />
|
||||
) : (
|
||||
<i className='tabler-chevron-right w-4 h-4' />
|
||||
)}
|
||||
</Box>
|
||||
Attachment
|
||||
</Button>
|
||||
{formData.showAttachment && (
|
||||
<ImageUpload
|
||||
onUpload={handleUpload}
|
||||
maxFileSize={1 * 1024 * 1024} // 1MB
|
||||
showUrlOption={false}
|
||||
dragDropText='Drop your image here'
|
||||
browseButtonText='Choose Image'
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
{/* Right Side - Totals */}
|
||||
<Grid size={{ xs: 12, md: 5 }}>
|
||||
<Box sx={{ backgroundColor: '#ffffff', p: 3, borderRadius: '8px' }}>
|
||||
{/* Sub Total */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f8f8f8'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='body1' color='text.secondary' sx={{ fontSize: '16px' }}>
|
||||
Sub Total
|
||||
</Typography>
|
||||
<Typography variant='body1' fontWeight={600} sx={{ fontSize: '16px', textAlign: 'right' }}>
|
||||
{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(formData.subtotal || 0)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Additional Options */}
|
||||
<Box>
|
||||
{/* Tambah Diskon */}
|
||||
<Box
|
||||
sx={{
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f8f8f8'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left' }}
|
||||
onClick={() => handleInputChange('showTambahDiskon', !formData.showTambahDiskon)}
|
||||
>
|
||||
{formData.showTambahDiskon ? '- Sembunyikan Diskon' : '+ Tambahan Diskon'}
|
||||
</Button>
|
||||
|
||||
{/* Show input form when showTambahDiskon is true */}
|
||||
{formData.showTambahDiskon && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center', mb: 1 }}>
|
||||
<CustomTextField
|
||||
size='small'
|
||||
placeholder='0'
|
||||
value={formData.discountValue || ''}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleInputChange('discountValue', e.target.value)
|
||||
}
|
||||
sx={{ flex: 1 }}
|
||||
InputProps={{
|
||||
endAdornment:
|
||||
formData.discountType === 'percentage' ? (
|
||||
<InputAdornment position='end'>%</InputAdornment>
|
||||
) : undefined
|
||||
}}
|
||||
/>
|
||||
<ToggleButtonGroup
|
||||
value={formData.discountType || 'percentage'}
|
||||
exclusive
|
||||
onChange={(_, newValue) => {
|
||||
if (newValue) handleInputChange('discountType', newValue)
|
||||
}}
|
||||
size='small'
|
||||
>
|
||||
<ToggleButton value='percentage' sx={{ px: 2 }}>
|
||||
%
|
||||
</ToggleButton>
|
||||
<ToggleButton value='fixed' sx={{ px: 2 }}>
|
||||
Rp
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Biaya Pengiriman */}
|
||||
<Box
|
||||
sx={{
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f8f8f8'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left' }}
|
||||
onClick={() => handleInputChange('showBiayaPengiriman', !formData.showBiayaPengiriman)}
|
||||
>
|
||||
{formData.showBiayaPengiriman ? '- Sembunyikan Biaya Pengiriman' : '+ Biaya pengiriman'}
|
||||
</Button>
|
||||
|
||||
{/* Show input form when showBiayaPengiriman is true */}
|
||||
{formData.showBiayaPengiriman && (
|
||||
<Box sx={{ mt: 2, display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Typography variant='body2' sx={{ minWidth: '140px' }}>
|
||||
Biaya pengiriman
|
||||
</Typography>
|
||||
<CustomTextField
|
||||
size='small'
|
||||
placeholder='0'
|
||||
value={formData.shippingCost || ''}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleInputChange('shippingCost', e.target.value)
|
||||
}
|
||||
sx={{ flex: 1 }}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position='start'>Rp</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Biaya Transaksi - Multiple */}
|
||||
<Box
|
||||
sx={{
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f8f8f8'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left' }}
|
||||
onClick={() => {
|
||||
if (!formData.showBiayaTransaksi) {
|
||||
handleInputChange('showBiayaTransaksi', true)
|
||||
if (transactionCosts.length === 0) {
|
||||
addTransactionCost()
|
||||
}
|
||||
} else {
|
||||
handleInputChange('showBiayaTransaksi', false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{formData.showBiayaTransaksi ? '- Sembunyikan Biaya Transaksi' : '+ Biaya Transaksi'}
|
||||
</Button>
|
||||
|
||||
{/* Show multiple transaction cost inputs */}
|
||||
{formData.showBiayaTransaksi && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
{transactionCosts.map((cost: TransactionCost, index: number) => (
|
||||
<Box key={cost.id} sx={{ display: 'flex', gap: 1, alignItems: 'center', mb: 2 }}>
|
||||
{/* Remove button */}
|
||||
<IconButton
|
||||
size='small'
|
||||
onClick={() => removeTransactionCost(cost.id)}
|
||||
sx={{
|
||||
color: 'error.main',
|
||||
border: '1px solid',
|
||||
borderColor: 'error.main',
|
||||
borderRadius: '50%',
|
||||
width: 28,
|
||||
height: 28,
|
||||
'&:hover': {
|
||||
backgroundColor: 'error.lighter'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i className='tabler-trash' />
|
||||
</IconButton>
|
||||
|
||||
{/* Type AutoComplete */}
|
||||
<CustomAutocomplete
|
||||
size='small'
|
||||
options={transactionCostOptions}
|
||||
getOptionLabel={option => (typeof option === 'string' ? option : option.label)}
|
||||
value={transactionCostOptions.find(option => option.value === cost.type) || null}
|
||||
onChange={(_, newValue) => {
|
||||
updateTransactionCost(cost.id, 'type', newValue ? newValue.value : '')
|
||||
}}
|
||||
renderInput={params => (
|
||||
<CustomTextField {...params} size='small' placeholder='Pilih biaya transaksi...' />
|
||||
)}
|
||||
sx={{ minWidth: 180 }}
|
||||
noOptionsText='Tidak ada pilihan'
|
||||
/>
|
||||
|
||||
{/* Name input */}
|
||||
<CustomTextField
|
||||
size='small'
|
||||
placeholder='Nama'
|
||||
value={cost.name}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
updateTransactionCost(cost.id, 'name', e.target.value)
|
||||
}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
|
||||
{/* Amount input */}
|
||||
<CustomTextField
|
||||
size='small'
|
||||
placeholder='0'
|
||||
value={cost.amount}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
updateTransactionCost(cost.id, 'amount', e.target.value)
|
||||
}
|
||||
sx={{ width: 120 }}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position='start'>Rp</InputAdornment>
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
|
||||
{/* Add more button */}
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
onClick={addTransactionCost}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontSize: '13px',
|
||||
mt: 1
|
||||
}}
|
||||
>
|
||||
+ Tambah biaya transaksi lain
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Total */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f8f8f8'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='h6' fontWeight={600} sx={{ fontSize: '18px' }}>
|
||||
Total
|
||||
</Typography>
|
||||
<Typography variant='h6' fontWeight={600} sx={{ fontSize: '18px', textAlign: 'right' }}>
|
||||
{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(total)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Uang Muka */}
|
||||
<Box
|
||||
sx={{
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f8f8f8'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left' }}
|
||||
onClick={() => handleInputChange('showUangMuka', !formData.showUangMuka)}
|
||||
>
|
||||
{formData.showUangMuka ? '- Sembunyikan Uang Muka' : '+ Uang Muka'}
|
||||
</Button>
|
||||
{formData.showUangMuka && (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{/* Dropdown */}
|
||||
<CustomAutocomplete
|
||||
size='small'
|
||||
options={[{ label: '1-10003 Gi...', value: '1-10003' }]}
|
||||
getOptionLabel={option => (typeof option === 'string' ? option : option.label)}
|
||||
value={{ label: '1-10003 Gi...', value: '1-10003' }}
|
||||
onChange={(_, newValue) => {
|
||||
// Handle change if needed
|
||||
}}
|
||||
renderInput={params => <CustomTextField {...params} size='small' />}
|
||||
sx={{ minWidth: 120 }}
|
||||
/>
|
||||
|
||||
{/* Amount input */}
|
||||
<CustomTextField
|
||||
size='small'
|
||||
placeholder='0'
|
||||
value={formData.downPayment || '0'}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
handleInputChange('downPayment', e.target.value)
|
||||
}
|
||||
sx={{ width: '80px' }}
|
||||
inputProps={{
|
||||
style: { textAlign: 'center' }
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Percentage/Fixed toggle */}
|
||||
<ToggleButtonGroup
|
||||
value={formData.downPaymentType || 'fixed'}
|
||||
exclusive
|
||||
onChange={(_, newValue) => {
|
||||
if (newValue) handleInputChange('downPaymentType', newValue)
|
||||
}}
|
||||
size='small'
|
||||
>
|
||||
<ToggleButton value='percentage' sx={{ px: 1.5 }}>
|
||||
%
|
||||
</ToggleButton>
|
||||
<ToggleButton value='fixed' sx={{ px: 1.5 }}>
|
||||
Rp
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
</Box>
|
||||
|
||||
{/* Right side text */}
|
||||
<Typography
|
||||
variant='body1'
|
||||
sx={{
|
||||
fontSize: '16px',
|
||||
fontWeight: 400
|
||||
}}
|
||||
>
|
||||
Uang muka {downPayment > 0 ? downPayment.toLocaleString('id-ID') : '0'}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* Sisa Tagihan */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
backgroundColor: '#f5f5f5',
|
||||
borderRadius: '4px',
|
||||
mb: 3,
|
||||
'&:hover': {
|
||||
backgroundColor: '#eeeeee'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='body1' color='text.primary' sx={{ fontSize: '16px', fontWeight: 600 }}>
|
||||
Sisa Tagihan
|
||||
</Typography>
|
||||
<Typography variant='body1' fontWeight={600} sx={{ fontSize: '16px', textAlign: 'right' }}>
|
||||
{new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
minimumFractionDigits: 0
|
||||
}).format(remainingBalance)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Save Button */}
|
||||
<Button
|
||||
variant='contained'
|
||||
color='primary'
|
||||
fullWidth
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontWeight: 600,
|
||||
py: 1.5,
|
||||
boxShadow: 'none',
|
||||
'&:hover': {
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default PurchaseSummary
|
||||
@ -1,11 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import { Button, Switch, FormControlLabel } from '@mui/material'
|
||||
import { Button, Switch, FormControlLabel, Box, Typography } from '@mui/material'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import CustomAutocomplete from '@/@core/components/mui/Autocomplete'
|
||||
import CustomTextField from '@/@core/components/mui/TextField'
|
||||
import { DropdownOption, PurchaseOrderFormData } from '@/types/apps/purchaseOrderTypes'
|
||||
import { useVendorActive } from '@/services/queries/vendor'
|
||||
|
||||
interface PurchaseBasicInfoProps {
|
||||
formData: PurchaseOrderFormData
|
||||
@ -13,12 +14,22 @@ interface PurchaseBasicInfoProps {
|
||||
}
|
||||
|
||||
const PurchaseBasicInfo: React.FC<PurchaseBasicInfoProps> = ({ formData, handleInputChange }) => {
|
||||
// Sample data for dropdowns
|
||||
const vendorOptions: DropdownOption[] = [
|
||||
{ label: 'Vendor A', value: 'vendor_a' },
|
||||
{ label: 'Vendor B', value: 'vendor_b' },
|
||||
{ label: 'Vendor C', value: 'vendor_c' }
|
||||
]
|
||||
const { data: vendors, isLoading } = useVendorActive()
|
||||
|
||||
// Transform vendors data to dropdown options
|
||||
const vendorOptions: DropdownOption[] =
|
||||
vendors?.map(vendor => ({
|
||||
label: vendor.name,
|
||||
value: vendor.id
|
||||
})) || []
|
||||
|
||||
// Function to get selected vendor data
|
||||
const getSelectedVendorData = () => {
|
||||
if (!formData.vendor?.value || !vendors) return null
|
||||
|
||||
const selectedVendor = vendors.find(vendor => vendor.id === (formData?.vendor?.value ?? ''))
|
||||
return selectedVendor
|
||||
}
|
||||
|
||||
const terminOptions: DropdownOption[] = [
|
||||
{ label: 'Net 30', value: 'net_30' },
|
||||
@ -43,9 +54,53 @@ const PurchaseBasicInfo: React.FC<PurchaseBasicInfoProps> = ({ formData, handleI
|
||||
fullWidth
|
||||
options={vendorOptions}
|
||||
value={formData.vendor}
|
||||
onChange={(event, newValue) => handleInputChange('vendor', newValue)}
|
||||
renderInput={params => <CustomTextField {...params} label='Vendor' placeholder='Pilih kontak' fullWidth />}
|
||||
onChange={(event, newValue) => {
|
||||
handleInputChange('vendor', newValue)
|
||||
|
||||
// Optional: Bisa langsung akses full data vendor saat berubah
|
||||
if (newValue?.value) {
|
||||
const selectedVendorData = vendors?.find(vendor => vendor.id === newValue.value)
|
||||
console.log('Vendor selected:', selectedVendorData)
|
||||
// Atau bisa trigger callback lain jika dibutuhkan
|
||||
}
|
||||
}}
|
||||
loading={isLoading}
|
||||
renderInput={params => (
|
||||
<CustomTextField
|
||||
{...params}
|
||||
label='Vendor'
|
||||
placeholder={isLoading ? 'Loading vendors...' : 'Pilih kontak'}
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{getSelectedVendorData() && (
|
||||
<Box className='space-y-1 mt-3'>
|
||||
{/* Nama Perum */}
|
||||
<Box className='flex items-center space-x-2'>
|
||||
<i className='tabler-user text-gray-500 w-3 h-3' />
|
||||
<Typography className='text-gray-700 font-medium text-xs'>
|
||||
{getSelectedVendorData()?.contact_person ?? ''}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Alamat */}
|
||||
<Box className='flex items-start space-x-2'>
|
||||
<i className='tabler-map text-gray-500 w-3 h-3' />
|
||||
<Typography className='text-gray-700 font-medium text-xs'>
|
||||
{getSelectedVendorData()?.address ?? '-'}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Nomor Telepon */}
|
||||
<Box className='flex items-center space-x-2'>
|
||||
<i className='tabler-phone text-gray-500 w-3 h-3' />
|
||||
<Typography className='text-gray-700 font-medium text-xs'>
|
||||
{getSelectedVendorData()?.phone_number ?? '-'}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 6, md: 6 }}>
|
||||
<CustomTextField
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user