- Added InvoiceIndex component for displaying a list of invoices with pagination and search capabilities. - Created InvoiceShareButton component for sharing invoice details via WhatsApp. - Developed InvoicePrint component for printing invoice details. - Defined routes for invoice management including share and print functionalities. - Implemented InvoiceTest to cover authentication, authorization, and CRUD operations for invoices.
236 lines
9.6 KiB
TypeScript
236 lines
9.6 KiB
TypeScript
import { Link, router } from '@inertiajs/react';
|
|
import type { LucideIcon } from 'lucide-react';
|
|
import {
|
|
Activity,
|
|
ArrowUpFromLine,
|
|
BarChart3,
|
|
Boxes,
|
|
CalendarCheck,
|
|
CalendarDays,
|
|
ClipboardCheck,
|
|
DollarSign,
|
|
HandCoins,
|
|
LayoutGrid,
|
|
Package,
|
|
Receipt,
|
|
RefreshCw,
|
|
Scissors,
|
|
Settings,
|
|
Shield,
|
|
ShoppingCart,
|
|
Tags,
|
|
Truck,
|
|
UserCircle,
|
|
Users,
|
|
Wallet,
|
|
} from 'lucide-react';
|
|
import React from 'react';
|
|
import { AppLogo } from '@/components/brand';
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarGroup,
|
|
SidebarGroupLabel,
|
|
SidebarHeader,
|
|
SidebarMenu,
|
|
SidebarMenuButton,
|
|
SidebarMenuItem,
|
|
useSidebar,
|
|
} from '@/components/ui/sidebar';
|
|
import { useCan } from '@/hooks/use-can';
|
|
import { useCurrentUrl } from '@/hooks/use-current-url';
|
|
import { dashboard } from '@/routes';
|
|
import analysis from '@/routes/admin/analysis';
|
|
import { index as cashAccountsIndex } from '@/routes/admin/finance/cash-accounts';
|
|
import { index as employeeAdvancesIndex } from '@/routes/admin/finance/employee-advances';
|
|
import { index as expensesIndex } from '@/routes/admin/finance/expenses';
|
|
import { current as payrollCurrent } from '@/routes/admin/finance/payroll-periods';
|
|
import { index as attendancesIndex } from '@/routes/admin/hr/attendances';
|
|
import { index as employeesIndex } from '@/routes/admin/hr/employees';
|
|
import { index as leaveRequestsIndex } from '@/routes/admin/hr/leave-requests';
|
|
import { index as cuttingsIndex } from '@/routes/admin/manage/cuttings';
|
|
import { index as invoicesIndex } from '@/routes/admin/manage/invoices';
|
|
import { index as purchasesIndex } from '@/routes/admin/manage/purchases';
|
|
import { index as restocksIndex } from '@/routes/admin/manage/restocks';
|
|
import { index as stokOpnamesIndex } from '@/routes/admin/manage/stok-opnames';
|
|
import { index as transactionsIndex } from '@/routes/admin/manage/transactions';
|
|
import { index as categoriesIndex } from '@/routes/admin/master/categories';
|
|
import { index as customersIndex } from '@/routes/admin/master/customers';
|
|
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';
|
|
|
|
type NavMenuItem = { title: string; href: string; icon: LucideIcon; permission?: string | string[] };
|
|
|
|
const dasborItem: NavMenuItem = {
|
|
title: 'Dasbor',
|
|
href: dashboard.url(),
|
|
icon: LayoutGrid,
|
|
};
|
|
|
|
const analisaItem: NavMenuItem = {
|
|
title: 'Analisa',
|
|
href: analysis.index.url(),
|
|
icon: BarChart3,
|
|
permission: 'analysis.view',
|
|
};
|
|
|
|
const masterItems: NavMenuItem[] = [
|
|
{ title: 'Kategori', href: categoriesIndex.url(), icon: Tags, permission: 'categories.view' },
|
|
{ title: 'Produk', href: productsIndex.url(), icon: Package, permission: 'products.view' },
|
|
{ title: 'Bahan Baku', href: rawMaterialsIndex.url(), icon: Boxes, permission: 'raw_materials.view' },
|
|
{ title: 'Supplier', href: suppliersIndex.url(), icon: Truck, permission: 'suppliers.view' },
|
|
{ title: 'Customer', href: customersIndex.url(), icon: Users, permission: 'customers.view' },
|
|
];
|
|
|
|
const kelolaItems: NavMenuItem[] = [
|
|
{ title: 'Belanja', href: purchasesIndex.url(), icon: ShoppingCart, permission: 'purchases.view' },
|
|
{ title: 'Cutting', href: cuttingsIndex.url(), icon: Scissors, permission: 'cuttings.view' },
|
|
{ title: 'Transaksi', href: transactionsIndex.url(), icon: ShoppingCart, permission: 'orders.view' },
|
|
{ title: 'Restock', href: restocksIndex.url(), icon: RefreshCw, permission: 'restocks.view' },
|
|
{ title: 'Stok Opname', href: stokOpnamesIndex.url(), icon: ClipboardCheck, permission: 'stok_opnames.view' },
|
|
{ title: 'Invoice', href: invoicesIndex.url(), icon: Receipt, permission: 'invoices.view' },
|
|
];
|
|
|
|
const keuanganItems: NavMenuItem[] = [
|
|
{ title: 'Kas Toko', href: cashAccountsIndex.url(), icon: Wallet, permission: 'cash.view' },
|
|
{ title: 'Pengeluaran', href: expensesIndex.url(), icon: ArrowUpFromLine, permission: 'expenses.view' },
|
|
{ title: 'Kasbon', href: employeeAdvancesIndex.url(), icon: HandCoins, permission: 'employee_advances.view' },
|
|
{ title: 'Gaji', href: payrollCurrent.url(), icon: DollarSign, permission: 'payroll.view' },
|
|
];
|
|
|
|
const hrItems: NavMenuItem[] = [
|
|
{ title: 'Pegawai', href: employeesIndex.url(), icon: UserCircle, permission: 'employees.view' },
|
|
{ title: 'Presensi', href: attendancesIndex.url(), icon: CalendarCheck, permission: 'attendances.view' },
|
|
{ title: 'Cuti', href: leaveRequestsIndex.url(), icon: CalendarDays, permission: 'leave_requests.view' },
|
|
];
|
|
|
|
const sistemItems: NavMenuItem[] = [
|
|
{ title: 'Pengaturan', href: '/admin/settings', icon: Settings, permission: ['settings.view_system', 'settings.view_homepage', 'settings.view_social_media', 'settings.view_hr'] },
|
|
{ title: 'Role & Permission', href: rolesIndex.url(), icon: Shield, permission: 'roles.view' },
|
|
{ title: 'Log Aktivitas', href: '/admin/form-histories', icon: Activity, permission: 'activity_logs.view' },
|
|
];
|
|
|
|
function MenuGroup({ label, items }: { label: string; items: NavMenuItem[] }) {
|
|
const { isCurrentUrl } = useCurrentUrl();
|
|
const { can, canAny } = useCan();
|
|
const { isMobile, setOpenMobile } = useSidebar();
|
|
|
|
const filtered = items.filter((item) => {
|
|
if (!item.permission) {
|
|
return true;
|
|
}
|
|
|
|
if (Array.isArray(item.permission)) {
|
|
return canAny(...item.permission);
|
|
}
|
|
|
|
return can(item.permission);
|
|
});
|
|
|
|
if (filtered.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<SidebarGroup>
|
|
<SidebarGroupLabel>{label}</SidebarGroupLabel>
|
|
<SidebarMenu>
|
|
{filtered.map((item) => (
|
|
<SidebarMenuItem key={item.title}>
|
|
<SidebarMenuButton
|
|
asChild
|
|
isActive={isCurrentUrl(item.href)}
|
|
tooltip={{ children: item.title }}
|
|
>
|
|
<Link href={item.href} prefetch onClick={() => isMobile && setOpenMobile(false)}>
|
|
<item.icon />
|
|
<span>{item.title}</span>
|
|
</Link>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
))}
|
|
</SidebarMenu>
|
|
</SidebarGroup>
|
|
);
|
|
}
|
|
|
|
export function AppSidebar() {
|
|
const { isCurrentUrl } = useCurrentUrl();
|
|
const { isMobile, setOpenMobile } = useSidebar();
|
|
const { can } = useCan();
|
|
|
|
React.useEffect(() => {
|
|
if (!isMobile) {
|
|
return;
|
|
}
|
|
|
|
const cleanup = router.on('finish', () => {
|
|
setOpenMobile(false);
|
|
});
|
|
|
|
return cleanup;
|
|
}, [isMobile, setOpenMobile]);
|
|
|
|
return (
|
|
<Sidebar collapsible="icon" variant="inset">
|
|
<SidebarHeader>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton size="lg" asChild>
|
|
<Link href={dashboard.url()} prefetch onClick={() => isMobile && setOpenMobile(false)}>
|
|
<AppLogo />
|
|
</Link>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarHeader>
|
|
|
|
<SidebarContent>
|
|
<SidebarGroup>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton
|
|
asChild
|
|
isActive={isCurrentUrl(dasborItem.href)}
|
|
tooltip={{ children: dasborItem.title }}
|
|
>
|
|
<Link href={dasborItem.href} prefetch onClick={() => isMobile && setOpenMobile(false)}>
|
|
<dasborItem.icon />
|
|
<span>{dasborItem.title}</span>
|
|
</Link>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarGroup>
|
|
|
|
<SidebarGroup>
|
|
<SidebarMenu>
|
|
{can(analisaItem.permission as string) && (
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton
|
|
asChild
|
|
isActive={isCurrentUrl(analisaItem.href)}
|
|
tooltip={{ children: analisaItem.title }}
|
|
>
|
|
<Link href={analisaItem.href} prefetch onClick={() => isMobile && setOpenMobile(false)}>
|
|
<analisaItem.icon />
|
|
<span>{analisaItem.title}</span>
|
|
</Link>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
)}
|
|
</SidebarMenu>
|
|
</SidebarGroup>
|
|
|
|
<MenuGroup label="Master" items={masterItems} />
|
|
<MenuGroup label="Kelola" items={kelolaItems} />
|
|
<MenuGroup label="Keuangan" items={keuanganItems} />
|
|
<MenuGroup label="HR" items={hrItems} />
|
|
<MenuGroup label="Sistem" items={sistemItems} />
|
|
</SidebarContent>
|
|
</Sidebar>
|
|
);
|
|
}
|