diff --git a/app/Models/PushSubscription.php b/app/Models/PushSubscription.php index 566f101..ac0ab8b 100644 --- a/app/Models/PushSubscription.php +++ b/app/Models/PushSubscription.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Attributes\Guarded; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; #[Guarded(['id'])] diff --git a/app/Models/User.php b/app/Models/User.php index f3217f9..694f394 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; - use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -38,21 +37,21 @@ protected function casts(): array protected function email(): Attribute { return Attribute::make( - set: fn(string $value) => strtolower($value), + set: fn (string $value) => strtolower($value), ); } protected function name(): Attribute { return Attribute::make( - get: fn() => $this->userProfile?->full_name ?? '', + get: fn () => $this->userProfile?->full_name ?? '', ); } protected function fullName(): Attribute { return Attribute::make( - get: fn() => $this->userProfile?->full_name ?: $this->username, + get: fn () => $this->userProfile?->full_name ?: $this->username, ); } diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index e5fb634..bc5be8a 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -1,3 +1,29 @@ +import { Link, router } from '@inertiajs/react'; +import type { LucideIcon } from 'lucide-react'; +import { + Activity, + ArrowUpFromLine, + BarChart3, + Boxes, + CalendarCheck, + CalendarDays, + ClipboardCheck, + DollarSign, + HandCoins, + LayoutGrid, + Package, + RefreshCw, + Scissors, + Settings, + Shield, + ShoppingCart, + Tags, + Truck, + UserCircle, + Users, + Wallet, +} from 'lucide-react'; +import React from 'react'; import AppLogo from '@/components/app-logo'; import { Sidebar, @@ -30,32 +56,6 @@ import { index as productsIndex } from '@/routes/admin/master/products'; import { index as rawMaterialsIndex } from '@/routes/admin/master/raw-materials'; import { index as suppliersIndex } from '@/routes/admin/master/suppliers'; import { index as rolesIndex } from '@/routes/admin/settings/roles'; -import { Link, router } from '@inertiajs/react'; -import type { LucideIcon } from 'lucide-react'; -import { - Activity, - ArrowUpFromLine, - BarChart3, - Boxes, - CalendarCheck, - CalendarDays, - ClipboardCheck, - DollarSign, - HandCoins, - LayoutGrid, - Package, - RefreshCw, - Scissors, - Settings, - Shield, - ShoppingCart, - Tags, - Truck, - UserCircle, - Users, - Wallet, -} from 'lucide-react'; -import React from 'react'; type NavMenuItem = { title: string; href: string; icon: LucideIcon; permission?: string | string[] }; @@ -111,12 +111,20 @@ function MenuGroup({ label, items }: { label: string; items: NavMenuItem[] }) { const { can, canAny } = useCan(); const filtered = items.filter((item) => { - if (!item.permission) return true; - if (Array.isArray(item.permission)) return canAny(...item.permission); + if (!item.permission) { +return true; +} + + if (Array.isArray(item.permission)) { +return canAny(...item.permission); +} + return can(item.permission); }); - if (filtered.length === 0) return null; + if (filtered.length === 0) { +return null; +} return ( diff --git a/resources/js/components/card/attendance-card.tsx b/resources/js/components/card/attendance-card.tsx index 6ea4ff5..5bcff72 100644 --- a/resources/js/components/card/attendance-card.tsx +++ b/resources/js/components/card/attendance-card.tsx @@ -1,6 +1,6 @@ import { Clock, LogIn, LogOut } from 'lucide-react'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; type TodayAttendance = { id: number; @@ -29,8 +29,12 @@ export function AttendanceCard({ todayAttendance, isOnLeave, canCheckIn, onCheck const hasCheckedOut = !!todayAttendance?.check_out_at; function formatTime(dateStr: string | null): string { - if (!dateStr) return '-'; + if (!dateStr) { +return '-'; +} + const d = new Date(dateStr); + return d.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit', hour12: false }); } diff --git a/resources/js/components/hooks/use-card-table-expand.ts b/resources/js/components/hooks/use-card-table-expand.ts index a99e4a2..f370554 100644 --- a/resources/js/components/hooks/use-card-table-expand.ts +++ b/resources/js/components/hooks/use-card-table-expand.ts @@ -6,8 +6,14 @@ export function useCardTableExpand( defaultExpanded: boolean | (number | string)[] = false, ) { const [expandedKeys, setExpandedKeys] = useState(() => { - if (defaultExpanded === true) return 'all'; - if (Array.isArray(defaultExpanded)) return new Set(defaultExpanded); + if (defaultExpanded === true) { +return 'all'; +} + + if (Array.isArray(defaultExpanded)) { +return new Set(defaultExpanded); +} + return new Set(); }); @@ -16,12 +22,15 @@ export function useCardTableExpand( if (prev === 'all') { return new Set([key]); } + const next = new Set(prev); + if (next.has(key)) { next.delete(key); } else { next.add(key); } + return next; }); }, []); @@ -36,7 +45,10 @@ export function useCardTableExpand( const isExpanded = useCallback( (key: number | string): boolean => { - if (expandedKeys === 'all') return true; + if (expandedKeys === 'all') { +return true; +} + return expandedKeys.has(key); }, [expandedKeys], diff --git a/resources/js/hooks/use-can.ts b/resources/js/hooks/use-can.ts index f1a3eff..ae8ad98 100644 --- a/resources/js/hooks/use-can.ts +++ b/resources/js/hooks/use-can.ts @@ -17,7 +17,10 @@ type PageProps = { }; function extractNames(items?: RoleOrPermission[]): string[] { - if (!items) return []; + if (!items) { +return []; +} + return items.map((item) => (typeof item === 'string' ? item : item.name)); } @@ -29,24 +32,42 @@ export function useCan() { const permissionNames = extractNames(user?.permissions); function can(permission: string): boolean { - if (!user) return false; - if (roleNames.includes('developer') || roleNames.includes('owner')) return true; + if (!user) { +return false; +} + + if (roleNames.includes('developer') || roleNames.includes('owner')) { +return true; +} + return permissionNames.includes(permission); } function canAny(...permissions: string[]): boolean { - if (!user) return false; - if (roleNames.includes('developer') || roleNames.includes('owner')) return true; + if (!user) { +return false; +} + + if (roleNames.includes('developer') || roleNames.includes('owner')) { +return true; +} + return permissions.some((p) => permissionNames.includes(p)); } function hasRole(role: string): boolean { - if (!user) return false; + if (!user) { +return false; +} + return roleNames.includes(role); } function hasAnyRole(roles: string[]): boolean { - if (!user) return false; + if (!user) { +return false; +} + return roles.some((role) => roleNames.includes(role)); } diff --git a/resources/js/lib/rupiah.ts b/resources/js/lib/rupiah.ts index a3a2759..d72854a 100644 --- a/resources/js/lib/rupiah.ts +++ b/resources/js/lib/rupiah.ts @@ -6,12 +6,15 @@ export function formatRupiahShort(value: number): string { if (value >= 1_000_000_000) { return (value / 1_000_000_000).toFixed(1).replace('.0', '') + 'jt'; } + if (value >= 1_000_000) { return (value / 1_000_000).toFixed(1).replace('.0', '') + 'jt'; } + if (value >= 1_000) { return (value / 1_000).toFixed(0) + 'rb'; } + return value.toString(); } diff --git a/resources/js/pages/admin/manage/cutting/create.tsx b/resources/js/pages/admin/manage/cutting/create.tsx index 3571d8f..b29619b 100644 --- a/resources/js/pages/admin/manage/cutting/create.tsx +++ b/resources/js/pages/admin/manage/cutting/create.tsx @@ -1,5 +1,8 @@ 'use no memo'; +import { Form, Head, Link, usePage } from '@inertiajs/react'; +import { ArrowLeft, Check, Layers, Plus, ShoppingCart, Trash2 } from 'lucide-react'; +import { useCallback, useMemo, useRef, useState } from 'react'; import { ConfirmDialog } from '@/components/confirm-dialog'; import { FileUpload } from '@/components/file-upload'; import { ImagePreviewModal } from '@/components/image-preview-modal'; @@ -19,9 +22,6 @@ import { formatNumber } from '@/lib/format'; import { getTemporaryUrl } from '@/lib/upload'; import { formatCurrency } from '@/lib/utils'; import { index as cuttingIndex, store } from '@/routes/admin/manage/cuttings'; -import { Form, Head, Link, usePage } from '@inertiajs/react'; -import { ArrowLeft, Check, Layers, Plus, ShoppingCart, Trash2 } from 'lucide-react'; -import { useCallback, useMemo, useRef, useState } from 'react'; import type { CuttingCreateData } from './columns'; type MaterialState = { @@ -63,6 +63,7 @@ export default function CuttingCreate({ data }: Props) { photo_url: m.photo_url, })); } + return []; }); const [combinations, setCombinations] = useState(() => { @@ -71,6 +72,7 @@ export default function CuttingCreate({ data }: Props) { material_result: c.material_result ?? 0, })); } + return []; }); const [selectedMaterialName, setSelectedMaterialName] = useState(draft?.selectedMaterialName ?? ''); @@ -149,12 +151,21 @@ export default function CuttingCreate({ data }: Props) { const addVariant = useCallback( (priceId: number) => { - if (!selectedMaterial) return; + if (!selectedMaterial) { +return; +} + const price = selectedMaterial.raw_material_prices.find((p) => p.id === priceId); - if (!price) return; + + if (!price) { +return; +} setMaterials((prev) => { - if (prev.some((m) => m.raw_material_price_id === priceId && m.combination_id === null)) return prev; + if (prev.some((m) => m.raw_material_price_id === priceId && m.combination_id === null)) { +return prev; +} + return [ ...prev, { @@ -187,21 +198,26 @@ export default function CuttingCreate({ data }: Props) { }, []); const confirmCombo = useCallback(() => { - if (comboSelectedPriceIds.length < 2) return; + if (comboSelectedPriceIds.length < 2) { +return; +} const comboIndex = combinations.length; const newMaterials: MaterialState[] = comboSelectedPriceIds.map((priceId) => { let foundMaterial: typeof rawMaterials[number] | undefined; let foundPrice: typeof rawMaterials[number]['raw_material_prices'][number] | undefined; + for (const rm of rawMaterials) { const p = rm.raw_material_prices.find((pp) => pp.id === priceId); + if (p) { foundMaterial = rm; foundPrice = p; break; } } + return { raw_material_price_id: priceId, material_usage: 0, @@ -238,6 +254,7 @@ export default function CuttingCreate({ data }: Props) { setMaterials((prev) => { const updated = [...prev]; (updated[index] as Record)[field] = value; + return updated; }); }, @@ -251,6 +268,7 @@ export default function CuttingCreate({ data }: Props) { const totalMaterialCost = useMemo(() => { return materials.reduce((sum, m) => { const price = priceMap.get(m.raw_material_price_id); + return sum + (price ? price.price * m.material_usage : 0); }, 0); }, [materials, priceMap]); @@ -297,7 +315,9 @@ export default function CuttingCreate({ data }: Props) { -
({ ...data, ...getPayload() })} onSubmit={() => { submittingRef.current = true; }}> + ({ ...data, ...getPayload() })} onSubmit={() => { + submittingRef.current = true; +}}> {({ errors, processing }) => (
@@ -337,6 +357,7 @@ export default function CuttingCreate({ data }: Props) { {selectedMaterial.raw_material_prices.map((price) => { const isAdded = materials.some((m) => m.raw_material_price_id === price.id); const addedCount = materials.filter((m) => m.raw_material_price_id === price.id).length; + return (
@@ -435,7 +456,9 @@ export default function CuttingCreate({ data }: Props) {
- { setPhoto(key); setPhotoUrl(key ? getTemporaryUrl(key) : null); }} folder="cutting" existingUrl={photoUrl} onUploadingChange={setUploading} /> + { + setPhoto(key); setPhotoUrl(key ? getTemporaryUrl(key) : null); +}} folder="cutting" existingUrl={photoUrl} onUploadingChange={setUploading} />
@@ -474,7 +497,10 @@ export default function CuttingCreate({ data }: Props) { materials.forEach((m, i) => { if (m.combination_id !== null) { - if (!comboMap.has(m.combination_id)) comboMap.set(m.combination_id, []); + if (!comboMap.has(m.combination_id)) { +comboMap.set(m.combination_id, []); +} + comboMap.get(m.combination_id)!.push({ m, index: i }); } else { singleItems.push({ m, index: i }); @@ -509,6 +535,7 @@ export default function CuttingCreate({ data }: Props) { {group.items.map(({ m, index }) => { const price = priceMap.get(m.raw_material_price_id); const cartKey = `material-${index}`; + return (
@@ -571,7 +598,11 @@ export default function CuttingCreate({ data }: Props) { { if (!open) setPreviewKey(null); }} + onOpenChange={(open) => { + if (!open) { +setPreviewKey(null); +} +}} src={previewKey && previewKey.startsWith('material-') ? (materials[Number(previewKey.replace('material-', ''))]?.photo_url ?? null) : null} title={previewKey && previewKey.startsWith('material-') ? `${materials[Number(previewKey.replace('material-', ''))]?.material_name} — ${materials[Number(previewKey.replace('material-', ''))]?.variant}` : undefined} sources={materials.filter((m) => m.photo_url).map((m) => m.photo_url!)} @@ -619,8 +650,10 @@ export default function CuttingCreate({ data }: Props) { let variantName = ''; let materialName = ''; let photoUrl: string | null = null; + for (const rm of rawMaterials) { const p = rm.raw_material_prices.find((pp) => pp.id === priceId); + if (p) { variantName = p.variant; materialName = rm.name; @@ -628,6 +661,7 @@ export default function CuttingCreate({ data }: Props) { break; } } + return (
@@ -657,6 +691,7 @@ export default function CuttingCreate({ data }: Props) {
{comboMaterial.raw_material_prices.map((price) => { const isSelected = comboSelectedPriceIds.includes(price.id); + return (
@@ -689,11 +724,41 @@ export default function CuttingCreate({ data }: Props) { - { if (!open) { setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); } }} title="Hapus Bahan Baku" description="Apakah Anda yakin ingin menghapus bahan baku ini?" confirmLabel="Hapus" onConfirm={() => { if (deleteMaterialIndex !== null) setMaterials((prev) => prev.filter((_, i) => i !== deleteMaterialIndex)); setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); }} /> + { + if (!open) { + setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); +} +}} title="Hapus Bahan Baku" description="Apakah Anda yakin ingin menghapus bahan baku ini?" confirmLabel="Hapus" onConfirm={() => { + if (deleteMaterialIndex !== null) { +setMaterials((prev) => prev.filter((_, i) => i !== deleteMaterialIndex)); +} - { if (!open) { setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); } }} title="Hapus dari Keranjang" description="Apakah Anda yakin ingin menghapus item ini dari keranjang?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { if (cartDeleteIndex !== null) setMaterials((prev) => prev.filter((_, i) => i !== cartDeleteIndex)); setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); }} /> + setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); +}} /> - { if (!open) { setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); } }} title="Hapus Kombinasi" description="Apakah Anda yakin ingin menghapus kombinasi ini beserta semua bahannya?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { if (comboDeleteIndex !== null) { setCombinations((prev) => prev.filter((_, i) => i !== comboDeleteIndex)); setMaterials((prev) => prev.filter((m) => m.combination_id !== comboDeleteIndex)); } setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); }} /> + { + if (!open) { + setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); +} +}} title="Hapus dari Keranjang" description="Apakah Anda yakin ingin menghapus item ini dari keranjang?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { + if (cartDeleteIndex !== null) { +setMaterials((prev) => prev.filter((_, i) => i !== cartDeleteIndex)); +} + + setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); +}} /> + + { + if (!open) { + setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); +} +}} title="Hapus Kombinasi" description="Apakah Anda yakin ingin menghapus kombinasi ini beserta semua bahannya?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { + if (comboDeleteIndex !== null) { + setCombinations((prev) => prev.filter((_, i) => i !== comboDeleteIndex)); setMaterials((prev) => prev.filter((m) => m.combination_id !== comboDeleteIndex)); +} + + setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); +}} />
); diff --git a/resources/js/pages/admin/manage/cutting/edit.tsx b/resources/js/pages/admin/manage/cutting/edit.tsx index 435ec5e..b272a8a 100644 --- a/resources/js/pages/admin/manage/cutting/edit.tsx +++ b/resources/js/pages/admin/manage/cutting/edit.tsx @@ -1,5 +1,8 @@ 'use no memo'; +import { Form, Head, Link } from '@inertiajs/react'; +import { ArrowLeft, Check, Layers, Plus, ShoppingCart, Trash2 } from 'lucide-react'; +import { useCallback, useMemo, useRef, useState } from 'react'; import { ConfirmDialog } from '@/components/confirm-dialog'; import { FileUpload } from '@/components/file-upload'; import { ImagePreviewModal } from '@/components/image-preview-modal'; @@ -17,9 +20,6 @@ import { formatNumber } from '@/lib/format'; import { getTemporaryUrl } from '@/lib/upload'; import { formatCurrency } from '@/lib/utils'; import { index as cuttingIndex, update } from '@/routes/admin/manage/cuttings'; -import { Form, Head, Link } from '@inertiajs/react'; -import { ArrowLeft, Check, Layers, Plus, ShoppingCart, Trash2 } from 'lucide-react'; -import { useCallback, useMemo, useRef, useState } from 'react'; import type { CuttingCreateData, CuttingForEdit } from './columns'; type MaterialState = { @@ -131,12 +131,21 @@ export default function CuttingEdit({ cutting, data }: Props) { const addVariant = useCallback( (priceId: number) => { - if (!selectedMaterial) return; + if (!selectedMaterial) { +return; +} + const price = selectedMaterial.raw_material_prices.find((p) => p.id === priceId); - if (!price) return; + + if (!price) { +return; +} setMaterials((prev) => { - if (prev.some((m) => m.raw_material_price_id === priceId && m.combination_id === null)) return prev; + if (prev.some((m) => m.raw_material_price_id === priceId && m.combination_id === null)) { +return prev; +} + return [ ...prev, { @@ -169,21 +178,26 @@ export default function CuttingEdit({ cutting, data }: Props) { }, []); const confirmCombo = useCallback(() => { - if (comboSelectedPriceIds.length < 2) return; + if (comboSelectedPriceIds.length < 2) { +return; +} const comboIndex = combinations.length; const newMaterials: MaterialState[] = comboSelectedPriceIds.map((priceId) => { let foundMaterial: (typeof rawMaterials)[number] | undefined; let foundPrice: (typeof rawMaterials)[number]['raw_material_prices'][number] | undefined; + for (const rm of rawMaterials) { const p = rm.raw_material_prices.find((pp) => pp.id === priceId); + if (p) { foundMaterial = rm; foundPrice = p; break; } } + return { raw_material_price_id: priceId, material_usage: 0, @@ -215,6 +229,7 @@ export default function CuttingEdit({ cutting, data }: Props) { setMaterials((prev) => { const updated = [...prev]; (updated[index] as Record)[field] = value; + return updated; }); }, @@ -228,6 +243,7 @@ export default function CuttingEdit({ cutting, data }: Props) { const totalMaterialCost = useMemo(() => { return materials.reduce((sum, m) => { const price = priceMap.get(m.raw_material_price_id); + return sum + (price ? price.price * m.material_usage : 0); }, 0); }, [materials, priceMap]); @@ -274,7 +290,9 @@ export default function CuttingEdit({ cutting, data }: Props) {
- ({ ...data, ...getPayload() })} onSubmit={() => { submittingRef.current = true; }}> + ({ ...data, ...getPayload() })} onSubmit={() => { + submittingRef.current = true; +}}> {({ errors, processing }) => (
@@ -314,6 +332,7 @@ export default function CuttingEdit({ cutting, data }: Props) { {selectedMaterial.raw_material_prices.map((price) => { const isAdded = materials.some((m) => m.raw_material_price_id === price.id); const addedCount = materials.filter((m) => m.raw_material_price_id === price.id).length; + return (
@@ -410,7 +429,9 @@ export default function CuttingEdit({ cutting, data }: Props) {
- { setPhoto(key); setPhotoUrl(key ? getTemporaryUrl(key) : null); }} folder="cutting" existingUrl={photoUrl} onUploadingChange={setUploading} /> + { + setPhoto(key); setPhotoUrl(key ? getTemporaryUrl(key) : null); +}} folder="cutting" existingUrl={photoUrl} onUploadingChange={setUploading} />
@@ -449,7 +470,10 @@ export default function CuttingEdit({ cutting, data }: Props) { materials.forEach((m, i) => { if (m.combination_id !== null) { - if (!comboMap.has(m.combination_id)) comboMap.set(m.combination_id, []); + if (!comboMap.has(m.combination_id)) { +comboMap.set(m.combination_id, []); +} + comboMap.get(m.combination_id)!.push({ m, index: i }); } else { singleItems.push({ m, index: i }); @@ -484,6 +508,7 @@ export default function CuttingEdit({ cutting, data }: Props) { {group.items.map(({ m, index }) => { const price = priceMap.get(m.raw_material_price_id); const cartKey = `material-${index}`; + return (
@@ -546,7 +571,11 @@ export default function CuttingEdit({ cutting, data }: Props) { { if (!open) setPreviewKey(null); }} + onOpenChange={(open) => { + if (!open) { +setPreviewKey(null); +} +}} src={previewKey && previewKey.startsWith('material-') ? (materials[Number(previewKey.replace('material-', ''))]?.photo_url ?? null) : null} title={previewKey && previewKey.startsWith('material-') ? `${materials[Number(previewKey.replace('material-', ''))]?.material_name} — ${materials[Number(previewKey.replace('material-', ''))]?.variant}` : undefined} sources={materials.filter((m) => m.photo_url).map((m) => m.photo_url!)} @@ -594,8 +623,10 @@ export default function CuttingEdit({ cutting, data }: Props) { let variantName = ''; let materialName = ''; let photoUrl: string | null = null; + for (const rm of rawMaterials) { const p = rm.raw_material_prices.find((pp) => pp.id === priceId); + if (p) { variantName = p.variant; materialName = rm.name; @@ -603,6 +634,7 @@ export default function CuttingEdit({ cutting, data }: Props) { break; } } + return (
@@ -632,6 +664,7 @@ export default function CuttingEdit({ cutting, data }: Props) {
{comboMaterial.raw_material_prices.map((price) => { const isSelected = comboSelectedPriceIds.includes(price.id); + return (
@@ -664,11 +697,41 @@ export default function CuttingEdit({ cutting, data }: Props) { - { if (!open) { setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); } }} title="Hapus Bahan Baku" description="Apakah Anda yakin ingin menghapus bahan baku ini?" confirmLabel="Hapus" onConfirm={() => { if (deleteMaterialIndex !== null) setMaterials((prev) => prev.filter((_, i) => i !== deleteMaterialIndex)); setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); }} /> + { + if (!open) { + setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); +} +}} title="Hapus Bahan Baku" description="Apakah Anda yakin ingin menghapus bahan baku ini?" confirmLabel="Hapus" onConfirm={() => { + if (deleteMaterialIndex !== null) { +setMaterials((prev) => prev.filter((_, i) => i !== deleteMaterialIndex)); +} - { if (!open) { setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); } }} title="Hapus dari Keranjang" description="Apakah Anda yakin ingin menghapus item ini dari keranjang?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { if (cartDeleteIndex !== null) setMaterials((prev) => prev.filter((_, i) => i !== cartDeleteIndex)); setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); }} /> + setDeleteConfirmOpen(false); setDeleteMaterialIndex(null); +}} /> - { if (!open) { setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); } }} title="Hapus Kombinasi" description="Apakah Anda yakin ingin menghapus kombinasi ini beserta semua bahannya?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { if (comboDeleteIndex !== null) { setCombinations((prev) => prev.filter((_, i) => i !== comboDeleteIndex)); setMaterials((prev) => prev.filter((m) => m.combination_id !== comboDeleteIndex)); } setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); }} /> + { + if (!open) { + setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); +} +}} title="Hapus dari Keranjang" description="Apakah Anda yakin ingin menghapus item ini dari keranjang?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { + if (cartDeleteIndex !== null) { +setMaterials((prev) => prev.filter((_, i) => i !== cartDeleteIndex)); +} + + setCartDeleteConfirmOpen(false); setCartDeleteIndex(null); +}} /> + + { + if (!open) { + setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); +} +}} title="Hapus Kombinasi" description="Apakah Anda yakin ingin menghapus kombinasi ini beserta semua bahannya?" confirmLabel="Hapus" variant="destructive" onConfirm={() => { + if (comboDeleteIndex !== null) { + setCombinations((prev) => prev.filter((_, i) => i !== comboDeleteIndex)); setMaterials((prev) => prev.filter((m) => m.combination_id !== comboDeleteIndex)); +} + + setComboDeleteConfirmOpen(false); setComboDeleteIndex(null); +}} />
); diff --git a/resources/js/pages/admin/manage/purchase/index.tsx b/resources/js/pages/admin/manage/purchase/index.tsx index e927101..0d7006c 100644 --- a/resources/js/pages/admin/manage/purchase/index.tsx +++ b/resources/js/pages/admin/manage/purchase/index.tsx @@ -74,7 +74,6 @@ export default function PurchaseIndex({ pagination, filters, filterWithParams: false, - reloadOnly: ['purchases', 'filters'], }); const selectedSupplier = useMemo( diff --git a/resources/js/pages/admin/manage/transaction/create.tsx b/resources/js/pages/admin/manage/transaction/create.tsx index e1ec084..5ea51b3 100644 --- a/resources/js/pages/admin/manage/transaction/create.tsx +++ b/resources/js/pages/admin/manage/transaction/create.tsx @@ -28,7 +28,6 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Switch } from '@/components/ui/switch'; import { Sheet, SheetContent, @@ -36,6 +35,7 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; +import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { useTransactionDraftSave } from '@/hooks/use-transaction-draft'; import { formatNumber } from '@/lib/format'; @@ -182,6 +182,7 @@ export default function TransactionCreate({ data }: Props) { if (stockType === 'reject') { return priceTypeOptions.filter((o) => o.value === 'reject'); } + return priceTypeOptions.filter((o) => SELLING_PRICE_TYPES.includes(o.value)); }, [stockType, priceTypeOptions]); diff --git a/resources/js/pages/admin/manage/transaction/edit.tsx b/resources/js/pages/admin/manage/transaction/edit.tsx index 8eddb6a..326ef81 100644 --- a/resources/js/pages/admin/manage/transaction/edit.tsx +++ b/resources/js/pages/admin/manage/transaction/edit.tsx @@ -28,7 +28,6 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Switch } from '@/components/ui/switch'; import { Sheet, SheetContent, @@ -36,6 +35,7 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; +import { Switch } from '@/components/ui/switch'; import { Textarea } from '@/components/ui/textarea'; import { formatNumber } from '@/lib/format'; import { getTemporaryUrl } from '@/lib/upload'; @@ -151,6 +151,7 @@ export default function TransactionEdit({ transaction, data }: Props) { if (stockType === 'reject') { return priceTypeOptions.filter((o) => o.value === 'reject'); } + return priceTypeOptions.filter((o) => SELLING_PRICE_TYPES.includes(o.value)); }, [stockType, priceTypeOptions]); diff --git a/resources/js/pages/admin/master/raw-material/index.tsx b/resources/js/pages/admin/master/raw-material/index.tsx index a860b68..67fcf45 100644 --- a/resources/js/pages/admin/master/raw-material/index.tsx +++ b/resources/js/pages/admin/master/raw-material/index.tsx @@ -1,3 +1,6 @@ +import { Head, Link, router } from '@inertiajs/react'; +import { Plus } from 'lucide-react'; +import { useState } from 'react'; import { CardTable } from '@/components/card-table'; import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog'; import { FilterPopover } from '@/components/filter-popover'; @@ -11,6 +14,7 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; +import { useCan } from '@/hooks/use-can'; import { useServerTable } from '@/hooks/use-server-table'; import { destroy, @@ -22,13 +26,9 @@ import { import { destroy as variantDestroy } from '@/routes/admin/master/raw-materials/variants'; -import { Head, Link, router } from '@inertiajs/react'; -import { Plus } from 'lucide-react'; -import { useState } from 'react'; import type { RawMaterial, RawMaterialVariant } from './columns'; import { RawMaterialCardRow } from './raw-material-card'; import { RawMaterialVariantSubRow } from './variant/sub-row'; -import { useCan } from '@/hooks/use-can'; type Props = { rawMaterials: {