Add mobile-stacked card layout for all admin tables
Implemented responsive card layout for mobile devices across all admin pages: - Desktop (md+): Shows traditional table layout - Mobile (<md): Shows stacked card layout with better readability AdminProducts.tsx: - Mobile cards display title, type, price (with sale badge), status - Action buttons (edit/delete) in header AdminOrders.tsx: - Mobile cards display order ID, email, status badge, total, payment method, date - View detail button in header AdminMembers.tsx: - Mobile cards display name, email, role badge, join date - Action buttons (detail/toggle admin) at bottom with full width AdminConsulting.tsx (upcoming & past tabs): - Mobile cards display date, time, client, category, status, meet link - Action buttons (link/complete/cancel) stacked at bottom AdminEvents.tsx (events & availability tabs): - Mobile cards display title/event type or block type, dates, status, notes - Action buttons (edit/delete) at bottom This approach provides much better UX on mobile compared to horizontal scrolling, especially for complex cells like sale prices with badges and multiple action buttons.
This commit is contained in:
@@ -307,7 +307,8 @@ export default function AdminConsulting() {
|
|||||||
<TabsContent value="upcoming">
|
<TabsContent value="upcoming">
|
||||||
<Card className="border-2 border-border">
|
<Card className="border-2 border-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -405,6 +406,93 @@ export default function AdminConsulting() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{upcomingSlots.map((slot) => (
|
||||||
|
<Card key={slot.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2 flex-wrap mb-1">
|
||||||
|
<h3 className="font-semibold text-sm">
|
||||||
|
{format(parseISO(slot.date), 'd MMM yyyy', { locale: id })}
|
||||||
|
</h3>
|
||||||
|
<Badge variant={statusLabels[slot.status]?.variant || 'secondary'}>
|
||||||
|
{statusLabels[slot.status]?.label || slot.status}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{slot.start_time.substring(0, 5)} - {slot.end_time.substring(0, 5)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Klien:</span>
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="text-sm font-medium">{slot.profiles?.name || '-'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Kategori:</span>
|
||||||
|
<Badge variant="outline" className="text-xs">{slot.topic_category}</Badge>
|
||||||
|
</div>
|
||||||
|
{slot.meet_link && (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Meet:</span>
|
||||||
|
<a
|
||||||
|
href={slot.meet_link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-primary hover:underline text-sm flex items-center gap-1"
|
||||||
|
>
|
||||||
|
<ExternalLink className="w-3 h-3" />
|
||||||
|
Buka
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{slot.status === 'confirmed' && (
|
||||||
|
<div className="flex gap-2 pt-2 border-t border-border">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => openMeetDialog(slot)}
|
||||||
|
className="flex-1 border-2 text-xs"
|
||||||
|
>
|
||||||
|
<LinkIcon className="w-3 h-3 mr-1" />
|
||||||
|
{slot.meet_link ? 'Edit' : 'Link'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => updateSlotStatus(slot.id, 'completed')}
|
||||||
|
className="flex-1 border-2 text-green-600 text-xs"
|
||||||
|
>
|
||||||
|
<CheckCircle className="w-3 h-3 mr-1" />
|
||||||
|
Selesai
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => updateSlotStatus(slot.id, 'cancelled')}
|
||||||
|
className="flex-1 border-2 text-destructive text-xs"
|
||||||
|
>
|
||||||
|
<XCircle className="w-3 h-3 mr-1" />
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{upcomingSlots.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Tidak ada jadwal mendatang
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@@ -412,7 +500,8 @@ export default function AdminConsulting() {
|
|||||||
<TabsContent value="past">
|
<TabsContent value="past">
|
||||||
<Card className="border-2 border-border">
|
<Card className="border-2 border-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -447,6 +536,44 @@ export default function AdminConsulting() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{pastSlots.slice(0, 20).map((slot) => (
|
||||||
|
<Card key={slot.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-2">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="font-semibold text-sm">
|
||||||
|
{format(parseISO(slot.date), 'd MMM yyyy', { locale: id })}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{slot.start_time.substring(0, 5)} - {slot.end_time.substring(0, 5)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Badge variant={statusLabels[slot.status]?.variant || 'secondary'}>
|
||||||
|
{statusLabels[slot.status]?.label || slot.status}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Klien:</span>
|
||||||
|
<span className="text-sm">{slot.profiles?.name || '-'}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Kategori:</span>
|
||||||
|
<Badge variant="outline" className="text-xs">{slot.topic_category}</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{pastSlots.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Belum ada riwayat konsultasi
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -235,7 +235,8 @@ export default function AdminEvents() {
|
|||||||
</Button>
|
</Button>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -277,6 +278,46 @@ export default function AdminEvents() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{events.map((event) => (
|
||||||
|
<Card key={event.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="font-semibold text-base line-clamp-1">{event.title}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground capitalize">{event.type}</p>
|
||||||
|
</div>
|
||||||
|
<Badge className={event.status === 'confirmed' ? 'bg-accent' : 'bg-muted shrink-0'}>
|
||||||
|
{event.status}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Mulai:</span>
|
||||||
|
<span className="text-sm">{formatDateTime(event.starts_at)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 pt-2 border-t border-border">
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => handleEditEvent(event)} className="flex-1">
|
||||||
|
<Pencil className="w-4 h-4 mr-1" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => handleDeleteEvent(event.id)} className="flex-1 text-destructive">
|
||||||
|
<Trash2 className="w-4 h-4 mr-1" />
|
||||||
|
Hapus
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{events.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Belum ada event
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@@ -291,7 +332,8 @@ export default function AdminEvents() {
|
|||||||
</Button>
|
</Button>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -333,6 +375,57 @@ export default function AdminEvents() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{blocks.map((block) => (
|
||||||
|
<Card key={block.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="font-semibold text-base">
|
||||||
|
{block.kind === 'available' ? 'Tersedia' : 'Tidak Tersedia'}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<Badge className={block.kind === 'available' ? 'bg-accent' : 'bg-destructive shrink-0'}>
|
||||||
|
{block.kind === 'available' ? 'Tersedia' : 'Tidak'}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Mulai:</span>
|
||||||
|
<span className="text-sm">{formatDateTime(block.starts_at)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Selesai:</span>
|
||||||
|
<span className="text-sm">{formatDateTime(block.ends_at)}</span>
|
||||||
|
</div>
|
||||||
|
{block.note && (
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Catatan:</span>
|
||||||
|
<span className="text-sm text-right flex-1 ml-4">{block.note}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 pt-2 border-t border-border">
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => handleEditBlock(block)} className="flex-1">
|
||||||
|
<Pencil className="w-4 h-4 mr-1" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => handleDeleteBlock(block.id)} className="flex-1 text-destructive">
|
||||||
|
<Trash2 className="w-4 h-4 mr-1" />
|
||||||
|
Hapus
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{blocks.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Belum ada blok ketersediaan
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ export default function AdminMembers() {
|
|||||||
|
|
||||||
<Card className="border-2 border-border">
|
<Card className="border-2 border-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -153,6 +154,54 @@ export default function AdminMembers() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{members.map((member) => (
|
||||||
|
<Card key={member.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="font-semibold text-base line-clamp-1">{member.name || "Tanpa Nama"}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground truncate">{member.email || "-"}</p>
|
||||||
|
</div>
|
||||||
|
{adminIds.has(member.id) ? (
|
||||||
|
<Badge className="bg-primary shrink-0">Admin</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge className="bg-secondary text-primary shrink-0">Member</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Bergabung:</span>
|
||||||
|
<span className="text-sm">{formatDateTime(member.created_at)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 pt-2 border-t border-border">
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => viewMemberDetails(member)} className="flex-1">
|
||||||
|
<Eye className="w-4 h-4 mr-1" />
|
||||||
|
Detail
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => toggleAdminRole(member.id, adminIds.has(member.id))}
|
||||||
|
disabled={member.id === user?.id}
|
||||||
|
className="flex-1"
|
||||||
|
>
|
||||||
|
{adminIds.has(member.id) ? <ShieldOff className="w-4 h-4 mr-1" /> : <Shield className="w-4 h-4 mr-1" />}
|
||||||
|
{adminIds.has(member.id) ? "Hapus Admin" : "Jadikan Admin"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{members.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Belum ada member
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,8 @@ export default function AdminOrders() {
|
|||||||
|
|
||||||
<Card className="border-2 border-border">
|
<Card className="border-2 border-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -217,6 +218,47 @@ export default function AdminOrders() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{orders.map((order) => (
|
||||||
|
<Card key={order.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
|
<h3 className="font-semibold text-sm font-mono">{order.id.slice(0, 8)}</h3>
|
||||||
|
{getStatusBadge(order.payment_status)}
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground truncate">{order.profile?.email || "-"}</p>
|
||||||
|
</div>
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => viewOrderDetails(order)} className="shrink-0">
|
||||||
|
<Eye className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Total:</span>
|
||||||
|
<span className="font-bold">{formatIDR(order.total_amount)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Metode:</span>
|
||||||
|
<span className="uppercase text-sm">{order.payment_method || "-"}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Tanggal:</span>
|
||||||
|
<span className="text-sm">{formatDateTime(order.created_at)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{orders.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Belum ada order
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ export default function AdminProducts() {
|
|||||||
|
|
||||||
<Card className="border-2 border-border">
|
<Card className="border-2 border-border">
|
||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="overflow-x-auto">
|
{/* Desktop Table */}
|
||||||
|
<div className="hidden md:block overflow-x-auto">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -215,6 +216,56 @@ export default function AdminProducts() {
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Card Layout */}
|
||||||
|
<div className="md:hidden space-y-3 p-4">
|
||||||
|
{products.map((product) => (
|
||||||
|
<Card key={product.id} className="border-2 border-border">
|
||||||
|
<CardContent className="p-4 space-y-3">
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="font-semibold text-base line-clamp-1">{product.title}</h3>
|
||||||
|
<p className="text-sm text-muted-foreground capitalize">{product.type}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-1 shrink-0">
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => handleEdit(product)}>
|
||||||
|
<Pencil className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="ghost" size="sm" onClick={() => handleDelete(product.id)}>
|
||||||
|
<Trash2 className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Harga:</span>
|
||||||
|
<div className="text-right">
|
||||||
|
{product.sale_price ? (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="font-bold">{formatIDR(product.sale_price)}</span>
|
||||||
|
<span className="text-sm text-muted-foreground line-through">{formatIDR(product.price)}</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="font-bold">{formatIDR(product.price)}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-muted-foreground">Status:</span>
|
||||||
|
<span className={product.is_active ? 'text-foreground' : 'text-muted-foreground'}>
|
||||||
|
{product.is_active ? 'Aktif' : 'Nonaktif'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
{products.length === 0 && (
|
||||||
|
<div className="text-center py-8 text-muted-foreground">
|
||||||
|
Belum ada produk
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user