Compare commits
2
Commits
1.0.2
...
44466f1566
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44466f1566 | ||
|
|
be343f375a |
@@ -6,7 +6,8 @@ import { getOrderDetail, updateSubmissionPayment, updateOrderStatus, updateOrder
|
|||||||
import { Card, CardContent } from '@/components/ui/card'
|
import { Card, CardContent } from '@/components/ui/card'
|
||||||
import { SummaryGenerator } from '@/components/SummaryGenerator'
|
import { SummaryGenerator } from '@/components/SummaryGenerator'
|
||||||
import { Button, buttonVariants } from '@/components/ui/button'
|
import { Button, buttonVariants } from '@/components/ui/button'
|
||||||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||||
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
@@ -435,12 +436,14 @@ function SubmissionRow({
|
|||||||
const [paidAmount, setPaidAmount] = useState(sub.paid_amount ?? '')
|
const [paidAmount, setPaidAmount] = useState(sub.paid_amount ?? '')
|
||||||
const [status, setStatus] = useState(sub.payment_status || 'BELUM_BAYAR')
|
const [status, setStatus] = useState(sub.payment_status || 'BELUM_BAYAR')
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
|
const [isEditingLunas, setIsEditingLunas] = useState(false)
|
||||||
|
|
||||||
// Sync state when props change after save
|
// Sync state when props change after save
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBill(sub.bill ?? '')
|
setBill(sub.bill ?? '')
|
||||||
setPaidAmount(sub.paid_amount ?? '')
|
setPaidAmount(sub.paid_amount ?? '')
|
||||||
setStatus(sub.payment_status || 'BELUM_BAYAR')
|
setStatus(sub.payment_status || 'BELUM_BAYAR')
|
||||||
|
setIsEditingLunas(false) // reset edit mode after save/refresh
|
||||||
}, [sub])
|
}, [sub])
|
||||||
|
|
||||||
const handleSave = async (overrideStatus?: string, overridePaid?: string | number) => {
|
const handleSave = async (overrideStatus?: string, overridePaid?: string | number) => {
|
||||||
@@ -550,7 +553,7 @@ function SubmissionRow({
|
|||||||
type="number"
|
type="number"
|
||||||
placeholder="Rp"
|
placeholder="Rp"
|
||||||
value={bill}
|
value={bill}
|
||||||
disabled={sub.payment_status === 'LUNAS'}
|
disabled={sub.payment_status === 'LUNAS' && !isEditingLunas}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
const val = e.target.value
|
const val = e.target.value
|
||||||
setBill(val)
|
setBill(val)
|
||||||
@@ -573,7 +576,7 @@ function SubmissionRow({
|
|||||||
type="number"
|
type="number"
|
||||||
placeholder="Rp"
|
placeholder="Rp"
|
||||||
value={paidAmount}
|
value={paidAmount}
|
||||||
disabled={sub.payment_status === 'LUNAS'}
|
disabled={sub.payment_status === 'LUNAS' && !isEditingLunas}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
const val = e.target.value
|
const val = e.target.value
|
||||||
setPaidAmount(val)
|
setPaidAmount(val)
|
||||||
@@ -592,7 +595,7 @@ function SubmissionRow({
|
|||||||
<label className="text-[10px] font-bold uppercase tracking-wider text-slate-400">
|
<label className="text-[10px] font-bold uppercase tracking-wider text-slate-400">
|
||||||
Status
|
Status
|
||||||
</label>
|
</label>
|
||||||
<Select value={status} onValueChange={setStatus} disabled={sub.payment_status === 'LUNAS'}>
|
<Select value={status} onValueChange={setStatus} disabled={sub.payment_status === 'LUNAS' && !isEditingLunas}>
|
||||||
<SelectTrigger className="h-8 rounded-md text-xs font-semibold px-2">
|
<SelectTrigger className="h-8 rounded-md text-xs font-semibold px-2">
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@@ -605,7 +608,22 @@ function SubmissionRow({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 w-full mt-1">
|
<div className="flex gap-2 w-full mt-1">
|
||||||
{sub.payment_status !== 'LUNAS' ? (
|
{sub.payment_status === 'LUNAS' && !isEditingLunas ? (
|
||||||
|
// Locked state — show paid badge + edit icon
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
|
<div className="flex-1 bg-emerald-50 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800 rounded-md h-8 flex items-center justify-center text-xs font-bold gap-1.5">
|
||||||
|
<Check className="w-4 h-4" /> Telah Lunas
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsEditingLunas(true)}
|
||||||
|
title="Edit tagihan"
|
||||||
|
className="ml-2 h-8 w-8 shrink-0 flex items-center justify-center rounded-md border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-500 hover:text-[#1B2CC1] hover:border-[#1B2CC1] transition-colors"
|
||||||
|
>
|
||||||
|
<Pencil className="w-3.5 h-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// Editable state — show save + cancel
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -615,6 +633,15 @@ function SubmissionRow({
|
|||||||
>
|
>
|
||||||
{saving ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <><Save className="w-3.5 h-3.5" /> Simpan</>}
|
{saving ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <><Save className="w-3.5 h-3.5" /> Simpan</>}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{sub.payment_status === 'LUNAS' && isEditingLunas && (
|
||||||
|
<button
|
||||||
|
onClick={() => setIsEditingLunas(false)}
|
||||||
|
className="h-8 px-3 shrink-0 flex items-center justify-center rounded-md border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-500 hover:text-rose-600 hover:border-rose-300 text-xs font-semibold transition-colors"
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{currentBalance !== undefined && currentBalance > 0 && status !== 'LUNAS' && (
|
{currentBalance !== undefined && currentBalance > 0 && status !== 'LUNAS' && (
|
||||||
<Button
|
<Button
|
||||||
@@ -629,10 +656,6 @@ function SubmissionRow({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<div className="w-full bg-emerald-50 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400 border border-emerald-200 dark:border-emerald-800 rounded-md h-8 flex items-center justify-center text-xs font-bold gap-1.5 cursor-not-allowed opacity-80">
|
|
||||||
<Check className="w-4 h-4" /> Telah Lunas
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -725,12 +748,12 @@ function EditDetailOrderModal({ order, onClose, onSuccess }: { order: any, onClo
|
|||||||
<Label htmlFor="detail-edit-desc" className="text-xs font-bold uppercase tracking-wider text-slate-700 dark:text-slate-300">
|
<Label htmlFor="detail-edit-desc" className="text-xs font-bold uppercase tracking-wider text-slate-700 dark:text-slate-300">
|
||||||
Deskripsi <span className="text-slate-400 font-normal lowercase">(opsional)</span>
|
Deskripsi <span className="text-slate-400 font-normal lowercase">(opsional)</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Textarea
|
||||||
id="detail-edit-desc"
|
id="detail-edit-desc"
|
||||||
placeholder="Keterangan tambahan tentang PO ini"
|
placeholder="Keterangan tambahan tentang PO ini"
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
className="h-11 rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]"
|
className="min-h-[100px] rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
|
|||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { ShareButton } from '@/components/ShareButton'
|
import { ShareButton } from '@/components/ShareButton'
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||||
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Checkbox } from '@/components/ui/checkbox'
|
import { Checkbox } from '@/components/ui/checkbox'
|
||||||
@@ -622,12 +623,12 @@ function CreateOrderModal({ userId, onSuccess, initialData }: { userId: string,
|
|||||||
<Label htmlFor="order-desc" className="text-xs font-bold uppercase tracking-wider text-slate-700 dark:text-slate-300">
|
<Label htmlFor="order-desc" className="text-xs font-bold uppercase tracking-wider text-slate-700 dark:text-slate-300">
|
||||||
Deskripsi <span className="text-slate-400 font-normal lowercase">(opsional)</span>
|
Deskripsi <span className="text-slate-400 font-normal lowercase">(opsional)</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Textarea
|
||||||
id="order-desc"
|
id="order-desc"
|
||||||
placeholder="Keterangan tambahan tentang PO ini"
|
placeholder="Keterangan tambahan tentang PO ini"
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
className="h-11 rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]"
|
className="min-h-[100px] rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -787,12 +788,12 @@ function EditOrderModal({ order, onClose, onSuccess }: { order: any, onClose: ()
|
|||||||
<Label htmlFor="edit-order-desc" className="text-xs font-bold uppercase tracking-wider text-slate-700 dark:text-slate-300">
|
<Label htmlFor="edit-order-desc" className="text-xs font-bold uppercase tracking-wider text-slate-700 dark:text-slate-300">
|
||||||
Deskripsi <span className="text-slate-400 font-normal lowercase">(opsional)</span>
|
Deskripsi <span className="text-slate-400 font-normal lowercase">(opsional)</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Textarea
|
||||||
id="edit-order-desc"
|
id="edit-order-desc"
|
||||||
placeholder="Keterangan tambahan tentang PO ini"
|
placeholder="Keterangan tambahan tentang PO ini"
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
className="h-11 rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]"
|
className="min-h-[100px] rounded-xl border-slate-200 dark:border-slate-800 focus-visible:ring-4 focus-visible:ring-[#1B2CC1]/15 focus-visible:border-[#1B2CC1]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -98,8 +98,17 @@ export default async function PublicOrderDetailPage({ params }: { params: Promis
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 items-start">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 items-start">
|
||||||
{/* Left 2 Cols: Daftar Penitip */}
|
{/* Left 2 Cols: Daftar Penitip & Description */}
|
||||||
<div className="lg:col-span-2 space-y-6">
|
<div className="lg:col-span-2 space-y-6">
|
||||||
|
{order.description && (
|
||||||
|
<div className="bg-white dark:bg-slate-900 rounded-3xl border border-slate-200/60 dark:border-slate-800 shadow-sm overflow-hidden p-5 sm:p-6">
|
||||||
|
<h3 className="font-bold text-slate-900 dark:text-white mb-3">Deskripsi</h3>
|
||||||
|
<div className="text-slate-600 dark:text-slate-300 text-sm leading-relaxed whitespace-pre-wrap">
|
||||||
|
{order.description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="bg-white dark:bg-slate-900 rounded-3xl border border-slate-200/60 dark:border-slate-800 shadow-sm overflow-hidden">
|
<div className="bg-white dark:bg-slate-900 rounded-3xl border border-slate-200/60 dark:border-slate-800 shadow-sm overflow-hidden">
|
||||||
<div className="p-5 sm:p-6 border-b border-slate-100 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-800/30 flex items-center justify-between">
|
<div className="p-5 sm:p-6 border-b border-slate-100 dark:border-slate-800 bg-slate-50/50 dark:bg-slate-800/30 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export default function ReportByPersonGrid({ data, balancesData = [], creatorId,
|
|||||||
<th className="px-6 py-4 font-bold">Nama Pemesan</th>
|
<th className="px-6 py-4 font-bold">Nama Pemesan</th>
|
||||||
<th className="px-6 py-4 font-bold text-center">Total PO</th>
|
<th className="px-6 py-4 font-bold text-center">Total PO</th>
|
||||||
<th className="px-6 py-4 font-bold text-right">Total Piutang</th>
|
<th className="px-6 py-4 font-bold text-right">Total Piutang</th>
|
||||||
<th className="px-6 py-4 font-bold text-center">Aksi</th>
|
{/* <th className="px-6 py-4 font-bold text-center">Aksi</th> */}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-100 dark:divide-slate-800">
|
<tbody className="divide-y divide-slate-100 dark:divide-slate-800">
|
||||||
@@ -191,7 +191,7 @@ export default function ReportByPersonGrid({ data, balancesData = [], creatorId,
|
|||||||
<p className="text-[10px] text-slate-500 font-semibold mt-0.5">Lunas: {formatRupiah(userObj.totalPaid)}</p>
|
<p className="text-[10px] text-slate-500 font-semibold mt-0.5">Lunas: {formatRupiah(userObj.totalPaid)}</p>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-center">
|
{/* <td className="px-6 py-4 text-center">
|
||||||
{unpaid.length > 0 ? (
|
{unpaid.length > 0 ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={(e) => openModal(e, unpaid.map((s: any) => s.id), `Semua tagihan ${userObj.user.name}`, userObj.totalPiutang, userObj.user.id)}
|
onClick={(e) => openModal(e, unpaid.map((s: any) => s.id), `Semua tagihan ${userObj.user.name}`, userObj.totalPiutang, userObj.user.id)}
|
||||||
@@ -201,7 +201,7 @@ export default function ReportByPersonGrid({ data, balancesData = [], creatorId,
|
|||||||
<CheckCircle2 className="w-3.5 h-3.5" /> Lunasi Semua
|
<CheckCircle2 className="w-3.5 h-3.5" /> Lunasi Semua
|
||||||
</Button>
|
</Button>
|
||||||
):( <span className="text-[10px] font-bold text-slate-400">Selesai</span>)}
|
):( <span className="text-[10px] font-bold text-slate-400">Selesai</span>)}
|
||||||
</td>
|
</td> */}
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{isExpanded && (
|
{isExpanded && (
|
||||||
@@ -219,7 +219,7 @@ export default function ReportByPersonGrid({ data, balancesData = [], creatorId,
|
|||||||
<th className="px-4 py-2.5 text-left">Tgl Order</th>
|
<th className="px-4 py-2.5 text-left">Tgl Order</th>
|
||||||
<th className="px-4 py-2.5 text-left">Status</th>
|
<th className="px-4 py-2.5 text-left">Status</th>
|
||||||
<th className="px-4 py-2.5 text-right">Tagihan</th>
|
<th className="px-4 py-2.5 text-right">Tagihan</th>
|
||||||
<th className="px-4 py-2.5 text-center">Aksi</th>
|
{/* <th className="px-4 py-2.5 text-center">Aksi</th> */}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-100 dark:divide-slate-700/50">
|
<tbody className="divide-y divide-slate-100 dark:divide-slate-700/50">
|
||||||
@@ -244,7 +244,7 @@ export default function ReportByPersonGrid({ data, balancesData = [], creatorId,
|
|||||||
<td className={cn('px-4 py-2.5 text-right font-black', isUnpaid ? 'text-rose-600 dark:text-rose-400' : 'text-emerald-600 dark:text-emerald-400')}>
|
<td className={cn('px-4 py-2.5 text-right font-black', isUnpaid ? 'text-rose-600 dark:text-rose-400' : 'text-emerald-600 dark:text-emerald-400')}>
|
||||||
{sub.bill ? formatRupiah(sub.bill) : 'Rp0'}
|
{sub.bill ? formatRupiah(sub.bill) : 'Rp0'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2.5 text-center">
|
{/* <td className="px-4 py-2.5 text-center">
|
||||||
{isUnpaid ? (
|
{isUnpaid ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={(e) => openModal(e, [sub.id], `PO: ${sub.orderTitle}`, Number(sub.bill) || 0, sub.user.id)}
|
onClick={(e) => openModal(e, [sub.id], `PO: ${sub.orderTitle}`, Number(sub.bill) || 0, sub.user.id)}
|
||||||
@@ -257,7 +257,7 @@ export default function ReportByPersonGrid({ data, balancesData = [], creatorId,
|
|||||||
) : (
|
) : (
|
||||||
<span className="text-[10px] font-bold text-slate-400">Selesai</span>
|
<span className="text-[10px] font-bold text-slate-400">Selesai</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td> */}
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user