266 lines
13 KiB
TypeScript
266 lines
13 KiB
TypeScript
import { Head, useForm, Link } from '@inertiajs/react';
|
|
import type { Product, ProductPrice } from '@/types';
|
|
import { Category } from '@/types/category';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Field, FieldGroup } from "@/components/ui/field"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Label } from "@/components/ui/label"
|
|
import { toast } from 'sonner';
|
|
import productRoutes from '@/routes/product';
|
|
import React from 'react';
|
|
import { SimpleEditor } from '@/components/tiptap-templates/simple/simple-editor';
|
|
import {
|
|
Combobox,
|
|
ComboboxContent,
|
|
ComboboxEmpty,
|
|
ComboboxItem,
|
|
ComboboxList,
|
|
ComboboxChips,
|
|
ComboboxChip,
|
|
ComboboxChipsInput,
|
|
ComboboxValue,
|
|
useComboboxAnchor,
|
|
} from "@/components/ui/combobox"
|
|
import { NumericFormat } from 'react-number-format';
|
|
|
|
export default function ProductEdit({ product, categories }: { product: Product, categories: Category[] }) {
|
|
const getPrice = (type: string) => {
|
|
const priceObj = product.prices?.find((p: ProductPrice) => p.price_type === type);
|
|
return priceObj ? priceObj.price.toString() : '';
|
|
};
|
|
|
|
const initialCategoryIds = product.categories?.map(c => c.id) || [];
|
|
|
|
const { data, setData, patch, processing, errors } = useForm({
|
|
name: product.name || '',
|
|
description: product.description || '',
|
|
category_ids: initialCategoryIds,
|
|
prices: {
|
|
purchase: getPrice('purchase'),
|
|
distributor: getPrice('distributor'),
|
|
agent: getPrice('agent'),
|
|
reseller: getPrice('reseller'),
|
|
retail: getPrice('retail'),
|
|
}
|
|
});
|
|
|
|
const anchor = useComboboxAnchor()
|
|
|
|
const onSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
patch(productRoutes.update(product.id).url, {
|
|
onSuccess: (response: any) => {
|
|
toast.success(response.props.flash.success);
|
|
},
|
|
});
|
|
};
|
|
|
|
const filteredCategories = categories.filter((c) => !data.category_ids.includes(c.id))
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6 p-6">
|
|
<Head title={`Ubah Produk: ${product.name}`} />
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold tracking-tight">Ubah Produk</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm p-5">
|
|
<CardContent className="p-0">
|
|
<form onSubmit={onSubmit} className="space-y-6 mt-4">
|
|
<FieldGroup>
|
|
<Field>
|
|
<Label htmlFor="name">Nama</Label>
|
|
<Input
|
|
id="name"
|
|
name="name"
|
|
value={data.name}
|
|
onChange={e => setData('name', e.target.value)}
|
|
autoComplete='off'
|
|
placeholder='Contoh: Gamis Wanita'
|
|
maxLength={100}
|
|
/>
|
|
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
|
</Field>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<Field>
|
|
<Label>Kategori</Label>
|
|
<Combobox
|
|
multiple
|
|
autoHighlight
|
|
items={filteredCategories}
|
|
value={categories.filter(c => data.category_ids.includes(c.id))}
|
|
onValueChange={(selected) => {
|
|
setData(
|
|
"category_ids",
|
|
selected.map((item: Category) => item.id)
|
|
)
|
|
}}
|
|
>
|
|
<ComboboxChips ref={anchor} className="w-full">
|
|
<ComboboxValue>
|
|
{(values: Category[]) => (
|
|
<>
|
|
{values.map((value) => (
|
|
<ComboboxChip key={value.id}>
|
|
{value.title}
|
|
</ComboboxChip>
|
|
))}
|
|
<ComboboxChipsInput placeholder='Pilih Kategori' />
|
|
</>
|
|
)}
|
|
</ComboboxValue>
|
|
</ComboboxChips>
|
|
<ComboboxContent anchor={anchor}>
|
|
<ComboboxEmpty>Data tidak ditemukan.</ComboboxEmpty>
|
|
<ComboboxList>
|
|
{(item: Category) => (
|
|
<ComboboxItem key={item.id} value={item}>
|
|
{item.title}
|
|
</ComboboxItem>
|
|
)}
|
|
</ComboboxList>
|
|
</ComboboxContent>
|
|
</Combobox>
|
|
{errors.category_ids && <p className="text-xs text-red-500">{errors.category_ids}</p>}
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="price_purchase">Harga Beli</Label>
|
|
<NumericFormat
|
|
id="price_purchase"
|
|
customInput={Input}
|
|
thousandSeparator="."
|
|
decimalSeparator=","
|
|
prefix="Rp "
|
|
value={data.prices.purchase}
|
|
onValueChange={(values) => {
|
|
setData('prices', {
|
|
...data.prices,
|
|
purchase: values.value
|
|
})
|
|
}}
|
|
placeholder="Rp 0"
|
|
/>
|
|
{errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>}
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="price_distributor">Harga Distributor</Label>
|
|
<NumericFormat
|
|
id="price_distributor"
|
|
customInput={Input}
|
|
thousandSeparator="."
|
|
decimalSeparator=","
|
|
prefix="Rp "
|
|
value={data.prices.distributor}
|
|
onValueChange={(values) => {
|
|
setData('prices', {
|
|
...data.prices,
|
|
distributor: values.value
|
|
})
|
|
}}
|
|
placeholder="Rp 0"
|
|
/>
|
|
{errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>}
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="price_agent">Harga Agen</Label>
|
|
<NumericFormat
|
|
id="price_agent"
|
|
customInput={Input}
|
|
thousandSeparator="."
|
|
decimalSeparator=","
|
|
prefix="Rp "
|
|
value={data.prices.agent}
|
|
onValueChange={(values) => {
|
|
setData('prices', {
|
|
...data.prices,
|
|
agent: values.value
|
|
})
|
|
}}
|
|
placeholder="Rp 0"
|
|
/>
|
|
{errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>}
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="price_reseller">Harga Reseller</Label>
|
|
<NumericFormat
|
|
id="price_reseller"
|
|
customInput={Input}
|
|
thousandSeparator="."
|
|
decimalSeparator=","
|
|
prefix="Rp "
|
|
value={data.prices.reseller}
|
|
onValueChange={(values) => {
|
|
setData('prices', {
|
|
...data.prices,
|
|
reseller: values.value
|
|
})
|
|
}}
|
|
placeholder="Rp 0"
|
|
/>
|
|
{errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>}
|
|
</Field>
|
|
|
|
<Field>
|
|
<Label htmlFor="price_retail">Harga Retail</Label>
|
|
<NumericFormat
|
|
id="price_retail"
|
|
customInput={Input}
|
|
thousandSeparator="."
|
|
decimalSeparator=","
|
|
prefix="Rp "
|
|
value={data.prices.retail}
|
|
onValueChange={(values) => {
|
|
setData('prices', {
|
|
...data.prices,
|
|
retail: values.value
|
|
})
|
|
}}
|
|
placeholder="Rp 0"
|
|
/>
|
|
{errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>}
|
|
</Field>
|
|
</div>
|
|
|
|
<Field>
|
|
<Label htmlFor="description">Deskripsi</Label>
|
|
<SimpleEditor
|
|
value={data.description}
|
|
onChange={(val) => setData('description', val)}
|
|
/>
|
|
{errors.description && <p className="text-xs text-red-500">{errors.description}</p>}
|
|
</Field>
|
|
</FieldGroup>
|
|
|
|
<div className="flex gap-4 p-4 mt-6 rounded-lg justify-end">
|
|
<Link href={productRoutes.index().url}>
|
|
<Button type="button" variant="outline">Kembali</Button>
|
|
</Link>
|
|
<Button type="submit" disabled={processing}>
|
|
{processing ? 'Menyimpan...' : 'Simpan'}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ProductEdit.layout = {
|
|
breadcrumbs: [
|
|
{
|
|
title: 'Master',
|
|
},
|
|
],
|
|
};
|