feat: implement flexible payment processing with partial cash payments and automatic balance deduction in reports

This commit is contained in:
Firman Ramdhani
2026-09-03 14:26:45 +07:00
parent 83a09a4c03
commit d32e7eda91
9 changed files with 693 additions and 128 deletions
+36 -24
View File
@@ -10,7 +10,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Checkbox } from '@/components/ui/checkbox'
import { format } from 'date-fns'
import { format, isToday } from 'date-fns'
import { id as idLocale } from 'date-fns/locale'
import Link from 'next/link'
import {
@@ -306,10 +306,10 @@ export default function MyOrdersPage() {
return (
<div
key={order.id}
className="flex flex-col md:flex-row h-full md:h-auto rounded-2xl border border-slate-200/90 dark:border-slate-800 bg-white dark:bg-slate-900 shadow-sm hover:shadow-md hover:border-[#1B2CC1]/40 transition-all overflow-hidden"
className="flex flex-col md:flex-row shrink-0 rounded-2xl border border-slate-200/90 dark:border-slate-800 bg-white dark:bg-slate-900 shadow-sm hover:shadow-md hover:border-[#1B2CC1]/40 transition-all overflow-hidden"
>
{/* Left: Info */}
<div className="flex-1 p-5 md:border-r border-slate-100 dark:border-slate-800/80 flex flex-col justify-center">
<div className="w-full md:flex-1 p-5 md:border-r border-slate-100 dark:border-slate-800/80 flex flex-col justify-center">
<div className="flex justify-between items-start gap-4">
<h3 className="text-xl font-bold text-slate-900 dark:text-white line-clamp-2 leading-snug flex-1 min-w-0">
{order.title}
@@ -358,14 +358,14 @@ export default function MyOrdersPage() {
</div>
{/* Right: Actions */}
<div className="w-full md:w-64 p-5 bg-slate-50/70 dark:bg-slate-800/40 flex flex-col justify-center gap-3 md:border-l border-slate-100 dark:border-slate-800/80 mt-auto md:mt-0">
<div className="grid grid-cols-2 gap-2 w-full">
<div className="w-full md:w-[260px] p-4 sm:p-5 bg-slate-50/70 dark:bg-slate-800/40 flex flex-col justify-center gap-3 shrink-0">
<div className="flex flex-row gap-2 w-full">
{/* Detail Link */}
<Link
href={`/my-orders/${order.id}`}
className={cn(
buttonVariants({ variant: "outline", size: "sm" }),
"w-full h-10 rounded-xl border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 font-bold text-xs gap-1.5 hover:bg-blue-50/60 hover:text-[#1B2CC1] hover:border-[#1B2CC1]/40 shadow-2xs transition-all flex items-center justify-center px-2"
"flex-1 h-10 rounded-xl border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 font-bold text-xs gap-1.5 hover:bg-blue-50/60 hover:text-[#1B2CC1] hover:border-[#1B2CC1]/40 shadow-sm transition-all flex items-center justify-center px-2"
)}
>
<FileText className="w-4 h-4 text-[#1B2CC1] shrink-0" />
@@ -373,12 +373,18 @@ export default function MyOrdersPage() {
</Link>
{/* Status Action Button */}
<div className="w-full">
<div className="flex-1">
{order.status === 'DRAFT' && (
<Button
onClick={() => handleStatusChange(order.id, 'OPEN')}
className="w-full h-10 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs shadow-sm gap-1.5 transition-all cursor-pointer px-2"
title="Buka PO"
disabled={!isToday(new Date(order.date))}
className={cn(
"w-full h-10 rounded-xl font-bold text-xs shadow-sm gap-1.5 transition-all px-2",
isToday(new Date(order.date))
? "bg-emerald-600 hover:bg-emerald-700 text-white cursor-pointer"
: "bg-slate-200 text-slate-500 cursor-not-allowed opacity-50"
)}
title={isToday(new Date(order.date)) ? "Buka PO" : "Hanya PO hari ini yang bisa dibuka"}
>
<Sparkles className="w-3.5 h-3.5 shrink-0" />
<span className="truncate">Buka</span>
@@ -397,8 +403,14 @@ export default function MyOrdersPage() {
{order.status === 'CLOSE' && (
<Button
onClick={() => handleStatusChange(order.id, 'OPEN')}
className="w-full h-10 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs shadow-sm gap-1.5 transition-all cursor-pointer px-2"
title="Buka Kembali PO"
disabled={!isToday(new Date(order.date))}
className={cn(
"w-full h-10 rounded-xl font-bold text-xs shadow-sm gap-1.5 transition-all px-2",
isToday(new Date(order.date))
? "bg-emerald-600 hover:bg-emerald-700 text-white cursor-pointer"
: "bg-slate-200 text-slate-500 cursor-not-allowed opacity-50"
)}
title={isToday(new Date(order.date)) ? "Buka Kembali PO" : "Hanya PO hari ini yang bisa dibuka"}
>
<Sparkles className="w-3.5 h-3.5 shrink-0" />
<span className="truncate">Buka</span>
@@ -408,33 +420,33 @@ export default function MyOrdersPage() {
</div>
{/* Utility Tools Row */}
<div className="grid grid-cols-3 gap-1.5 pt-2 border-t border-slate-200/60 dark:border-slate-800/60">
<div className="flex items-center justify-between gap-1 pt-3 border-t border-slate-200/60 dark:border-slate-800/60">
<Button
variant="ghost"
size="sm"
disabled={isClosed}
disabled={isClosed || !isToday(new Date(order.date))}
onClick={() => setEditingOrder(order)}
className={cn(
"h-8 rounded-lg font-bold text-[11px] gap-1 px-1.5 transition-all",
isClosed
"flex-1 h-8 rounded-lg font-bold text-[10px] sm:text-[11px] gap-1 px-1 transition-all",
(isClosed || !isToday(new Date(order.date)))
? "opacity-35 cursor-not-allowed text-slate-400"
: "text-slate-600 hover:text-[#1B2CC1] hover:bg-[#1B2CC1]/10 dark:text-slate-300"
)}
title={isClosed ? "PO sudah CLOSE" : "Edit PO"}
title={isClosed ? "PO sudah CLOSE" : !isToday(new Date(order.date)) ? "Hanya PO hari ini yang bisa diedit" : "Edit PO"}
>
<Pencil className="w-3.5 h-3.5" />
<span>Edit</span>
<Pencil className="w-3.5 h-3.5 shrink-0" />
<span className="truncate">Edit</span>
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setOrderToDuplicate(order)}
className="h-8 rounded-lg font-bold text-[11px] gap-1 px-1.5 text-slate-600 hover:text-slate-900 hover:bg-slate-200/60 dark:text-slate-300 transition-all"
className="flex-1 h-8 rounded-lg font-bold text-[10px] sm:text-[11px] gap-1 px-1 text-slate-600 hover:text-slate-900 hover:bg-slate-200/60 dark:text-slate-300 transition-all"
title="Duplikasi PO ini"
>
<Copy className="w-3.5 h-3.5" />
<span>Duplikat</span>
<Copy className="w-3.5 h-3.5 shrink-0" />
<span className="truncate">Duplikat</span>
</Button>
<Button
@@ -443,15 +455,15 @@ export default function MyOrdersPage() {
disabled={!canDelete}
onClick={() => setOrderToDelete(order)}
className={cn(
"h-8 rounded-lg font-bold text-[11px] gap-1 px-1.5 transition-all",
"flex-1 h-8 rounded-lg font-bold text-[10px] sm:text-[11px] gap-1 px-1 transition-all",
!canDelete
? "opacity-35 cursor-not-allowed text-slate-400"
: "text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-950/40"
)}
title={!canDelete ? "PO OPEN tidak dapat dihapus" : "Hapus PO"}
>
<Trash2 className="w-3.5 h-3.5" />
<span>Hapus</span>
<Trash2 className="w-3.5 h-3.5 shrink-0" />
<span className="truncate">Hapus</span>
</Button>
</div>
</div>