feat: add description field to orders and support marking menu items as sold out
This commit is contained in:
@@ -312,9 +312,16 @@ export default function MyOrdersPage() {
|
||||
{/* Left: Info */}
|
||||
<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}
|
||||
</h3>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-xl font-bold text-slate-900 dark:text-white line-clamp-2 leading-snug">
|
||||
{order.title}
|
||||
</h3>
|
||||
{order.description && (
|
||||
<p className="text-xs text-slate-500 mt-1 line-clamp-2 font-medium">
|
||||
{order.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row items-end sm:items-center gap-2 shrink-0">
|
||||
<span className="text-[11px] text-slate-500 font-semibold bg-slate-100/80 dark:bg-slate-800 px-2.5 py-1 rounded-full whitespace-nowrap">
|
||||
{format(date, 'EEEE, dd MMM yyyy', { locale: idLocale })}
|
||||
@@ -538,6 +545,7 @@ export default function MyOrdersPage() {
|
||||
|
||||
function CreateOrderModal({ userId, onSuccess, initialData }: { userId: string, onSuccess: () => void, initialData?: any }) {
|
||||
const [title, setTitle] = useState(initialData ? `${initialData.title} (Copy)` : '')
|
||||
const [description, setDescription] = useState(initialData ? (initialData.description || '') : '')
|
||||
const [allowCustom, setAllowCustom] = useState(initialData ? initialData.allow_custom : false)
|
||||
const [items, setItems] = useState<{ id: string; name: string }[]>(
|
||||
initialData && initialData.available_items?.length > 0
|
||||
@@ -567,6 +575,7 @@ function CreateOrderModal({ userId, onSuccess, initialData }: { userId: string,
|
||||
const res = await createOrder({
|
||||
creator_id: userId,
|
||||
title: title.trim(),
|
||||
description: description.trim(),
|
||||
date: new Date(),
|
||||
allow_custom: allowCustom,
|
||||
available_items: validItems
|
||||
@@ -609,6 +618,19 @@ function CreateOrderModal({ userId, onSuccess, initialData }: { userId: string,
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<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>
|
||||
</Label>
|
||||
<Input
|
||||
id="order-desc"
|
||||
placeholder="Keterangan tambahan tentang PO ini"
|
||||
value={description}
|
||||
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]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Custom Item Checkbox */}
|
||||
<div className="flex items-start space-x-3 p-4 rounded-xl border border-slate-200/80 dark:border-slate-800 bg-slate-50/70 dark:bg-slate-800/40">
|
||||
<Checkbox
|
||||
@@ -689,18 +711,20 @@ function CreateOrderModal({ userId, onSuccess, initialData }: { userId: string,
|
||||
|
||||
function EditOrderModal({ order, onClose, onSuccess }: { order: any, onClose: () => void, onSuccess: () => void }) {
|
||||
const [title, setTitle] = useState(order.title || '')
|
||||
const [description, setDescription] = useState(order.description || '')
|
||||
const [allowCustom, setAllowCustom] = useState(order.allow_custom || false)
|
||||
const [items, setItems] = useState<Array<{ id: string, name: string }>>(
|
||||
const [items, setItems] = useState<Array<{ id: string, name: string, is_sold_out: boolean }>>(
|
||||
order.available_items?.length > 0
|
||||
? order.available_items.map((ai: any) => ({ id: ai.id, name: ai.name }))
|
||||
: [{ id: '1', name: '' }]
|
||||
? order.available_items.map((ai: any) => ({ id: ai.id, name: ai.name, is_sold_out: ai.is_sold_out || false }))
|
||||
: [{ id: '1', name: '', is_sold_out: false }]
|
||||
)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
|
||||
const addItem = () => setItems([...items, { id: Math.random().toString(), name: '' }])
|
||||
const addItem = () => setItems([...items, { id: Math.random().toString(), name: '', is_sold_out: false }])
|
||||
const removeItem = (id: string) => setItems(items.filter(i => i.id !== id))
|
||||
const updateItem = (id: string, name: string) => setItems(items.map(i => i.id === id ? { ...i, name } : i))
|
||||
const toggleSoldOut = (id: string, is_sold_out: boolean) => setItems(items.map(i => i.id === id ? { ...i, is_sold_out } : i))
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
@@ -708,7 +732,7 @@ function EditOrderModal({ order, onClose, onSuccess }: { order: any, onClose: ()
|
||||
|
||||
if (!title.trim()) return setError('Judul PO wajib diisi.')
|
||||
|
||||
const validItems = items.filter(i => i.name.trim()).map(i => i.name.trim())
|
||||
const validItems = items.filter(i => i.name.trim()).map(i => ({ name: i.name.trim(), is_sold_out: i.is_sold_out }))
|
||||
if (!allowCustom && validItems.length === 0) {
|
||||
return setError('Wajib menambahkan minimal 1 menu pilihan jika tidak mengizinkan custom item.')
|
||||
}
|
||||
@@ -717,6 +741,7 @@ function EditOrderModal({ order, onClose, onSuccess }: { order: any, onClose: ()
|
||||
|
||||
const res = await updateOrder(order.id, {
|
||||
title: title.trim(),
|
||||
description: description.trim(),
|
||||
allow_custom: allowCustom,
|
||||
available_items: validItems
|
||||
})
|
||||
@@ -758,6 +783,19 @@ function EditOrderModal({ order, onClose, onSuccess }: { order: any, onClose: ()
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<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>
|
||||
</Label>
|
||||
<Input
|
||||
id="edit-order-desc"
|
||||
placeholder="Keterangan tambahan tentang PO ini"
|
||||
value={description}
|
||||
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]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Custom Item Checkbox */}
|
||||
<div className="flex items-start space-x-3 p-4 rounded-xl border border-slate-200/80 dark:border-slate-800 bg-slate-50/70 dark:bg-slate-800/40">
|
||||
<Checkbox
|
||||
@@ -800,16 +838,34 @@ function EditOrderModal({ order, onClose, onSuccess }: { order: any, onClose: ()
|
||||
<Input
|
||||
placeholder="Nama menu (mis: Es Kopi Susu Tetangga)"
|
||||
value={item.name}
|
||||
disabled={item.is_sold_out}
|
||||
onChange={(e) => updateItem(item.id, e.target.value)}
|
||||
className="flex-1 h-9 rounded-lg bg-white dark:bg-slate-900 text-xs font-medium"
|
||||
className={cn(
|
||||
"flex-1 h-9 rounded-lg bg-white dark:bg-slate-900 text-xs font-medium transition-all",
|
||||
item.is_sold_out && "opacity-60 cursor-not-allowed line-through decoration-rose-500/50"
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant={item.is_sold_out ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => toggleSoldOut(item.id, !item.is_sold_out)}
|
||||
className={cn(
|
||||
"h-9 px-3 rounded-lg text-xs font-bold transition-all whitespace-nowrap",
|
||||
item.is_sold_out
|
||||
? "bg-rose-600 hover:bg-rose-700 text-white border-rose-600"
|
||||
: "text-slate-500 hover:text-rose-600 hover:border-rose-300"
|
||||
)}
|
||||
>
|
||||
Sold Out
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => removeItem(item.id)}
|
||||
disabled={items.length === 1 && !allowCustom}
|
||||
className="h-8 w-8 text-red-500 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-950/30 rounded-lg"
|
||||
className="h-8 w-8 text-red-500 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-950/30 rounded-lg shrink-0"
|
||||
>
|
||||
<MinusCircle className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user