128 lines
5.0 KiB
Vue
128 lines
5.0 KiB
Vue
<script setup lang="ts">
|
|
import { router } from '@inertiajs/vue3';
|
|
import { ChevronDown, PackageX, TriangleAlert } from '@lucide/vue';
|
|
import { computed, ref } from 'vue';
|
|
import PosCatalogCard from '@/components/admin/manage/PosCatalogCard.vue';
|
|
import PosCatalogVariantThumb from '@/components/admin/manage/PosCatalogVariantThumb.vue';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import {
|
|
Collapsible,
|
|
CollapsibleContent,
|
|
CollapsibleTrigger,
|
|
} from '@/components/ui/collapsible';
|
|
import {
|
|
Empty,
|
|
EmptyDescription,
|
|
EmptyHeader,
|
|
EmptyTitle,
|
|
} from '@/components/ui/empty';
|
|
import type { MasterOutOfStockGroup } from '@/types/master-inventory';
|
|
|
|
const props = withDefaults(defineProps<{
|
|
title: string;
|
|
description?: string;
|
|
groups: MasterOutOfStockGroup[];
|
|
severity?: 'danger' | 'warning';
|
|
emptyTitle?: string;
|
|
emptyDescription?: string;
|
|
}>(), {
|
|
severity: 'danger',
|
|
emptyTitle: 'Tidak ada stok habis',
|
|
emptyDescription: 'Semua varian masih memiliki stok tersedia.',
|
|
});
|
|
|
|
const open = ref(false);
|
|
|
|
const variantCount = computed(() => props.groups.reduce(
|
|
(total, group) => total + group.variants.length,
|
|
0,
|
|
));
|
|
|
|
const iconClass = computed(() => (
|
|
props.severity === 'warning'
|
|
? 'text-amber-600 dark:text-amber-400'
|
|
: 'text-red-600 dark:text-red-400'
|
|
));
|
|
|
|
const stockTextClass = computed(() => (
|
|
props.severity === 'warning'
|
|
? 'text-amber-600 dark:text-amber-400'
|
|
: 'text-red-600 dark:text-red-400'
|
|
));
|
|
|
|
const badgeVariant = computed(() => (
|
|
props.severity === 'warning' ? 'outline' : 'destructive'
|
|
));
|
|
|
|
function visitEdit(url: string): void {
|
|
router.visit(url);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Collapsible v-model:open="open">
|
|
<Card class="py-0">
|
|
<CollapsibleTrigger
|
|
class="flex w-full items-center justify-between gap-3 px-6 py-4 text-left transition-colors hover:bg-muted/30">
|
|
<div class="min-w-0 space-y-1">
|
|
<div class="flex items-center gap-2">
|
|
<TriangleAlert v-if="severity === 'warning'" class="size-4 shrink-0" :class="iconClass" />
|
|
<PackageX v-else class="size-4 shrink-0" :class="iconClass" />
|
|
<h3 class="font-semibold leading-tight">
|
|
{{ title }}
|
|
</h3>
|
|
<Badge v-if="variantCount > 0" :variant="badgeVariant">
|
|
{{ variantCount }}
|
|
</Badge>
|
|
</div>
|
|
<p v-if="description" class="text-xs text-muted-foreground">
|
|
{{ description }}
|
|
</p>
|
|
</div>
|
|
|
|
<ChevronDown class="size-4 shrink-0 text-muted-foreground transition-transform duration-200"
|
|
:class="open ? 'rotate-180' : ''" />
|
|
</CollapsibleTrigger>
|
|
|
|
<CollapsibleContent>
|
|
<CardContent class="pt-0">
|
|
<div v-if="groups.length" class="columns-1 gap-4 sm:columns-2 xl:columns-3">
|
|
<PosCatalogCard v-for="group in groups" :key="group.id" :title="group.name" :show-cover="false"
|
|
class="cursor-pointer transition-shadow hover:shadow-md" @click="visitEdit(group.edit_url)">
|
|
<template v-if="group.unit_label" #header-extra>
|
|
<Badge variant="secondary" class="mt-1.5">
|
|
{{ group.unit_label }}
|
|
</Badge>
|
|
</template>
|
|
|
|
<div v-for="variant in group.variants" :key="variant.id"
|
|
class="flex items-center gap-2.5 px-3 py-2.5 transition-colors hover:bg-muted/30"
|
|
@click.stop="visitEdit(group.edit_url)">
|
|
<PosCatalogVariantThumb :items="variant.images" />
|
|
<div class="min-w-0 flex-1">
|
|
<p class="truncate text-sm font-medium">
|
|
{{ variant.name }}
|
|
</p>
|
|
<p class="text-xs font-medium tabular-nums" :class="stockTextClass">
|
|
{{ variant.stock_formatted }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</PosCatalogCard>
|
|
</div>
|
|
|
|
<Empty v-else class="py-8">
|
|
<EmptyHeader>
|
|
<EmptyTitle>{{ emptyTitle }}</EmptyTitle>
|
|
<EmptyDescription>
|
|
{{ emptyDescription }}
|
|
</EmptyDescription>
|
|
</EmptyHeader>
|
|
</Empty>
|
|
</CardContent>
|
|
</CollapsibleContent>
|
|
</Card>
|
|
</Collapsible>
|
|
</template>
|