This commit is contained in:
2026-03-26 16:50:43 +03:00
parent 5dc54ffd00
commit 4b0aef983b
21 changed files with 1365 additions and 35 deletions
+90
View File
@@ -0,0 +1,90 @@
"use client"
import { ResourcePage } from "@/shared/data-view/resource-page"
import { ColumnHeader } from "@/shared/data-view/table-view"
import { PartForm } from "@/modules/parts/part-form"
import { Badge } from "@/shared/components/ui/badge"
import { PARTS_ROUTES } from "@repo/api"
import type { PartsClient } from "@repo/api"
export default function PartsPage() {
return (
<ResourcePage<PartsClient>
pageTitle="Parts"
title="Part"
routeKey={PARTS_ROUTES.INDEX}
getClient={(api) => api.parts}
columns={({ actionsColumn }) => [
{
accessorKey: "title",
header: ({ column }) => <ColumnHeader column={column} title="Title" />,
cell: ({ row }) => {
const r = row.original as any
return (
<div>
<span className="font-medium">{r.title || "—"}</span>
{r.sku && (
<span className="ml-2 text-xs text-muted-foreground">{r.sku}</span>
)}
</div>
)
},
},
{
accessorKey: "part_number",
header: ({ column }) => <ColumnHeader column={column} title="Part #" />,
cell: ({ row }) => (row.original as any).part_number || "—",
},
{
accessorKey: "manufactured_by",
header: ({ column }) => <ColumnHeader column={column} title="Manufacturer" />,
cell: ({ row }) => (row.original as any).manufactured_by || "—",
},
{
accessorKey: "selling_price",
header: ({ column }) => <ColumnHeader column={column} title="Sell Price" />,
cell: ({ row }) => {
const val = (row.original as any).selling_price
return val != null ? `$${Number(val).toFixed(2)}` : "—"
},
},
{
accessorKey: "purchase_price",
header: ({ column }) => <ColumnHeader column={column} title="Cost" />,
cell: ({ row }) => {
const val = (row.original as any).purchase_price
return val != null ? `$${Number(val).toFixed(2)}` : "—"
},
},
{
accessorKey: "is_active",
header: ({ column }) => <ColumnHeader column={column} title="Status" />,
cell: ({ row }) => {
const active = (row.original as any).is_active
return (
<Badge variant={active ? "default" : "secondary"}>
{active ? "Active" : "Inactive"}
</Badge>
)
},
},
{
accessorKey: "created_at",
header: ({ column }) => <ColumnHeader column={column} title="Created" />,
cell: ({ row }) => {
const val = (row.original as any).created_at
return val ? new Date(val).toLocaleDateString() : "—"
},
},
actionsColumn(),
]}
renderForm={({ resourceId, initialData, onSuccess }) => (
<PartForm
resourceId={resourceId}
initialData={initialData}
onSuccess={onSuccess}
/>
)}
/>
)
}
@@ -0,0 +1,72 @@
"use client"
import { ResourcePage } from "@/shared/data-view/resource-page"
import { ColumnHeader } from "@/shared/data-view/table-view"
import { ServiceGroupForm } from "@/modules/service-groups/service-group-form"
import { Badge } from "@/shared/components/ui/badge"
import { SERVICE_GROUP_ROUTES } from "@repo/api"
import type { ServiceGroupsClient } from "@repo/api"
export default function ServiceGroupPage() {
return (
<ResourcePage<ServiceGroupsClient>
pageTitle="Service Groups"
title="Service Group"
routeKey={SERVICE_GROUP_ROUTES.INDEX}
getClient={(api) => api.serviceGroups}
columns={({ actionsColumn }) => [
{
accessorKey: "name",
header: ({ column }) => <ColumnHeader column={column} title="Name" />,
cell: ({ row }) => {
const r = row.original as any
return (
<div>
<span className="font-medium">{r.service_name || r.name || "—"}</span>
{r.code && (
<span className="ml-2 text-xs text-muted-foreground">{r.code}</span>
)}
</div>
)
},
},
{
accessorKey: "selling_price",
header: ({ column }) => <ColumnHeader column={column} title="Price" />,
cell: ({ row }) => {
const val = (row.original as any).selling_price
return val != null ? `$${Number(val).toFixed(2)}` : "—"
},
},
{
accessorKey: "is_active",
header: ({ column }) => <ColumnHeader column={column} title="Status" />,
cell: ({ row }) => {
const active = (row.original as any).is_active
return (
<Badge variant={active ? "default" : "secondary"}>
{active ? "Active" : "Inactive"}
</Badge>
)
},
},
{
accessorKey: "created_at",
header: ({ column }) => <ColumnHeader column={column} title="Created" />,
cell: ({ row }) => {
const val = (row.original as any).created_at
return val ? new Date(val).toLocaleDateString() : "—"
},
},
actionsColumn(),
]}
renderForm={({ resourceId, initialData, onSuccess }) => (
<ServiceGroupForm
resourceId={resourceId}
initialData={initialData}
onSuccess={onSuccess}
/>
)}
/>
)
}
@@ -0,0 +1,69 @@
"use client"
import { ResourcePage } from "@/shared/data-view/resource-page"
import { ColumnHeader } from "@/shared/data-view/table-view"
import { ServiceForm } from "@/modules/services/service-form"
import { SERVICE_ROUTES } from "@repo/api"
import type { ServicesClient } from "@repo/api"
export default function ServicesPage() {
return (
<ResourcePage<ServicesClient>
pageTitle="Services"
title="Service"
routeKey={SERVICE_ROUTES.INDEX}
getClient={(api) => api.services}
columns={({ actionsColumn }) => [
{
accessorKey: "labor_name",
header: ({ column }) => <ColumnHeader column={column} title="Name" />,
cell: ({ row }) => {
const r = row.original as any
return (
<div>
<span className="font-medium">{r.labor_name || r.name || "—"}</span>
{r.service_code && (
<span className="ml-2 text-xs text-muted-foreground">{r.service_code}</span>
)}
</div>
)
},
},
{
accessorKey: "description",
header: ({ column }) => <ColumnHeader column={column} title="Description" />,
cell: ({ row }) => {
const val = (row.original as any).description
return val
? <span className="max-w-[200px] truncate block">{val}</span>
: "—"
},
},
{
accessorKey: "selling_price",
header: ({ column }) => <ColumnHeader column={column} title="Price" />,
cell: ({ row }) => {
const val = (row.original as any).selling_price
return val != null ? `$${Number(val).toFixed(2)}` : "—"
},
},
{
accessorKey: "created_at",
header: ({ column }) => <ColumnHeader column={column} title="Created" />,
cell: ({ row }) => {
const val = (row.original as any).created_at
return val ? new Date(val).toLocaleDateString() : "—"
},
},
actionsColumn(),
]}
renderForm={({ resourceId, initialData, onSuccess }) => (
<ServiceForm
resourceId={resourceId}
initialData={initialData}
onSuccess={onSuccess}
/>
)}
/>
)
}
+22 -22
View File
@@ -61,7 +61,7 @@ const navGroups: NavGroup[] = [
},
{
title: "Customer & Vehicles",
href: "/customer_vehicles",
href: "/customer-vehicles",
icon: <UsersIcon />,
},
{
@@ -79,7 +79,7 @@ const navGroups: NavGroup[] = [
href: "/calendars",
icon: <CalendarIcon />,
items: [
{ title: "Work Schedule", href: "/calendar/work_schedule/list", icon: <Clock3Icon /> },
{ title: "Work Schedule", href: "/calendar/work-schedule/list", icon: <Clock3Icon /> },
{ title: "Appointments", href: "/calendar/appointment/list", icon: <CalendarCheck2Icon /> },
],
},
@@ -94,8 +94,8 @@ const navGroups: NavGroup[] = [
{ title: "Estimates", href: "/sales/estimate", icon: <ReceiptTextIcon /> },
{ title: "Job Cards", href: "/sales/workorder/list", icon: <ClipboardListIcon /> },
{ title: "Invoices", href: "/sales/invoice", icon: <ReceiptIcon /> },
{ title: "Payments Received", href: "/sales/payment_received", icon: <HandCoinsIcon /> },
{ title: "Credit Notes", href: "/sales/credit_notes", icon: <ReceiptTextIcon /> },
{ title: "Payments Received", href: "/sales/payment-received", icon: <HandCoinsIcon /> },
{ title: "Credit Notes", href: "/sales/credit-notes", icon: <ReceiptTextIcon /> },
],
},
{
@@ -105,10 +105,10 @@ const navGroups: NavGroup[] = [
items: [
{ title: "Vendors", href: "/purchase/vendor", icon: <StoreIcon /> },
{ title: "Expenses", href: "/purchase/expense", icon: <WalletIcon /> },
{ title: "Purchase Orders", href: "/purchase/purchase_order", icon: <ShoppingBasketIcon /> },
{ title: "Purchase Orders", href: "/purchase/purchase-order", icon: <ShoppingBasketIcon /> },
{ title: "Bills", href: "/purchase/bill", icon: <ReceiptIcon /> },
{ title: "Payments Made", href: "/purchase/payments_made", icon: <BanknoteArrowDownIcon /> },
{ title: "Vendor Credits", href: "/purchase/vendor_credit", icon: <ReceiptTextIcon /> },
{ title: "Payments Made", href: "/purchase/payments-made", icon: <BanknoteArrowDownIcon /> },
{ title: "Vendor Credits", href: "/purchase/vendor-credit", icon: <ReceiptTextIcon /> },
],
},
{
@@ -117,7 +117,7 @@ const navGroups: NavGroup[] = [
icon: <BriefcaseBusinessIcon />,
items: [
{ title: "Leads", href: "/crm/leads/list", icon: <GemIcon /> },
{ title: "Calls", href: "/crm/calls_follow_up/list", icon: <PhoneCallIcon /> },
{ title: "Calls", href: "/crm/calls-follow-up/list", icon: <PhoneCallIcon /> },
{ title: "Tasks", href: "/crm/tasks/list", icon: <ListTodoIcon /> },
],
},
@@ -126,9 +126,9 @@ const navGroups: NavGroup[] = [
href: "/marketing",
icon: <MegaphoneIcon />,
items: [
{ title: "Service Reminders", href: "/marketing/service_reminder/list", icon: <AlarmClockIcon /> },
{ title: "Rating & Reviews", href: "/marketing/rating_review", icon: <StarIcon /> },
{ title: "Google Business Reviews", href: "/marketing/google_rating_review", icon: <AwardIcon /> },
{ title: "Service Reminders", href: "/marketing/service-reminder/list", icon: <AlarmClockIcon /> },
{ title: "Rating & Reviews", href: "/marketing/rating-review", icon: <StarIcon /> },
{ title: "Google Business Reviews", href: "/marketing/google-rating-review", icon: <AwardIcon /> },
],
},
{
@@ -136,8 +136,8 @@ const navGroups: NavGroup[] = [
href: "/accountants",
icon: <BookIcon />,
items: [
{ title: "Manual Journals", href: "/accountants/manual_journal", icon: <BookIcon /> },
{ title: "Chart of Accounts", href: "/accountants/chart_of_account", icon: <GitBranchIcon /> },
{ title: "Manual Journals", href: "/accountants/manual-journal", icon: <BookIcon /> },
{ title: "Chart of Accounts", href: "/accountants/chart-of-account", icon: <GitBranchIcon /> },
],
},
{
@@ -146,12 +146,12 @@ const navGroups: NavGroup[] = [
icon: <UserCogIcon />,
items: [
{ title: "Employees", href: "/productivity/employees", icon: <UsersIcon /> },
{ title: "Time Clocks", href: "/productivity/time_clocks", icon: <TimerIcon /> },
{ title: "Time Clocks", href: "/productivity/time-clocks", icon: <TimerIcon /> },
{ title: "Time Sheets", href: "/productivity/timesheet", icon: <ClockIcon /> },
{ title: "Payroll", href: "/productivity/payroll", icon: <WalletIcon /> },
{ title: "Payments Made", href: "/productivity/employee_payments_made", icon: <HandCoinsIcon /> },
{ title: "Shop Calendars", href: "/productivity/shop_calendars", icon: <CalendarDaysIcon /> },
{ title: "Shop Timing", href: "/productivity/shop_timings", icon: <Clock3Icon /> },
{ title: "Payments Made", href: "/productivity/employee-payments-made", icon: <HandCoinsIcon /> },
{ title: "Shop Calendars", href: "/productivity/shop-calendars", icon: <CalendarDaysIcon /> },
{ title: "Shop Timing", href: "/productivity/shop-timings", icon: <Clock3Icon /> },
{ title: "Holidays", href: "/productivity/holidays", icon: <CalendarIcon /> },
],
},
@@ -162,8 +162,8 @@ const navGroups: NavGroup[] = [
items: [
{ title: "Services", href: "/items/services", icon: <WrenchIcon /> },
{ title: "Parts", href: "/items/parts", icon: <WrenchIcon /> },
{ title: "Expense Item", href: "/items/expense_item", icon: <WalletIcon /> },
{ title: "Service Group", href: "/items/service_group", icon: <PackageIcon /> },
{ title: "Expense Item", href: "/items/expense-item", icon: <WalletIcon /> },
{ title: "Service Group", href: "/items/service-group", icon: <PackageIcon /> },
{ title: "Inspections", href: "/items/inspection", icon: <ClipboardCheckIcon /> },
{ title: "Inventory Adjustments", href: "/items/adjustment", icon: <ListIcon /> },
],
@@ -174,12 +174,12 @@ const navGroups: NavGroup[] = [
icon: <SettingsIcon />,
items: [
{ title: "Company", href: "/setting/company", icon: <Building2Icon /> },
{ title: "Shop Types", href: "/setting/shop_type", icon: <CarIcon /> },
{ title: "Tax & Rates", href: "/setting/tax_rates", icon: <ReceiptTextIcon /> },
{ title: "Shop Types", href: "/setting/shop-type", icon: <CarIcon /> },
{ title: "Tax & Rates", href: "/setting/tax-rates", icon: <ReceiptTextIcon /> },
{ title: "Configurations", href: "/setting/configurations/preferences/sales", icon: <SettingsIcon /> },
{ title: "Templates", href: "/setting/templates", icon: <ClipboardListIcon /> },
{ title: "Integrations", href: "/setting/integrations/providers", icon: <PlugZapIcon /> },
{ title: "Master", href: "/setting/master/body_type", icon: <ListIcon /> },
{ title: "Master", href: "/setting/master/body-type", icon: <ListIcon /> },
],
},
],
+42 -4
View File
@@ -20,11 +20,18 @@ export default function VehiclesPage() {
header: ({ column }) => <ColumnHeader column={column} title="Vehicle" />,
cell: ({ row }) => {
const r = row.original as any
const display = r.name || `${r.make ?? ""} ${r.model ?? ""}`.trim() || "—"
const make = r.make ?? ""
const model = r.model ?? ""
const display = r.name || `${make} ${model}`.trim() || "—"
return (
<div className="flex items-center gap-2">
<CarIcon className="h-4 w-4 text-muted-foreground" />
<span>{display}</span>
<div>
<span className="font-medium">{display}</span>
{r.sub_model && (
<span className="ml-1 text-xs text-muted-foreground">{r.sub_model}</span>
)}
</div>
</div>
)
},
@@ -37,12 +44,43 @@ export default function VehiclesPage() {
{
accessorKey: "license_plate",
header: ({ column }) => <ColumnHeader column={column} title="License Plate" />,
cell: ({ row }) => (row.original as any).license_plate ?? "—",
cell: ({ row }) => {
const val = (row.original as any).license_plate
return val
? <span className="font-mono text-xs">{val}</span>
: "—"
},
},
{
accessorKey: "vin_number",
header: ({ column }) => <ColumnHeader column={column} title="VIN" />,
cell: ({ row }) => {
const val = (row.original as any).vin_number
return val
? <span className="max-w-30 truncate block font-mono text-xs">{val}</span>
: "—"
},
},
{
accessorKey: "engine_size",
header: ({ column }) => <ColumnHeader column={column} title="Engine" />,
cell: ({ row }) => (row.original as any).engine_size ?? "—",
},
{
accessorKey: "mileage",
header: ({ column }) => <ColumnHeader column={column} title="Mileage" />,
cell: ({ row }) => (row.original as any).mileage ?? "—",
cell: ({ row }) => {
const val = (row.original as any).mileage
return val != null ? `${Number(val).toLocaleString()} mi` : "—"
},
},
{
accessorKey: "created_at",
header: ({ column }) => <ColumnHeader column={column} title="Created" />,
cell: ({ row }) => {
const val = (row.original as any).created_at
return val ? new Date(val).toLocaleDateString() : "—"
},
},
actionsColumn(),
]}