efril #7

Merged
aefril merged 23 commits from efril into main 2025-09-12 21:12:53 +00:00
5 changed files with 128 additions and 184 deletions
Showing only changes of commit 2d3274d3bf - Show all commits

View File

@ -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

View File

@ -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
}
})
}

View File

@ -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>
)}
</>
)
}

View File

@ -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>

View File

@ -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,