feat: add confirmation dialogs for deleting variants in ProductForm and RawMaterialForm components to enhance user experience and prevent accidental deletions

This commit is contained in:
Yoga Pangestu 2026-07-08 17:17:45 +07:00
parent 897d9c62b7
commit 4753949338
2 changed files with 61 additions and 23 deletions

View File

@ -3,6 +3,7 @@ import { useForm } from '@inertiajs/vue3';
import { Plus, Save } from '@lucide/vue';
import { computed, ref } from 'vue';
import { toast } from 'vue-sonner';
import ConfirmDialog from '@/components/ConfirmDialog.vue';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { useCan } from '@/composables/useCan';
@ -12,6 +13,7 @@ import type { CategoryOption, ProductFormInitialData, ProductVariantFormItem } f
import ProductInfoSection from './ProductInfoSection.vue';
import ProductVariantSection from './ProductVariantSection.vue';
const { hasRole } = useCan();
const showPrices = !hasRole('admin-bahan-baku');
@ -33,7 +35,7 @@ function createClientId(): string {
return `variant-${crypto.randomUUID()}`;
}
const isUploading = computed(() =>
const isUploading = computed(() =>
variants.value.some((v) => v.media.pendingUploads > 0)
);
@ -122,8 +124,8 @@ function copyPrices(variantPrices: Record<string, string>) {
function pastePrices(clientId: string) {
if (!copiedPrices.value) {
return;
}
return;
}
setVariantField(clientId, 'prices', { ...copiedPrices.value });
}
@ -134,6 +136,23 @@ function applyToAllPrices(variantPrices: Record<string, string>) {
});
}
const showDeleteConfirm = ref(false);
const variantToDelete = ref<string | null>(null);
function confirmRemoveVariant(clientId: string) {
variantToDelete.value = clientId;
showDeleteConfirm.value = true;
}
function handleRemoveVariant() {
if (variantToDelete.value) {
removeVariant(variantToDelete.value);
variantToDelete.value = null;
}
showDeleteConfirm.value = false;
}
const form = useForm({
name: props.initialData?.name ?? '',
description: props.initialData?.description ?? '',
@ -205,27 +224,16 @@ function submit() {
<CardTitle>Informasi Produk</CardTitle>
</CardHeader>
<CardContent>
<ProductInfoSection
:form="form"
:categories="categories"
:category-error="categoryError"
@update:name="form.name = $event"
@update:description="form.description = $event"
@toggle-category="toggleCategory"
/>
<ProductInfoSection :form="form" :categories="categories" :category-error="categoryError"
@update:name="form.name = $event" @update:description="form.description = $event"
@toggle-category="toggleCategory" />
</CardContent>
</Card>
<ProductVariantSection
v-for="(variant, index) in variants"
:key="variant.client_id"
:form="form"
:variant="variant"
:index="index"
:can-remove="variants.length > 1"
:has-copied-prices="!!copiedPrices"
<ProductVariantSection v-for="(variant, index) in variants" :key="variant.client_id" :form="form"
:variant="variant" :index="index" :can-remove="variants.length > 1" :has-copied-prices="!!copiedPrices"
:variant-errors="(clientId, field) => variantErrors(form, clientId, field)"
@remove="removeVariant(variant.client_id)"
@remove="confirmRemoveVariant(variant.client_id)"
@update:name="setVariantField(variant.client_id, 'name', $event)"
@update:stock="setVariantField(variant.client_id, 'stock', $event)"
@update:retail-stock="setVariantField(variant.client_id, 'retail_stock', $event)"
@ -233,8 +241,7 @@ function submit() {
@update:media="setVariantField(variant.client_id, 'media', $event)"
@copy-prices="copyPrices(variant.prices as Record<string, string>)"
@paste-prices="pastePrices(variant.client_id)"
@apply-to-all-prices="applyToAllPrices(variant.prices as Record<string, string>)"
/>
@apply-to-all-prices="applyToAllPrices(variant.prices as Record<string, string>)" />
<div class="flex items-center justify-between gap-2">
<Button type="button" variant="outline" @click="addVariant">
@ -249,4 +256,8 @@ function submit() {
</div>
</div>
</form>
<ConfirmDialog v-model:open="showDeleteConfirm" title="Hapus Varian?"
description="Apakah Anda yakin ingin menghapus varian ini? Tindakan ini tidak dapat dibatalkan."
confirm-label="Hapus" cancel-label="Batal" destructive @confirm="handleRemoveVariant" />
</template>

View File

@ -3,6 +3,7 @@ import { useForm } from '@inertiajs/vue3';
import { Plus, Save } from '@lucide/vue';
import { computed, ref } from 'vue';
import { toast } from 'vue-sonner';
import ConfirmDialog from '@/components/ConfirmDialog.vue';
import { Button } from '@/components/ui/button';
import { FieldError } from '@/components/ui/field';
import { useVariantList } from '@/composables/useVariantList';
@ -89,6 +90,22 @@ const {
const useSamePrice = ref(prices.value.length <= 1 || allPricesHaveSameValue());
const showDeleteConfirm = ref(false);
const priceToDelete = ref<string | null>(null);
function confirmRemovePrice(clientId: string) {
priceToDelete.value = clientId;
showDeleteConfirm.value = true;
}
function handleRemovePrice() {
if (priceToDelete.value) {
removePrice(priceToDelete.value);
priceToDelete.value = null;
}
showDeleteConfirm.value = false;
}
const form = useForm({
name: props.initialData?.name ?? '',
unit: props.initialData?.unit ?? '',
@ -196,7 +213,7 @@ function submit() {
<RawMaterialVariantSection v-for="(price, index) in prices" :key="price.client_id" :form="form"
:price="price" :index="index" :total-prices="prices.length" :use-same-price="useSamePrice"
:price-errors="(clientId, field) => priceErrors(form, clientId, field)"
@remove="removePrice(price.client_id)" @apply-price-to-all="applyPriceToAllVariants(price.client_id)"
@remove="confirmRemovePrice(price.client_id)" @apply-price-to-all="applyPriceToAllVariants(price.client_id)"
@update:variant="setPriceField(price.client_id, 'variant', $event)"
@update:stock="setPriceField(price.client_id, 'stock', $event)"
@update:price="setPriceValue(price.client_id, $event)" />
@ -216,4 +233,14 @@ function submit() {
</div>
</div>
</form>
<ConfirmDialog
v-model:open="showDeleteConfirm"
title="Hapus Varian?"
description="Apakah Anda yakin ingin menghapus varian ini? Tindakan ini tidak dapat dibatalkan."
confirm-label="Hapus"
cancel-label="Batal"
destructive
@confirm="handleRemovePrice"
/>
</template>