feat: enhance UI consistency by adding consistent icons to action buttons and loaders to save operations across admin modules
This commit is contained in:
parent
2b43213ee4
commit
26eaaf6d66
@ -2,7 +2,7 @@ import { Head } from '@inertiajs/react';
|
||||
import type { Expense } from '@/types';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Trash2, X } from 'lucide-react';
|
||||
import { Trash2, X, Plus } from 'lucide-react';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import {
|
||||
AlertDialog,
|
||||
@ -55,7 +55,8 @@ export default function ExpenseIndex({ expenses }: { expenses: Expense[] }) {
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Pengeluaran</h1>
|
||||
</div>
|
||||
<Button onClick={onAdd}>
|
||||
<Button onClick={onAdd} className="gap-2">
|
||||
<Plus className="size-4" />
|
||||
Tambah
|
||||
</Button>
|
||||
</div>
|
||||
@ -119,8 +120,14 @@ export default function ExpenseIndex({ expenses }: { expenses: Expense[] }) {
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive" className="gap-2">
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -138,11 +145,16 @@ export default function ExpenseIndex({ expenses }: { expenses: Expense[] }) {
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmBulkDelete}
|
||||
variant="destructive"
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@ -15,7 +15,7 @@ import { useEffect, useState, useRef } from 'react';
|
||||
import expenseRoutes from '@/routes/expense';
|
||||
import { toast } from 'sonner';
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { ImagePlus, X } from 'lucide-react';
|
||||
import { ImagePlus, X, Save, Loader } from 'lucide-react';
|
||||
|
||||
interface ExpenseFormModalProps {
|
||||
isOpen: boolean;
|
||||
@ -183,8 +183,12 @@ export function ExpenseFormModal({ isOpen, onClose, expense }: ExpenseFormModalP
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<DialogFooter className="mt-6">
|
||||
<Button type="button" variant="outline" onClick={handleClose}>Batal</Button>
|
||||
<Button type="submit" disabled={processing}>
|
||||
<Button type="button" variant="outline" className="gap-2" onClick={handleClose}>
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</Button>
|
||||
<Button type="submit" className="gap-2" disabled={processing}>
|
||||
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head } from '@inertiajs/react';
|
||||
import type { Payroll } from '@/types';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { Trash2, Sparkles, Plus, X } from 'lucide-react';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import {
|
||||
AlertDialog,
|
||||
@ -60,7 +60,8 @@ export default function PayrollIndex({
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Penggajian</h1>
|
||||
</div>
|
||||
<Button onClick={onGenerate} disabled={isGenerating}>
|
||||
<Button onClick={onGenerate} disabled={isGenerating} className="gap-2">
|
||||
{isGenerating ? <Sparkles className="size-4 animate-spin" /> : <Sparkles className="size-4" />}
|
||||
Generate
|
||||
</Button>
|
||||
</div>
|
||||
@ -158,8 +159,14 @@ export default function PayrollIndex({
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive" className="gap-2">
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -177,11 +184,16 @@ export default function PayrollIndex({
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmBulkDelete}
|
||||
variant="destructive"
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@ -16,10 +16,11 @@ import { useForm } from '@inertiajs/react';
|
||||
import { useEffect } from 'react';
|
||||
import payrollRoutes from '@/routes/payroll';
|
||||
import { toast } from 'sonner';
|
||||
import { Plus, Trash2 } from 'lucide-react';
|
||||
import { Loader, Plus, Trash2 } from 'lucide-react';
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from '@/components/ui/empty';
|
||||
import { X, Save } from 'lucide-react';
|
||||
|
||||
interface PayrollFormModalProps {
|
||||
isOpen: boolean;
|
||||
@ -96,7 +97,8 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
|
||||
<FieldGroup>
|
||||
<div className="flex items-center justify-between border-b pb-2 mb-4">
|
||||
<h3 className="text-sm font-medium">Bonus & Potongan</h3>
|
||||
<Button type="button" variant="outline" size="sm" onClick={addAdjustment}>
|
||||
<Button type="button" variant="outline" size="sm" onClick={addAdjustment} className="gap-2">
|
||||
<Plus className="size-4" />
|
||||
Tambah
|
||||
</Button>
|
||||
</div>
|
||||
@ -180,8 +182,12 @@ export function PayrollFormModal({ isOpen, onClose, payroll }: PayrollFormModalP
|
||||
</div>
|
||||
|
||||
<DialogFooter className="mt-6">
|
||||
<Button type="button" variant="outline" onClick={handleClose}>Batal</Button>
|
||||
<Button type="submit" disabled={processing}>
|
||||
<Button type="button" variant="outline" className="gap-2" onClick={handleClose}>
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</Button>
|
||||
<Button type="submit" className="gap-2" disabled={processing}>
|
||||
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@ -9,7 +9,7 @@ import React, { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Product, ProductPrice } from '@/types';
|
||||
import { OrderItem } from '@/types/order';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Tag } from 'lucide-react';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Tag, ArrowLeft, Check, Loader, Save } from 'lucide-react';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { format } from 'date-fns';
|
||||
import { cn } from '@/lib/utils';
|
||||
@ -66,8 +66,8 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
const { data, setData, post, processing, errors, transform } = useForm({
|
||||
invoice_number: '',
|
||||
customer_name: '',
|
||||
discount: '',
|
||||
payment: '',
|
||||
discount: 0,
|
||||
payment: 0,
|
||||
payment_method: 'cash',
|
||||
order_status: 'delivered',
|
||||
order_channel: 'store',
|
||||
@ -181,8 +181,8 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
return cartItems.reduce((acc, item) => acc + item.total, 0);
|
||||
}, [cartItems]);
|
||||
|
||||
const total = subtotal - data.discount;
|
||||
const change = data.payment - total;
|
||||
const total = subtotal - (Number(data.discount) || 0);
|
||||
const change = (Number(data.payment) || 0) - total;
|
||||
const cartTotalItems = cartItems.reduce((a, i) => a + i.qty, 0);
|
||||
|
||||
const PriceTypeLabel = ({ type }: { type: string }) => {
|
||||
@ -365,7 +365,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
autoComplete='off'
|
||||
/>
|
||||
</Field>
|
||||
{change >= 0 && data.payment > 0 && (
|
||||
{change >= 0 && (Number(data.payment) || 0) > 0 && (
|
||||
<div className="flex justify-between items-center p-2 rounded bg-green-500/10 border border-green-500/20 text-green-600 dark:text-green-400">
|
||||
<span className="text-xs font-bold uppercase">Kembalian</span>
|
||||
<span className="text-lg font-bold">Rp {change.toLocaleString('id-ID')}</span>
|
||||
@ -383,8 +383,9 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Checkout Pesanan'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save
|
||||
className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -397,7 +398,10 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
<div className="flex items-center justify-between shrink-0">
|
||||
<h1 className="text-3xl font-bold tracking-tight">Tambah Pesanan</h1>
|
||||
<Link href={orderRoutes.index().url}>
|
||||
<Button variant='outline'>Kembali</Button>
|
||||
<Button variant='outline' className="gap-2">
|
||||
<ArrowLeft className="size-4" />
|
||||
Kembali
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@ -616,8 +620,14 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setQtyDialogIndex(null)}>Batal</Button>
|
||||
<Button onClick={confirmQty}>Konfirmasi</Button>
|
||||
<Button variant="outline" className="gap-2" onClick={() => setQtyDialogIndex(null)}>
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</Button>
|
||||
<Button className="gap-2" onClick={confirmQty}>
|
||||
<Check className="size-4" />
|
||||
Konfirmasi
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -9,7 +9,7 @@ import React, { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Product, ProductPrice } from '@/types';
|
||||
import { Order, OrderItem } from '@/types/order';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Tag } from 'lucide-react';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Tag, ArrowLeft, Check, Save, Loader } from 'lucide-react';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { format } from 'date-fns';
|
||||
import { cn } from '@/lib/utils';
|
||||
@ -371,7 +371,7 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full h-10 font-semibold shadow"
|
||||
className="w-full h-10 font-semibold shadow gap-2"
|
||||
disabled={processing || data.items.length === 0}
|
||||
onClick={() => {
|
||||
if (data.items.length > 0) {
|
||||
@ -379,8 +379,8 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Perbarui Pesanan'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -393,7 +393,10 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
<div className="flex items-center justify-between shrink-0">
|
||||
<h1 className="text-3xl font-bold tracking-tight">Ubah Pesanan</h1>
|
||||
<Link href={orderRoutes.index().url}>
|
||||
<Button variant='outline'>Kembali</Button>
|
||||
<Button variant='outline' className="gap-2">
|
||||
<ArrowLeft className="size-4" />
|
||||
Kembali
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@ -603,8 +606,14 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setQtyDialogIndex(null)}>Batal</Button>
|
||||
<Button onClick={confirmQty}>Konfirmasi</Button>
|
||||
<Button variant="outline" className="gap-2" onClick={() => setQtyDialogIndex(null)}>
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</Button>
|
||||
<Button className="gap-2" onClick={confirmQty}>
|
||||
<Check className="size-4" />
|
||||
Konfirmasi
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -3,7 +3,7 @@ import { useCallback } from 'react';
|
||||
import type { Order } from '@/types/order';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Trash2, ShoppingBag, Printer, Bluetooth, Usb, Unplug } from 'lucide-react';
|
||||
import { Trash2, ShoppingBag, Printer, Bluetooth, Usb, Unplug, Plus, X, ArrowLeft } from 'lucide-react';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -184,7 +184,8 @@ export default function OrderIndex({ orders }: { orders: Order[] }) {
|
||||
</DropdownMenu>
|
||||
|
||||
<Link href={orderRoutes.create().url}>
|
||||
<Button>
|
||||
<Button className="gap-2">
|
||||
<Plus className="size-4" />
|
||||
Tambah
|
||||
</Button>
|
||||
</Link>
|
||||
@ -220,14 +221,20 @@ export default function OrderIndex({ orders }: { orders: Order[] }) {
|
||||
<AlertDialogMedia className="bg-destructive/10 text-destructive dark:bg-destructive/20 dark:text-destructive">
|
||||
<Trash2 className="size-5" />
|
||||
</AlertDialogMedia>
|
||||
<AlertDialogTitle>Hapus data pesanan?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Hapus pesanan?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Tindakan ini tidak dapat dibatalkan. Data pesanan dengan nomor invoice <strong>{orderToDelete?.invoice_number}</strong> akan dihapus secara permanen dan stok akan dikembalikan.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive" className="gap-2">
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -245,11 +252,16 @@ export default function OrderIndex({ orders }: { orders: Order[] }) {
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmBulkDelete}
|
||||
variant="destructive"
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@ -8,7 +8,7 @@ import purchaseRoutes from '@/routes/purchase';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Product, ProductPrice } from '@/types';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X } from 'lucide-react';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Loader, Save } from 'lucide-react';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import { format } from 'date-fns';
|
||||
@ -300,8 +300,8 @@ export default function PurchaseCreate({ products, cartItems }: { products: Prod
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Checkout Belanja'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -593,8 +593,8 @@ export default function PurchaseCreate({ products, cartItems }: { products: Prod
|
||||
className="w-full h-10 font-semibold shadow"
|
||||
disabled={processing || cartItems.length === 0}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Checkout Belanja'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -769,8 +769,8 @@ export default function PurchaseCreate({ products, cartItems }: { products: Prod
|
||||
disabled={processing || cartItems.length === 0}
|
||||
onClick={() => onSubmit({ preventDefault: () => { } } as any)}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Checkout Belanja'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</SheetContent>
|
||||
|
||||
@ -8,7 +8,7 @@ import purchaseRoutes from '@/routes/purchase';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Product, ProductPrice, Purchase } from '@/types';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X } from 'lucide-react';
|
||||
import { Plus, Trash2, CalendarIcon, Minus, ShoppingCart, Search, ImagePlus, X, Loader, Save } from 'lucide-react';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import { format } from 'date-fns';
|
||||
@ -304,8 +304,8 @@ export default function PurchaseEdit({ purchase, products }: { purchase: Purchas
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Perbarui Belanja'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -597,8 +597,8 @@ export default function PurchaseEdit({ purchase, products }: { purchase: Purchas
|
||||
className="w-full h-10 font-semibold shadow"
|
||||
disabled={processing || data.items.length === 0}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Perbarui Belanja'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -773,8 +773,8 @@ export default function PurchaseEdit({ purchase, products }: { purchase: Purchas
|
||||
disabled={processing || data.items.length === 0}
|
||||
onClick={() => onSubmit({ preventDefault: () => { } } as any)}
|
||||
>
|
||||
<ShoppingCart className="h-4 w-4 mr-2" />
|
||||
{processing ? 'Menyimpan...' : 'Perbarui Belanja'}
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</SheetContent>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head } from '@inertiajs/react';
|
||||
import type { Category } from '@/types';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { Trash2, Plus, X } from 'lucide-react';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import {
|
||||
AlertDialog,
|
||||
@ -52,7 +52,8 @@ export default function CategoryIndex({ categories }: { categories: Category[] }
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Kategori</h1>
|
||||
</div>
|
||||
<Button onClick={onAdd}>
|
||||
<Button onClick={onAdd} className="gap-2">
|
||||
<Plus className="size-4" />
|
||||
Tambah
|
||||
</Button>
|
||||
</div>
|
||||
@ -108,8 +109,14 @@ export default function CategoryIndex({ categories }: { categories: Category[] }
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive" className="gap-2">
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -127,11 +134,16 @@ export default function CategoryIndex({ categories }: { categories: Category[] }
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmBulkDelete}
|
||||
variant="destructive"
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@ -14,6 +14,7 @@ import { useForm } from '@inertiajs/react';
|
||||
import { useEffect } from 'react';
|
||||
import categoryRoutes from '@/routes/category';
|
||||
import { toast } from 'sonner';
|
||||
import { X, Save, Loader } from 'lucide-react';
|
||||
|
||||
interface CategoryFormModalProps {
|
||||
isOpen: boolean;
|
||||
@ -88,8 +89,12 @@ export function CategoryFormModal({ isOpen, onClose, category }: CategoryFormMod
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<DialogFooter className="mt-6">
|
||||
<Button type="button" variant="outline" onClick={handleClose}>Batal</Button>
|
||||
<Button type="submit" disabled={processing}>
|
||||
<Button type="button" variant="outline" className="gap-2" onClick={handleClose}>
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</Button>
|
||||
<Button type="submit" className="gap-2" disabled={processing}>
|
||||
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@ -23,7 +23,7 @@ import {
|
||||
useComboboxAnchor,
|
||||
} from "@/components/ui/combobox"
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { ImagePlus, X, Upload } from 'lucide-react';
|
||||
import { ImagePlus, X, Upload, ArrowLeft, Save, Loader } from 'lucide-react';
|
||||
|
||||
export default function ProductCreate({ categories }: { categories: Category[] }) {
|
||||
const { data, setData, post, processing, errors } = useForm<{
|
||||
@ -117,7 +117,8 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
<h1 className="text-3xl font-bold tracking-tight">Tambah Produk</h1>
|
||||
</div>
|
||||
<Link href={productRoutes.index().url}>
|
||||
<Button variant='outline'>
|
||||
<Button variant='outline' className="gap-2">
|
||||
<ArrowLeft className="size-4" />
|
||||
Kembali
|
||||
</Button>
|
||||
</Link>
|
||||
@ -401,7 +402,8 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
</Card>
|
||||
|
||||
<div className="flex flex-col gap-4 p-4 rounded-xl bg-primary/5 border border-primary/10 shadow-sm">
|
||||
<Button type="submit" className="w-full" disabled={processing}>
|
||||
<Button type="submit" className="w-full gap-2" disabled={processing}>
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -23,7 +23,7 @@ import {
|
||||
useComboboxAnchor,
|
||||
} from "@/components/ui/combobox"
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { ImagePlus, X, Upload } from 'lucide-react';
|
||||
import { ImagePlus, X, Upload, ArrowLeft, Save, Loader } from 'lucide-react';
|
||||
|
||||
interface ExistingImage {
|
||||
kind: 'existing';
|
||||
@ -167,7 +167,8 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
<h1 className="text-3xl font-bold tracking-tight">Ubah Produk</h1>
|
||||
</div>
|
||||
<Link href={productRoutes.index().url}>
|
||||
<Button variant='outline'>
|
||||
<Button variant='outline' className="gap-2">
|
||||
<ArrowLeft className="size-4" />
|
||||
Kembali
|
||||
</Button>
|
||||
</Link>
|
||||
@ -469,7 +470,8 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
|
||||
<div className="flex flex-col gap-4 p-4 rounded-xl bg-primary/5 border border-primary/10 shadow-sm">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Button type="submit" className="w-full" disabled={processing}>
|
||||
<Button type="submit" className="w-full gap-2" disabled={processing}>
|
||||
{processing ? <Loader className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head, Link } from '@inertiajs/react';
|
||||
import type { Product, Category } from '@/types';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import { Trash2, Plus, X } from 'lucide-react';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import {
|
||||
AlertDialog,
|
||||
@ -52,7 +52,8 @@ export default function ProductIndex({ products, categories }: { products: Produ
|
||||
<h1 className="text-3xl font-bold tracking-tight">Produk</h1>
|
||||
</div>
|
||||
<Link href={productRoutes.create().url}>
|
||||
<Button>
|
||||
<Button className="gap-2">
|
||||
<Plus className="size-4" />
|
||||
Tambah
|
||||
</Button>
|
||||
</Link>
|
||||
@ -111,8 +112,14 @@ export default function ProductIndex({ products, categories }: { products: Produ
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive" className="gap-2">
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -130,11 +137,16 @@ export default function ProductIndex({ products, categories }: { products: Produ
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmBulkDelete}
|
||||
variant="destructive"
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { ArrowLeft, Loader, Save } from 'lucide-react';
|
||||
|
||||
export default function UserCreate() {
|
||||
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
||||
@ -50,7 +51,8 @@ export default function UserCreate() {
|
||||
<h1 className="text-3xl font-bold tracking-tight">Tambah Pegawai</h1>
|
||||
</div>
|
||||
<Link href={userRoutes.index().url}>
|
||||
<Button variant='outline'>
|
||||
<Button variant='outline' className="gap-2">
|
||||
<ArrowLeft className="size-4" />
|
||||
Kembali
|
||||
</Button>
|
||||
</Link>
|
||||
@ -223,6 +225,7 @@ export default function UserCreate() {
|
||||
|
||||
<div className="flex flex-col gap-4 p-4 rounded-xl bg-primary/5 border border-primary/10 shadow-sm">
|
||||
<Button type="submit" className="w-full" disabled={processing}>
|
||||
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
import { NumericFormat } from 'react-number-format';
|
||||
import { ArrowLeft, Loader, Save } from 'lucide-react';
|
||||
|
||||
export default function UserEdit({ user }: { user: any }) {
|
||||
const [isCalendarOpen, setIsCalendarOpen] = React.useState(false);
|
||||
@ -52,6 +53,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
</div>
|
||||
<Link href={userRoutes.index().url}>
|
||||
<Button variant='outline'>
|
||||
<ArrowLeft className="size-4" />
|
||||
Kembali
|
||||
</Button>
|
||||
</Link>
|
||||
@ -224,6 +226,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
|
||||
<div className="flex flex-col gap-4 p-4 rounded-xl bg-primary/5 border border-primary/10 shadow-sm">
|
||||
<Button type="submit" className="w-full" disabled={processing}>
|
||||
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head, Link } from '@inertiajs/react';
|
||||
import type { User } from '@/types';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Trash2, KeyRound } from 'lucide-react';
|
||||
import { Trash2, KeyRound, Plus, X } from 'lucide-react';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import {
|
||||
AlertDialog,
|
||||
@ -52,7 +52,8 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
||||
<h1 className="text-3xl font-bold tracking-tight">Pegawai</h1>
|
||||
</div>
|
||||
<Link href={userRoutes.create().url}>
|
||||
<Button>
|
||||
<Button className="gap-2">
|
||||
<Plus className="size-4" />
|
||||
Tambah
|
||||
</Button>
|
||||
</Link>
|
||||
@ -93,8 +94,14 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmDelete} variant="destructive" className="gap-2">
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -106,14 +113,20 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
||||
<AlertDialogMedia className="bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:text-blue-500">
|
||||
<KeyRound className="size-5" />
|
||||
</AlertDialogMedia>
|
||||
<AlertDialogTitle>Reset password?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Reset kata sandi?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Tindakan ini akan mereset password <strong>{userToReset?.name}</strong> menjadi <strong>{defaultPassword}</strong>. Pengguna dapat mengubahnya kembali nanti.
|
||||
Tindakan ini akan mereset kata sandi <strong>{userToReset?.name}</strong> menjadi <strong>{defaultPassword}</strong>. Pengguna dapat mengubahnya kembali nanti.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmResetPassword} className="bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-600">Reset</AlertDialogAction>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={confirmResetPassword} className="bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-600 gap-2">
|
||||
<KeyRound className="size-4" />
|
||||
Reset
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@ -131,11 +144,16 @@ export default function UserIndex({ users, defaultPassword }: { users: User[], d
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
|
||||
<AlertDialogCancel variant="outline" className="gap-2">
|
||||
<X className="size-4" />
|
||||
Batal
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmBulkDelete}
|
||||
variant="destructive"
|
||||
className="gap-2"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
Hapus
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
|
||||
@ -9,7 +9,7 @@ import type { GeneralSetting } from '@/types';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import systemRoutes from '@/routes/system';
|
||||
import { ImagePlus, X } from 'lucide-react';
|
||||
import { ImagePlus, X, Save, Loader } from 'lucide-react';
|
||||
|
||||
export default function SettingIndex({ setting }: { setting: GeneralSetting | null }) {
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
@ -227,8 +227,9 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
</Card>
|
||||
|
||||
<div className="flex flex-col gap-4 p-4 rounded-xl bg-primary/5 border border-primary/10 shadow-sm">
|
||||
<Button type="submit" className="w-full" disabled={processing}>
|
||||
{processing ? 'Menyimpan...' : 'Simpan Perubahan'}
|
||||
<Button type="submit" className="w-full gap-2" disabled={processing}>
|
||||
{processing ? <Loader className="size-4 animate-spin" /> : <Save className="size-4" />}
|
||||
{processing ? 'Menyimpan...' : 'Simpan'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user