efril #7
@ -0,0 +1,18 @@
|
||||
import PurchaseDetailContent from '@/views/apps/purchase/purchase-detail/PurchaseDetailContent'
|
||||
import PurchaseDetailHeader from '@/views/apps/purchase/purchase-detail/PurchaseDetailHeader'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
|
||||
const PurchaseOrderDetailPage = () => {
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailHeader title='Detail Pesanan Pembelian' />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailContent />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default PurchaseOrderDetailPage
|
||||
@ -1,4 +1,4 @@
|
||||
import { PurchaseOrders } from '@/types/services/purchaseOrder'
|
||||
import { PurchaseOrder, PurchaseOrders } from '@/types/services/purchaseOrder'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { api } from '../api'
|
||||
|
||||
@ -39,3 +39,13 @@ export function usePurchaseOrders(params: PurchaseOrderQueryParams = {}) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function usePurchaseOrderById(id: string) {
|
||||
return useQuery<PurchaseOrder>({
|
||||
queryKey: ['purchase-orders', id],
|
||||
queryFn: async () => {
|
||||
const res = await api.get(`/purchase-orders/${id}`)
|
||||
return res.data.data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,25 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import PurchaseDetailInformation from './PurchaseDetailInformation'
|
||||
import PurchaseDetailSendPayment from './PurchaseDetailSendPayment'
|
||||
import PurchaseDetailLog from './PurchaseDetailLog'
|
||||
import PurchaseDetailTransaction from './PurchaseDetailTransaction'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { usePurchaseOrderById } from '@/services/queries/purchaseOrder'
|
||||
import Loading from '@/components/layout/shared/Loading'
|
||||
|
||||
const PurchaseDetailContent = () => {
|
||||
const params = useParams()
|
||||
const { data, isLoading, error, isFetching } = usePurchaseOrderById(params.id as string)
|
||||
return (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailInformation />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailSendPayment />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailTransaction />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailLog />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<Grid container spacing={6}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailInformation data={data} />
|
||||
</Grid>
|
||||
{data?.status == 'sent' && (
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailSendPayment />
|
||||
</Grid>
|
||||
)}
|
||||
{/* <Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailTransaction />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<PurchaseDetailLog />
|
||||
</Grid> */}
|
||||
</Grid>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
Card,
|
||||
@ -15,87 +17,62 @@ import {
|
||||
IconButton
|
||||
} from '@mui/material'
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import { PurchaseOrder } from '@/types/services/purchaseOrder'
|
||||
|
||||
interface Product {
|
||||
produk: string
|
||||
deskripsi: string
|
||||
kuantitas: number
|
||||
satuan: string
|
||||
discount: string
|
||||
harga: number
|
||||
pajak: string
|
||||
jumlah: number
|
||||
interface Props {
|
||||
data?: PurchaseOrder
|
||||
}
|
||||
|
||||
interface PurchaseData {
|
||||
vendor: string
|
||||
nomor: string
|
||||
tglTransaksi: string
|
||||
tglJatuhTempo: string
|
||||
gudang: string
|
||||
status: string
|
||||
}
|
||||
const PurchaseDetailInformation = ({ data }: Props) => {
|
||||
const purchaseOrder = data
|
||||
|
||||
const PurchaseDetailInformation: React.FC = () => {
|
||||
const purchaseData: PurchaseData = {
|
||||
vendor: 'Bagas Rizki Sihotang S.Farm Widodo',
|
||||
nomor: 'PI/00053',
|
||||
tglTransaksi: '08/09/2025',
|
||||
tglJatuhTempo: '06/10/2025',
|
||||
gudang: 'Unassigned',
|
||||
status: 'Belum Dibayar'
|
||||
// Helper functions
|
||||
const formatDate = (dateString: string): string => {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('id-ID', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
const products: Product[] = [
|
||||
{
|
||||
produk: 'CB1 - Chelsea Boots',
|
||||
deskripsi: 'Ukuran XS',
|
||||
kuantitas: 3,
|
||||
satuan: 'Pcs',
|
||||
discount: '0%',
|
||||
harga: 299000,
|
||||
pajak: 'PPN',
|
||||
jumlah: 897000
|
||||
},
|
||||
{
|
||||
produk: 'CB1 - Chelsea Boots',
|
||||
deskripsi: 'Ukuran M',
|
||||
kuantitas: 1,
|
||||
satuan: 'Pcs',
|
||||
discount: '0%',
|
||||
harga: 299000,
|
||||
pajak: 'PPN',
|
||||
jumlah: 299000
|
||||
},
|
||||
{
|
||||
produk: 'KH1 - Kneel High Boots',
|
||||
deskripsi: 'Ukuran XL',
|
||||
kuantitas: 1,
|
||||
satuan: 'Pcs',
|
||||
discount: '0%',
|
||||
harga: 299000,
|
||||
pajak: 'PPN',
|
||||
jumlah: 299000
|
||||
}
|
||||
]
|
||||
|
||||
const totalKuantitas: number = products.reduce((sum, product) => sum + product.kuantitas, 0)
|
||||
const subTotal: number = 1495000
|
||||
const ppn: number = 98670
|
||||
const total: number = 1593670
|
||||
const sisaTagihan: number = 1593670
|
||||
|
||||
const formatCurrency = (amount: number): string => {
|
||||
return new Intl.NumberFormat('id-ID').format(amount)
|
||||
}
|
||||
|
||||
const getStatusLabel = (status: string): string => {
|
||||
const statusMap: Record<string, string> = {
|
||||
draft: 'Draft',
|
||||
sent: 'Dikirim',
|
||||
approved: 'Disetujui',
|
||||
received: 'Diterima',
|
||||
cancelled: 'Dibatalkan'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
const getStatusColor = (status: string): 'error' | 'success' | 'warning' | 'info' | 'default' => {
|
||||
const colorMap: Record<string, 'error' | 'success' | 'warning' | 'info' | 'default'> = {
|
||||
draft: 'default',
|
||||
sent: 'warning',
|
||||
approved: 'success',
|
||||
received: 'info',
|
||||
cancelled: 'error'
|
||||
}
|
||||
return colorMap[status] || 'info'
|
||||
}
|
||||
|
||||
// Calculations
|
||||
const totalQuantity = (purchaseOrder?.items ?? []).reduce((sum, item) => sum + (item?.quantity ?? 0), 0)
|
||||
const total = (purchaseOrder?.items ?? []).reduce((sum, item) => sum + (item?.amount ?? 0) * item?.quantity, 0)
|
||||
|
||||
return (
|
||||
<Card sx={{ width: '100%' }}>
|
||||
<CardHeader
|
||||
title={
|
||||
<Box display='flex' justifyContent='space-between' alignItems='center'>
|
||||
<Typography variant='h5' color='error' sx={{ fontWeight: 'bold' }}>
|
||||
Belum Dibayar
|
||||
<Typography variant='h5' color={getStatusColor(purchaseOrder?.status ?? '')} sx={{ fontWeight: 'bold' }}>
|
||||
{getStatusLabel(purchaseOrder?.status ?? '')}
|
||||
</Typography>
|
||||
<Box>
|
||||
<Button startIcon={<i className='tabler-share' />} variant='outlined' size='small' sx={{ mr: 1 }}>
|
||||
@ -121,24 +98,15 @@ const PurchaseDetailInformation: React.FC = () => {
|
||||
Vendor
|
||||
</Typography>
|
||||
<Typography variant='body1' color='primary' sx={{ fontWeight: 'medium', cursor: 'pointer' }}>
|
||||
{purchaseData.vendor}
|
||||
{purchaseOrder?.vendor?.name ?? ''}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<Typography variant='subtitle2' color='text.secondary'>
|
||||
Tgl. Transaksi
|
||||
</Typography>
|
||||
<Typography variant='body1'>{purchaseData.tglTransaksi}</Typography>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography variant='subtitle2' color='text.secondary'>
|
||||
Gudang
|
||||
</Typography>
|
||||
<Typography variant='body1' color='primary' sx={{ cursor: 'pointer' }}>
|
||||
{purchaseData.gudang}
|
||||
Tgl. Transaksi
|
||||
</Typography>
|
||||
<Typography variant='body1'>{formatDate(purchaseOrder?.transaction_date ?? '')}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
|
||||
@ -147,14 +115,14 @@ const PurchaseDetailInformation: React.FC = () => {
|
||||
<Typography variant='subtitle2' color='text.secondary'>
|
||||
Nomor
|
||||
</Typography>
|
||||
<Typography variant='body1'>{purchaseData.nomor}</Typography>
|
||||
<Typography variant='body1'>{purchaseOrder?.po_number}</Typography>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Typography variant='subtitle2' color='text.secondary'>
|
||||
Tgl. Jatuh Tempo
|
||||
</Typography>
|
||||
<Typography variant='body1'>{purchaseData.tglJatuhTempo}</Typography>
|
||||
<Typography variant='body1'>{formatDate(purchaseOrder?.due_date ?? '')}</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -168,43 +136,38 @@ const PurchaseDetailInformation: React.FC = () => {
|
||||
<TableCell>Deskripsi</TableCell>
|
||||
<TableCell align='center'>Kuantitas</TableCell>
|
||||
<TableCell align='center'>Satuan</TableCell>
|
||||
<TableCell align='center'>Discount</TableCell>
|
||||
<TableCell align='right'>Harga</TableCell>
|
||||
<TableCell align='center'>Pajak</TableCell>
|
||||
<TableCell align='right'>Jumlah</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{products.map((product, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>
|
||||
<Typography variant='body2' color='primary' sx={{ cursor: 'pointer' }}>
|
||||
{product.produk}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{product.deskripsi}</TableCell>
|
||||
<TableCell align='center'>{product.kuantitas}</TableCell>
|
||||
<TableCell align='center'>{product.satuan}</TableCell>
|
||||
<TableCell align='center'>{product.discount}</TableCell>
|
||||
<TableCell align='right'>{formatCurrency(product.harga)}</TableCell>
|
||||
<TableCell align='center'>{product.pajak}</TableCell>
|
||||
<TableCell align='right'>{formatCurrency(product.jumlah)}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{(purchaseOrder?.items ?? []).map((item, index) => {
|
||||
return (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell>
|
||||
<Typography variant='body2' color='primary' sx={{ cursor: 'pointer' }}>
|
||||
{item.ingredient.name}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>{item.description}</TableCell>
|
||||
<TableCell align='center'>{item.quantity}</TableCell>
|
||||
<TableCell align='center'>{item.unit.name}</TableCell>
|
||||
<TableCell align='right'>{formatCurrency(item.amount)}</TableCell>
|
||||
<TableCell align='right'>{formatCurrency(item.amount * item.quantity)}</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Total Kuantitas Row */}
|
||||
{/* Total Quantity Row */}
|
||||
<TableRow>
|
||||
<TableCell colSpan={2} sx={{ fontWeight: 'bold', borderTop: '2px solid #e0e0e0' }}>
|
||||
Total Kuantitas
|
||||
</TableCell>
|
||||
<TableCell align='center' sx={{ fontWeight: 'bold', borderTop: '2px solid #e0e0e0' }}>
|
||||
{totalKuantitas}
|
||||
{totalQuantity}
|
||||
</TableCell>
|
||||
<TableCell sx={{ borderTop: '2px solid #e0e0e0' }}></TableCell>
|
||||
<TableCell sx={{ borderTop: '2px solid #e0e0e0' }}></TableCell>
|
||||
<TableCell sx={{ borderTop: '2px solid #e0e0e0' }}></TableCell>
|
||||
<TableCell sx={{ borderTop: '2px solid #e0e0e0' }}></TableCell>
|
||||
<TableCell sx={{ borderTop: '2px solid #e0e0e0' }}></TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
@ -222,82 +185,19 @@ const PurchaseDetailInformation: React.FC = () => {
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
||||
transition: 'background-color 0.15s ease'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='body1' sx={{ fontWeight: 'medium' }}>
|
||||
Sub Total
|
||||
</Typography>
|
||||
<Typography variant='body1' sx={{ fontWeight: 'medium' }}>
|
||||
{formatCurrency(subTotal)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
||||
transition: 'background-color 0.15s ease'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='body1' sx={{ fontWeight: 'medium' }}>
|
||||
PPN
|
||||
</Typography>
|
||||
<Typography variant='body1' sx={{ fontWeight: 'medium' }}>
|
||||
{formatCurrency(ppn)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
borderBottom: '1px solid #e0e0e0',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
||||
transition: 'background-color 0.15s ease'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='body1' sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant='h6' sx={{ fontWeight: 'bold' }}>
|
||||
Total
|
||||
</Typography>
|
||||
<Typography variant='body1' sx={{ fontWeight: 'bold' }}>
|
||||
<Typography variant='h6' sx={{ fontWeight: 'bold' }}>
|
||||
{formatCurrency(total)}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
py: 2,
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.04)',
|
||||
transition: 'background-color 0.15s ease'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography variant='h6' sx={{ fontWeight: 'bold' }}>
|
||||
Sisa Tagihan
|
||||
</Typography>
|
||||
<Typography variant='h6' sx={{ fontWeight: 'bold' }}>
|
||||
{formatCurrency(sisaTagihan)}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@ -212,7 +212,8 @@ const PurchaseOrderListTable = () => {
|
||||
variant='text'
|
||||
color='primary'
|
||||
className='p-0 min-w-0 font-medium normal-case justify-start'
|
||||
onClick={() => handlePOClick(row.original.id.toString())}
|
||||
component={Link}
|
||||
href={getLocalizedUrl(`/apps/purchase/purchase-orders/${row.original.id}/detail`, locale as Locale)}
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontWeight: 500,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user