feat(invoice form): added new invoice form
This commit is contained in:
@@ -12,10 +12,10 @@ post {
|
|||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"leaseNumber":"500-50489192",
|
"leaseNumber":"40829107-1",
|
||||||
"startDate": "11/08/2023",
|
"startDate": "11/08/2023",
|
||||||
"endDate": "11/12/2025",
|
"endDate": "11/12/2025",
|
||||||
"companyId": "b34c6684-ec35-4364-acef-0c1570faf123"
|
"companyId": "59c4eaa3-55db-4348-a033-f2fcd91a91d1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,26 +53,28 @@ router.post("/", async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this will be the total invoice amount minus each forklift this way we can keep the total amount in here plus forklifts seperated
|
// this will be the total invoice amount minus each forklift this way we can keep the total amount in here plus forklifts seperated
|
||||||
const totalAmount = (
|
// const totalAmount = (
|
||||||
validatedForklifts.reduce((sum, f) => sum + Number(f.amount || 0), 0) -
|
// validatedForklifts.reduce((sum, f) => sum + Number(f.amount || 0), 0) -
|
||||||
req.body.totalInvoice
|
// req.body.totalInvoice
|
||||||
).toString();
|
// ).toString();
|
||||||
|
|
||||||
const { data, error } = await tryCatch(
|
const { data, error } = await tryCatch(
|
||||||
db
|
db
|
||||||
.insert(leaseInvoices)
|
.insert(leaseInvoices)
|
||||||
.values({
|
.values({
|
||||||
...invoiceData,
|
...invoiceData,
|
||||||
|
add_date: sql`NOW()`,
|
||||||
|
totalAmount: req.body.totalAmount,
|
||||||
uploadedBy: req.user!.username || "lst_user",
|
uploadedBy: req.user!.username || "lst_user",
|
||||||
})
|
})
|
||||||
.onConflictDoUpdate({
|
// .onConflictDoUpdate({
|
||||||
target: leaseInvoices.invoiceNumber,
|
// target: leaseInvoices.invoiceNumber,
|
||||||
set: {
|
// set: {
|
||||||
totalAmount,
|
// totalAmount,
|
||||||
invoiceDate: invoiceData.invoiceDate,
|
// invoiceDate: invoiceData.invoiceDate,
|
||||||
uploadedBy: req.user!.username || "lst_user",
|
// uploadedBy: req.user!.username || "lst_user",
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
.returning(),
|
.returning(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -80,18 +82,21 @@ router.post("/", async (req: Request, res: Response) => {
|
|||||||
const err: DrizzleError = error;
|
const err: DrizzleError = error;
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
message: `Error adding lease`,
|
message: `Error adding lease`,
|
||||||
error: err.cause,
|
// @ts-ignore
|
||||||
|
error: err.cause.detail,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceId = data[0]?.id;
|
const invoiceId = data[0]?.id;
|
||||||
|
console.log(validatedForklifts);
|
||||||
const forkliftInvoices = validatedForklifts.map((f) => ({
|
const forkliftInvoices = validatedForklifts.map((f) => {
|
||||||
|
return {
|
||||||
invoiceId,
|
invoiceId,
|
||||||
forkliftId: f.forklift_Id,
|
forkliftId: f.forklift_id,
|
||||||
amount: f.amount,
|
amount: f.amount,
|
||||||
}));
|
};
|
||||||
console.log(forkliftInvoices);
|
});
|
||||||
|
|
||||||
if (validatedForklifts.length > 0) {
|
if (validatedForklifts.length > 0) {
|
||||||
await db.insert(leaseInvoiceForklifts).values(forkliftInvoices);
|
await db.insert(leaseInvoiceForklifts).values(forkliftInvoices);
|
||||||
// .onConflictDoUpdate({
|
// .onConflictDoUpdate({
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
|
|||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get("/", async (req: Request, res: Response) => {
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
const lease = req.query.lease;
|
|
||||||
|
|
||||||
const conditions = [];
|
const conditions = [];
|
||||||
|
|
||||||
if (lease !== undefined) {
|
if (req.query.lease !== undefined) {
|
||||||
conditions.push(eq(leases.leaseNumber, `${lease}`));
|
conditions.push(eq(leases.leaseNumber, `${req.query.lease}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.query.companyId !== undefined) {
|
||||||
|
conditions.push(eq(leases.companyId, `${req.query.companyId}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
//conditions.push(eq(forkliftCompanies.active, true));
|
//conditions.push(eq(forkliftCompanies.active, true));
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function ForkliftSideBar() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Invoices",
|
title: "Invoices",
|
||||||
url: "/lst/app/admin/settings",
|
url: "/lst/app/forklifts/invoices",
|
||||||
icon: ReceiptText,
|
icon: ReceiptText,
|
||||||
role: ["systemAdmin", "admin", "manager"],
|
role: ["systemAdmin", "admin", "manager"],
|
||||||
module: "forklifts",
|
module: "forklifts",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Link, useRouterState } from "@tanstack/react-router";
|
import { Link, useRouterState } from "@tanstack/react-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import NewCompanyForm from "@/routes/_app/_forklifts/-components/NewCompany";
|
import NewCompanyForm from "@/routes/_app/_forklifts/-components/NewCompany";
|
||||||
|
import NewInvoice from "@/routes/_app/_forklifts/-components/NewInvoice";
|
||||||
import NewLeaseForm from "@/routes/_app/_forklifts/-components/NewLease";
|
import NewLeaseForm from "@/routes/_app/_forklifts/-components/NewLease";
|
||||||
import { useAuth, useLogout } from "../../lib/authClient";
|
import { useAuth, useLogout } from "../../lib/authClient";
|
||||||
import { ModeToggle } from "../mode-toggle";
|
import { ModeToggle } from "../mode-toggle";
|
||||||
@@ -23,6 +24,7 @@ export default function Nav() {
|
|||||||
const currentPath = router.location.href;
|
const currentPath = router.location.href;
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
const [openLeaseDialog, setOpenLeaseDialog] = useState(false);
|
const [openLeaseDialog, setOpenLeaseDialog] = useState(false);
|
||||||
|
const [openInvoiceDialog, setOpenInvoiceDialog] = useState(false);
|
||||||
return (
|
return (
|
||||||
<nav className="flex justify-end w-full shadow ">
|
<nav className="flex justify-end w-full shadow ">
|
||||||
<div className="m-2 flex flex-row gap-1">
|
<div className="m-2 flex flex-row gap-1">
|
||||||
@@ -65,6 +67,14 @@ export default function Nav() {
|
|||||||
>
|
>
|
||||||
New Lease
|
New Lease
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={() => {
|
||||||
|
// just open the dialog when clicked
|
||||||
|
setOpenInvoiceDialog(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
New Invoice
|
||||||
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
{/* Company */}
|
{/* Company */}
|
||||||
@@ -85,6 +95,16 @@ export default function Nav() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
|
{openInvoiceDialog && (
|
||||||
|
<Dialog
|
||||||
|
open={openInvoiceDialog}
|
||||||
|
onOpenChange={setOpenInvoiceDialog}
|
||||||
|
>
|
||||||
|
<DialogContent className="sm:max-w-fit">
|
||||||
|
<NewInvoice setOpenInvoiceDialog={setOpenInvoiceDialog} />
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { FieldErrors } from "./FieldErrors";
|
|||||||
|
|
||||||
type DateFieldProps = {
|
type DateFieldProps = {
|
||||||
label: string;
|
label: string;
|
||||||
|
required: boolean;
|
||||||
};
|
};
|
||||||
export const DateField = ({ label }: DateFieldProps) => {
|
export const DateField = ({ label }: DateFieldProps) => {
|
||||||
const field = useFieldContext<any>();
|
const field = useFieldContext<any>();
|
||||||
@@ -37,6 +38,7 @@ export const DateField = ({ label }: DateFieldProps) => {
|
|||||||
<Calendar
|
<Calendar
|
||||||
mode="single"
|
mode="single"
|
||||||
selected={date}
|
selected={date}
|
||||||
|
//required={required}
|
||||||
captionLayout="dropdown"
|
captionLayout="dropdown"
|
||||||
startMonth={new Date(new Date().getFullYear() - 10, 0)}
|
startMonth={new Date(new Date().getFullYear() - 10, 0)}
|
||||||
endMonth={new Date(new Date().getFullYear() + 20, 0)}
|
endMonth={new Date(new Date().getFullYear() + 20, 0)}
|
||||||
|
|||||||
17
frontend/src/lib/querys/forklifts/getInvoices.ts
Normal file
17
frontend/src/lib/querys/forklifts/getInvoices.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { queryOptions } from "@tanstack/react-query";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export function getInvoices() {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: ["getInvoices"],
|
||||||
|
queryFn: () => fetch(),
|
||||||
|
staleTime: 5000,
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetch = async () => {
|
||||||
|
const { data } = await axios.get("/lst/api/forklifts/invoices");
|
||||||
|
|
||||||
|
return data.data;
|
||||||
|
};
|
||||||
@@ -29,6 +29,7 @@ import { Route as MobileMobileLayoutMRelocateRouteImport } from './routes/_mobil
|
|||||||
import { Route as MobileMobileLayoutMDeliveryRouteImport } from './routes/_mobile/_mobileLayout/m/delivery'
|
import { Route as MobileMobileLayoutMDeliveryRouteImport } from './routes/_mobile/_mobileLayout/m/delivery'
|
||||||
import { Route as MobileMobileLayoutMCyclecountsRouteImport } from './routes/_mobile/_mobileLayout/m/cyclecounts'
|
import { Route as MobileMobileLayoutMCyclecountsRouteImport } from './routes/_mobile/_mobileLayout/m/cyclecounts'
|
||||||
import { Route as AppForkliftsForkliftsLeasesRouteImport } from './routes/_app/_forklifts/forklifts/leases'
|
import { Route as AppForkliftsForkliftsLeasesRouteImport } from './routes/_app/_forklifts/forklifts/leases'
|
||||||
|
import { Route as AppForkliftsForkliftsInvoicesRouteImport } from './routes/_app/_forklifts/forklifts/invoices'
|
||||||
import { Route as AppForkliftsForkliftsCompaniesRouteImport } from './routes/_app/_forklifts/forklifts/companies'
|
import { Route as AppForkliftsForkliftsCompaniesRouteImport } from './routes/_app/_forklifts/forklifts/companies'
|
||||||
import { Route as AppAdminLayoutAdminServersRouteImport } from './routes/_app/_adminLayout/admin/servers'
|
import { Route as AppAdminLayoutAdminServersRouteImport } from './routes/_app/_adminLayout/admin/servers'
|
||||||
import { Route as ApplogisticsLogisticsDeliveryScheduleRouteImport } from './routes/_app/(logistics)/logistics/deliverySchedule'
|
import { Route as ApplogisticsLogisticsDeliveryScheduleRouteImport } from './routes/_app/(logistics)/logistics/deliverySchedule'
|
||||||
@@ -152,6 +153,12 @@ const AppForkliftsForkliftsLeasesRoute =
|
|||||||
path: '/forklifts/leases',
|
path: '/forklifts/leases',
|
||||||
getParentRoute: () => AppForkliftsRouteRoute,
|
getParentRoute: () => AppForkliftsRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AppForkliftsForkliftsInvoicesRoute =
|
||||||
|
AppForkliftsForkliftsInvoicesRouteImport.update({
|
||||||
|
id: '/forklifts/invoices',
|
||||||
|
path: '/forklifts/invoices',
|
||||||
|
getParentRoute: () => AppForkliftsRouteRoute,
|
||||||
|
} as any)
|
||||||
const AppForkliftsForkliftsCompaniesRoute =
|
const AppForkliftsForkliftsCompaniesRoute =
|
||||||
AppForkliftsForkliftsCompaniesRouteImport.update({
|
AppForkliftsForkliftsCompaniesRouteImport.update({
|
||||||
id: '/forklifts/companies',
|
id: '/forklifts/companies',
|
||||||
@@ -287,6 +294,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
|
'/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
|
||||||
'/admin/servers': typeof AppAdminLayoutAdminServersRoute
|
'/admin/servers': typeof AppAdminLayoutAdminServersRoute
|
||||||
'/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
|
'/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
|
||||||
|
'/forklifts/invoices': typeof AppForkliftsForkliftsInvoicesRoute
|
||||||
'/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
|
'/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
|
||||||
'/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
|
'/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
|
||||||
'/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
|
'/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
|
||||||
@@ -322,6 +330,7 @@ export interface FileRoutesByTo {
|
|||||||
'/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
|
'/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
|
||||||
'/admin/servers': typeof AppAdminLayoutAdminServersRoute
|
'/admin/servers': typeof AppAdminLayoutAdminServersRoute
|
||||||
'/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
|
'/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
|
||||||
|
'/forklifts/invoices': typeof AppForkliftsForkliftsInvoicesRoute
|
||||||
'/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
|
'/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
|
||||||
'/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
|
'/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
|
||||||
'/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
|
'/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
|
||||||
@@ -365,6 +374,7 @@ export interface FileRoutesById {
|
|||||||
'/_app/(logistics)/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
|
'/_app/(logistics)/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
|
||||||
'/_app/_adminLayout/admin/servers': typeof AppAdminLayoutAdminServersRoute
|
'/_app/_adminLayout/admin/servers': typeof AppAdminLayoutAdminServersRoute
|
||||||
'/_app/_forklifts/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
|
'/_app/_forklifts/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
|
||||||
|
'/_app/_forklifts/forklifts/invoices': typeof AppForkliftsForkliftsInvoicesRoute
|
||||||
'/_app/_forklifts/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
|
'/_app/_forklifts/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
|
||||||
'/_mobile/_mobileLayout/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
|
'/_mobile/_mobileLayout/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
|
||||||
'/_mobile/_mobileLayout/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
|
'/_mobile/_mobileLayout/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
|
||||||
@@ -403,6 +413,7 @@ export interface FileRouteTypes {
|
|||||||
| '/logistics/deliverySchedule'
|
| '/logistics/deliverySchedule'
|
||||||
| '/admin/servers'
|
| '/admin/servers'
|
||||||
| '/forklifts/companies'
|
| '/forklifts/companies'
|
||||||
|
| '/forklifts/invoices'
|
||||||
| '/forklifts/leases'
|
| '/forklifts/leases'
|
||||||
| '/m/cyclecounts'
|
| '/m/cyclecounts'
|
||||||
| '/m/delivery'
|
| '/m/delivery'
|
||||||
@@ -438,6 +449,7 @@ export interface FileRouteTypes {
|
|||||||
| '/logistics/deliverySchedule'
|
| '/logistics/deliverySchedule'
|
||||||
| '/admin/servers'
|
| '/admin/servers'
|
||||||
| '/forklifts/companies'
|
| '/forklifts/companies'
|
||||||
|
| '/forklifts/invoices'
|
||||||
| '/forklifts/leases'
|
| '/forklifts/leases'
|
||||||
| '/m/cyclecounts'
|
| '/m/cyclecounts'
|
||||||
| '/m/delivery'
|
| '/m/delivery'
|
||||||
@@ -480,6 +492,7 @@ export interface FileRouteTypes {
|
|||||||
| '/_app/(logistics)/logistics/deliverySchedule'
|
| '/_app/(logistics)/logistics/deliverySchedule'
|
||||||
| '/_app/_adminLayout/admin/servers'
|
| '/_app/_adminLayout/admin/servers'
|
||||||
| '/_app/_forklifts/forklifts/companies'
|
| '/_app/_forklifts/forklifts/companies'
|
||||||
|
| '/_app/_forklifts/forklifts/invoices'
|
||||||
| '/_app/_forklifts/forklifts/leases'
|
| '/_app/_forklifts/forklifts/leases'
|
||||||
| '/_mobile/_mobileLayout/m/cyclecounts'
|
| '/_mobile/_mobileLayout/m/cyclecounts'
|
||||||
| '/_mobile/_mobileLayout/m/delivery'
|
| '/_mobile/_mobileLayout/m/delivery'
|
||||||
@@ -645,6 +658,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof AppForkliftsForkliftsLeasesRouteImport
|
preLoaderRoute: typeof AppForkliftsForkliftsLeasesRouteImport
|
||||||
parentRoute: typeof AppForkliftsRouteRoute
|
parentRoute: typeof AppForkliftsRouteRoute
|
||||||
}
|
}
|
||||||
|
'/_app/_forklifts/forklifts/invoices': {
|
||||||
|
id: '/_app/_forklifts/forklifts/invoices'
|
||||||
|
path: '/forklifts/invoices'
|
||||||
|
fullPath: '/forklifts/invoices'
|
||||||
|
preLoaderRoute: typeof AppForkliftsForkliftsInvoicesRouteImport
|
||||||
|
parentRoute: typeof AppForkliftsRouteRoute
|
||||||
|
}
|
||||||
'/_app/_forklifts/forklifts/companies': {
|
'/_app/_forklifts/forklifts/companies': {
|
||||||
id: '/_app/_forklifts/forklifts/companies'
|
id: '/_app/_forklifts/forklifts/companies'
|
||||||
path: '/forklifts/companies'
|
path: '/forklifts/companies'
|
||||||
@@ -860,12 +880,14 @@ const AppAdminLayoutRouteRouteWithChildren =
|
|||||||
|
|
||||||
interface AppForkliftsRouteRouteChildren {
|
interface AppForkliftsRouteRouteChildren {
|
||||||
AppForkliftsForkliftsCompaniesRoute: typeof AppForkliftsForkliftsCompaniesRoute
|
AppForkliftsForkliftsCompaniesRoute: typeof AppForkliftsForkliftsCompaniesRoute
|
||||||
|
AppForkliftsForkliftsInvoicesRoute: typeof AppForkliftsForkliftsInvoicesRoute
|
||||||
AppForkliftsForkliftsLeasesRoute: typeof AppForkliftsForkliftsLeasesRoute
|
AppForkliftsForkliftsLeasesRoute: typeof AppForkliftsForkliftsLeasesRoute
|
||||||
AppForkliftsForkliftsIndexRoute: typeof AppForkliftsForkliftsIndexRoute
|
AppForkliftsForkliftsIndexRoute: typeof AppForkliftsForkliftsIndexRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppForkliftsRouteRouteChildren: AppForkliftsRouteRouteChildren = {
|
const AppForkliftsRouteRouteChildren: AppForkliftsRouteRouteChildren = {
|
||||||
AppForkliftsForkliftsCompaniesRoute: AppForkliftsForkliftsCompaniesRoute,
|
AppForkliftsForkliftsCompaniesRoute: AppForkliftsForkliftsCompaniesRoute,
|
||||||
|
AppForkliftsForkliftsInvoicesRoute: AppForkliftsForkliftsInvoicesRoute,
|
||||||
AppForkliftsForkliftsLeasesRoute: AppForkliftsForkliftsLeasesRoute,
|
AppForkliftsForkliftsLeasesRoute: AppForkliftsForkliftsLeasesRoute,
|
||||||
AppForkliftsForkliftsIndexRoute: AppForkliftsForkliftsIndexRoute,
|
AppForkliftsForkliftsIndexRoute: AppForkliftsForkliftsIndexRoute,
|
||||||
}
|
}
|
||||||
|
|||||||
264
frontend/src/routes/_app/_forklifts/-components/NewInvoice.tsx
Normal file
264
frontend/src/routes/_app/_forklifts/-components/NewInvoice.tsx
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import axios from "axios";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DialogClose,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { useAppForm } from "@/lib/formStuff";
|
||||||
|
import { getCompanies } from "@/lib/querys/forklifts/getCompanies";
|
||||||
|
import { getInvoices } from "@/lib/querys/forklifts/getInvoices";
|
||||||
|
|
||||||
|
type CompanyData = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NewInvoice({
|
||||||
|
setOpenInvoiceDialog,
|
||||||
|
}: {
|
||||||
|
setOpenInvoiceDialog: any;
|
||||||
|
}) {
|
||||||
|
const { refetch } = useQuery(getInvoices());
|
||||||
|
const form = useAppForm({
|
||||||
|
defaultValues: {
|
||||||
|
companyName: "",
|
||||||
|
leaseId: "",
|
||||||
|
invoiceNumber: "",
|
||||||
|
invoiceDate: "",
|
||||||
|
totalAmount: "",
|
||||||
|
forklifts: [{ forklift_id: "", serialNumber: "", amount: "" }],
|
||||||
|
},
|
||||||
|
onSubmit: async ({ value }) => {
|
||||||
|
const updatedForklifts = value.forklifts.map(
|
||||||
|
({ serialNumber, ...rest }) => rest,
|
||||||
|
);
|
||||||
|
const postData = {
|
||||||
|
leaseId: value.leaseId,
|
||||||
|
invoiceNumber: value.invoiceNumber,
|
||||||
|
invoiceDate: format(value.invoiceDate, "MM/dd/yyyy"),
|
||||||
|
totalAmount: value.totalAmount,
|
||||||
|
forklifts: updatedForklifts,
|
||||||
|
};
|
||||||
|
console.log(postData);
|
||||||
|
try {
|
||||||
|
await axios.post("/lst/api/forklifts/invoices", postData);
|
||||||
|
form.reset();
|
||||||
|
setOpenInvoiceDialog(false);
|
||||||
|
refetch();
|
||||||
|
toast.success(`${value.invoiceNumber} was just created `);
|
||||||
|
} catch (error) {
|
||||||
|
// @ts-ignore
|
||||||
|
console.log(error);
|
||||||
|
// @ts-ignore
|
||||||
|
if (!error.response.data.success) {
|
||||||
|
// @ts-ignore
|
||||||
|
toast.error(<span>{error?.response?.data.error}</span>);
|
||||||
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
|
toast.error(error?.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: c, isLoading: ce } = useQuery(getCompanies());
|
||||||
|
|
||||||
|
let companyName = form.getFieldValue("companyName");
|
||||||
|
|
||||||
|
const { data: l = [], refetch: lf } = useQuery({
|
||||||
|
queryKey: ["lease", companyName],
|
||||||
|
queryFn: async () => {
|
||||||
|
//if (!companyName) return [];
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`/lst/api/forklifts/leases?companyId=${companyName}`,
|
||||||
|
);
|
||||||
|
return data.data;
|
||||||
|
},
|
||||||
|
enabled: !!companyName, // only run if nameId has value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ce) return <div>Loading Companies</div>;
|
||||||
|
|
||||||
|
// remap the companies to fit out select field
|
||||||
|
const companyMap = c.map((i: CompanyData) => {
|
||||||
|
return { value: i.id, label: i.name };
|
||||||
|
});
|
||||||
|
|
||||||
|
const leaseMap = l.map((i: any) => {
|
||||||
|
return { value: i.id, label: i.leaseNumber };
|
||||||
|
});
|
||||||
|
|
||||||
|
const onValueChange = (value: string) => {
|
||||||
|
companyName = value;
|
||||||
|
lf();
|
||||||
|
form.setFieldValue("leaseId", "");
|
||||||
|
};
|
||||||
|
|
||||||
|
let forkliftArray = [];
|
||||||
|
const onLeaseChange = (value: string) => {
|
||||||
|
const selectedLease = l.find((lease: any) => lease.id === value);
|
||||||
|
|
||||||
|
forkliftArray =
|
||||||
|
selectedLease?.forklifts.length > 0
|
||||||
|
? selectedLease.forklifts.map((f: any) => ({
|
||||||
|
forklift_id: f.forklift_id,
|
||||||
|
amount: 0,
|
||||||
|
serialNumber: f.serialNumber,
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
// {
|
||||||
|
// forklift_id: "missing",
|
||||||
|
// amount: 0,
|
||||||
|
// serialNumber: "Missing forklift",
|
||||||
|
// },
|
||||||
|
];
|
||||||
|
form.setFieldValue("forklifts", forkliftArray);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Create New Lease</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Select the company this lease will be for, lease number, start and end
|
||||||
|
date
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
form.handleSubmit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<form.AppField
|
||||||
|
name="companyName"
|
||||||
|
listeners={{
|
||||||
|
onChange: ({ value }) => {
|
||||||
|
onValueChange(value);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
children={(field) => (
|
||||||
|
<field.SelectField
|
||||||
|
label="Select Company"
|
||||||
|
placeholder="Companies"
|
||||||
|
options={companyMap}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<form.AppField
|
||||||
|
name="leaseId"
|
||||||
|
listeners={{
|
||||||
|
onChange: ({ value }) => {
|
||||||
|
onLeaseChange(value);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
children={(field) => (
|
||||||
|
<field.SelectField
|
||||||
|
label="Select Lease"
|
||||||
|
placeholder="LeaseNumber"
|
||||||
|
options={leaseMap}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="w-5/6 m-2">
|
||||||
|
<form.AppField
|
||||||
|
name="invoiceNumber"
|
||||||
|
children={(field) => (
|
||||||
|
<field.InputField
|
||||||
|
label="Enter Invoice Number"
|
||||||
|
inputType="string"
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-5/6 m-2">
|
||||||
|
<form.AppField
|
||||||
|
name="totalAmount"
|
||||||
|
children={(field) => (
|
||||||
|
<field.InputField
|
||||||
|
label="Enter Invoice Amount"
|
||||||
|
inputType="decimal"
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<form.AppField
|
||||||
|
name="invoiceDate"
|
||||||
|
children={(field) => (
|
||||||
|
<field.DateField label="Invoice Date" required={true} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<hr className="mt-2 mb-2" />
|
||||||
|
{/* Dynamic forklift section */}
|
||||||
|
<div className="space-y-3 mt-4">
|
||||||
|
<form.Field
|
||||||
|
name="forklifts"
|
||||||
|
mode="array"
|
||||||
|
children={(field) => (
|
||||||
|
<>
|
||||||
|
<Label>Forklifts</Label>
|
||||||
|
{field.state.value.map((fx, index) => (
|
||||||
|
<form.AppField
|
||||||
|
key={fx.forklift_id}
|
||||||
|
name={`forklifts[${index}].amount`}
|
||||||
|
children={(subField) => (
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<Label htmlFor={subField.name}>{fx.serialNumber}</Label>
|
||||||
|
<Input
|
||||||
|
className="w-1/4"
|
||||||
|
id={subField.name}
|
||||||
|
value={subField.state.value ?? ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
// update this subfield’s amount
|
||||||
|
subField.handleChange(e.target.value);
|
||||||
|
|
||||||
|
// if you also want to store the forklift_id with it
|
||||||
|
field.handleChange(
|
||||||
|
field.state.value.map((val, i) =>
|
||||||
|
i === index
|
||||||
|
? { ...val, amount: e.target.value }
|
||||||
|
: val,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onBlur={subField.handleBlur}
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Map out the input filed based on the forklift id */}
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
form.reset();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</DialogClose>
|
||||||
|
<Button type="submit">Submit</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -108,11 +108,13 @@ export default function NewLeaseForm({
|
|||||||
<div className="flex flex-row gap-2 mt-2 mb-2">
|
<div className="flex flex-row gap-2 mt-2 mb-2">
|
||||||
<form.AppField
|
<form.AppField
|
||||||
name="startDate"
|
name="startDate"
|
||||||
children={(field) => <field.DateField label="Start Date" />}
|
children={(field) => (
|
||||||
|
<field.DateField label="Start Date" required />
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<form.AppField
|
<form.AppField
|
||||||
name="endDate"
|
name="endDate"
|
||||||
children={(field) => <field.DateField label="End Date" />}
|
children={(field) => <field.DateField label="End Date" required />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
115
frontend/src/routes/_app/_forklifts/forklifts/invoices.tsx
Normal file
115
frontend/src/routes/_app/_forklifts/forklifts/invoices.tsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { createColumnHelper } from "@tanstack/react-table";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { ArrowDown, ArrowUp } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { getInvoices } from "@/lib/querys/forklifts/getInvoices";
|
||||||
|
import TableNoExpand from "@/lib/tableStuff/TableNoExpand";
|
||||||
|
|
||||||
|
type Invoices = {
|
||||||
|
id: string;
|
||||||
|
invoiceNumber: string;
|
||||||
|
invoiceDate: Date;
|
||||||
|
totalAmount: string;
|
||||||
|
add_date: Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_app/_forklifts/forklifts/invoices")({
|
||||||
|
component: RouteComponent,
|
||||||
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
const { data: invoiceData = [], isLoading } = useQuery(getInvoices());
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<Invoices>();
|
||||||
|
//const submitting = useRef(false);
|
||||||
|
console.log(invoiceData);
|
||||||
|
const columns = [
|
||||||
|
columnHelper.accessor("invoiceNumber", {
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
<span className="flex flex-row gap-2">Invoice Number</span>
|
||||||
|
{column.getIsSorted() === "asc" ? (
|
||||||
|
<ArrowUp className="ml-2 h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ArrowDown className="ml-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("totalAmount", {
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
<span className="flex flex-row gap-2">Total Amount</span>
|
||||||
|
{column.getIsSorted() === "asc" ? (
|
||||||
|
<ArrowUp className="ml-2 h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ArrowDown className="ml-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("invoiceDate", {
|
||||||
|
header: ({ column }) => {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
>
|
||||||
|
<span className="flex flex-row gap-2">Invoice Date</span>
|
||||||
|
{column.getIsSorted() === "asc" ? (
|
||||||
|
<ArrowUp className="ml-2 h-4 w-4" />
|
||||||
|
) : (
|
||||||
|
<ArrowDown className="ml-2 h-4 w-4" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const raw = getValue() as string | Date;
|
||||||
|
const date = typeof raw === "string" ? new Date(raw) : (raw as Date);
|
||||||
|
if (isNaN(date.getTime())) return "Invalid date";
|
||||||
|
return <span>{format(date, "MM/dd/yyyy")}</span>;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
// columnHelper.accessor("add_date", {
|
||||||
|
// header: ({ column }) => {
|
||||||
|
// return (
|
||||||
|
// <Button
|
||||||
|
// variant="ghost"
|
||||||
|
// onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||||
|
// >
|
||||||
|
// <span className="flex flex-row gap-2">Start Date</span>
|
||||||
|
// {column.getIsSorted() === "asc" ? (
|
||||||
|
// <ArrowUp className="ml-2 h-4 w-4" />
|
||||||
|
// ) : (
|
||||||
|
// <ArrowDown className="ml-2 h-4 w-4" />
|
||||||
|
// )}
|
||||||
|
// </Button>
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// cell: ({ getValue }) => {
|
||||||
|
// const raw = getValue() as string | Date;
|
||||||
|
// const date = typeof raw === "string" ? new Date(raw) : (raw as Date);
|
||||||
|
// if (isNaN(date.getTime())) return "Invalid date";
|
||||||
|
// return <span>{format(date, "MM/dd/yyyy")}</span>;
|
||||||
|
// },
|
||||||
|
//}),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <div className="m-auto">Loading user data</div>;
|
||||||
|
}
|
||||||
|
return <TableNoExpand data={invoiceData} columns={columns} />;
|
||||||
|
}
|
||||||
@@ -65,7 +65,6 @@ let lotColumns = [
|
|||||||
];
|
];
|
||||||
export default function Lots() {
|
export default function Lots() {
|
||||||
const { data, isError, isLoading } = useQuery(getlots());
|
const { data, isError, isLoading } = useQuery(getlots());
|
||||||
|
|
||||||
const { session } = useAuth();
|
const { session } = useAuth();
|
||||||
//const { settings } = useSettingStore();
|
//const { settings } = useSettingStore();
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export default function ManualPrintForm() {
|
|||||||
const { settings } = useSettingStore();
|
const { settings } = useSettingStore();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [printing, setPrinting] = useState(false);
|
||||||
// const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
|
// const serverPort = settings.filter((n) => n.name === "serverPort")[0]?.value;
|
||||||
// const serverUrl = `http://${server}:${serverPort}`;
|
// const serverUrl = `http://${server}:${serverPort}`;
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ export default function ManualPrintForm() {
|
|||||||
// toast.success(`A new label was sent to printer: ${lot.PrinterName} for line ${lot.MachineDescription} `);
|
// toast.success(`A new label was sent to printer: ${lot.PrinterName} for line ${lot.MachineDescription} `);
|
||||||
const logdataUrl = `/lst/old/api/ocp/manuallabellog`;
|
const logdataUrl = `/lst/old/api/ocp/manuallabellog`;
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
|
setPrinting(true);
|
||||||
axios
|
axios
|
||||||
.post(logdataUrl, logData, {})
|
.post(logdataUrl, logData, {})
|
||||||
.then((d) => {
|
.then((d) => {
|
||||||
@@ -71,11 +72,13 @@ export default function ManualPrintForm() {
|
|||||||
reset();
|
reset();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
|
setPrinting(false);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
if (e.response.status === 500) {
|
if (e.response.status === 500) {
|
||||||
toast.error(`Internal Server error please try again.`);
|
toast.error(`Internal Server error please try again.`);
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
|
setPrinting(false);
|
||||||
return { sucess: false };
|
return { sucess: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +100,7 @@ export default function ManualPrintForm() {
|
|||||||
const closeForm = () => {
|
const closeForm = () => {
|
||||||
reset();
|
reset();
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
setPrinting(false);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -106,13 +110,14 @@ export default function ManualPrintForm() {
|
|||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
setOpen(isOpen);
|
setOpen(isOpen);
|
||||||
|
setPrinting(true);
|
||||||
// toast.message("Model was something", {
|
// toast.message("Model was something", {
|
||||||
// description: isOpen ? "Modal is open" : "Modal is closed",
|
// description: isOpen ? "Modal is open" : "Modal is closed",
|
||||||
// });
|
// });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="outline" size="icon">
|
<Button variant="outline" size="icon" disabled={printing}>
|
||||||
<Tag className="h-[16px] w-[16px]" />
|
<Tag className="h-[16px] w-[16px]" />
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|||||||
Reference in New Issue
Block a user