Compare commits

...

5 Commits

67 changed files with 20224 additions and 659 deletions

View File

@@ -1,33 +1,33 @@
---
name: Bug Report
about: Report a bug to help us improve.
title: "[BUG] "
labels: ["bug", "needs-triage"]
---
---
name: Bug Report
about: Report a bug to help us improve.
title: "[BUG] "
labels: ["bug", "needs-triage"]
---
### Describe the bug
### Describe the bug
A clear and concise description of what the bug is.
A clear and concise description of what the bug is.
### Steps to reproduce
### Steps to reproduce
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
### Expected behavior
### Expected behavior
A clear and concise description of what you expected to happen.
A clear and concise description of what you expected to happen.
### Screenshots
### Screenshots
If applicable, add screenshots to help explain your problem.
If applicable, add screenshots to help explain your problem.
### Gitea Version
### Gitea Version
e.g., 1.25.0
e.g., 1.25.0
### Additional context
### Additional context
Add any other context about the problem here.
Add any other context about the problem here.

View File

@@ -0,0 +1,22 @@
meta {
name: Get Invoices
type: http
seq: 4
}
get {
url: {{url}}/lst/api/forklifts/invoices
body: none
auth: inherit
}
body:json {
{
"name":"Delage DLL"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,27 @@
meta {
name: Update lease
type: http
seq: 3
}
patch {
url: {{url}}/lst/api/forklifts/invoices/:id
body: json
auth: inherit
}
params:path {
id: de10c8ee-5756-4efb-9664-3c55338b2b60
}
body:json {
{
"companyId": "b34c6684-ec35-4364-acef-0c1570faf123"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,30 @@
meta {
name: add lease
type: http
seq: 1
}
post {
url: {{url}}/lst/api/forklifts/invoices
body: json
auth: inherit
}
body:json {
{
"leaseId": "0147d082-aee0-4594-b0f4-c6f4ee777e92",
"invoiceNumber": "592596987",
"invoiceDate": "10/12/2025",
"uploadedBy": "matthes01",
"totalAmount": "1820.88",
"forklifts": [
{ "forklift_Id": "ec2f3759-1580-4c1b-8fbf-8a4b0b506758", "amount": 909.91 }
]
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,8 @@
meta {
name: Invoices
seq: 4
}
auth {
mode: inherit
}

View File

@@ -1,6 +1,6 @@
meta {
name: companies
seq: 1
seq: 2
}
auth {

View File

@@ -0,0 +1,22 @@
meta {
name: Get forklift
type: http
seq: 2
}
get {
url: {{url}}/lst/api/forklifts
body: none
auth: inherit
}
body:json {
{
"name":"Delage DLL"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,28 @@
meta {
name: Update forklfit
type: http
seq: 3
}
patch {
url: {{url}}/lst/api/forklifts/:id
body: json
auth: inherit
}
params:path {
id: ec2f3759-1580-4c1b-8fbf-8a4b0b506758
}
body:json {
{
"glCode": 31
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,31 @@
meta {
name: add forklift
type: http
seq: 1
}
post {
url: {{url}}/lst/api/forklifts
body: json
auth: inherit
}
body:json {
{
"serialNumber":"FN682004",
"model": "EFG220",
"plant": "Iowa City ISBM",
"glCode": 31,
"profitCenter": 30,
"manufacturer":"Jungheinrich",
"manufacturerYear":"2022",
"engine":"electric",
"batteryType":"lead acid",
"leaseId":"0147d082-aee0-4594-b0f4-c6f4ee777e92"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,8 @@
meta {
name: forklifts
seq: 3
}
auth {
mode: inherit
}

View File

@@ -0,0 +1,22 @@
meta {
name: Get lease
type: http
seq: 2
}
get {
url: {{url}}/lst/api/forklifts/leases
body: none
auth: inherit
}
body:json {
{
"name":"Delage DLL"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,27 @@
meta {
name: Update lease
type: http
seq: 3
}
patch {
url: {{url}}/lst/api/forklifts/leases/:id
body: json
auth: inherit
}
params:path {
id: de10c8ee-5756-4efb-9664-3c55338b2b60
}
body:json {
{
"companyId": "b34c6684-ec35-4364-acef-0c1570faf123"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,25 @@
meta {
name: add lease
type: http
seq: 1
}
post {
url: {{url}}/lst/api/forklifts/leases
body: json
auth: inherit
}
body:json {
{
"leaseNumber":"500-50489192",
"startDate": "11/08/2023",
"endDate": "11/12/2025",
"companyId": "b34c6684-ec35-4364-acef-0c1570faf123"
}
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -0,0 +1,8 @@
meta {
name: lease
seq: 1
}
auth {
mode: inherit
}

View File

@@ -13,12 +13,12 @@ post {
body:json {
{
"scannerId": 999,
"lotNr": 3314,
"machineId": 22, // 457=22, 458=23
"printerId": 22, // 457=22, 458=23
"layoutId": 7,
"numberOfCopies": 0,
"qtyToPrint": 1
"lotNr": 26656,
"machineId": 5, // 457=22, 458=23
"printerId": 7, // 457=22, 458=23
"layoutId": 22,
"numberOfCopies": 1,
"qtyToPrint":0
}
}

View File

@@ -33,7 +33,16 @@ router.post("/", async (req: Request, res: Response) => {
upd_user: req.user?.username,
upd_date: sql`NOW()`,
})
//.onConflictDoNothing()
.onConflictDoUpdate({
target: forkliftCompanies.name,
set: {
...parsed.data,
add_user: req.user?.username,
add_date: sql`NOW()`,
upd_user: req.user?.username,
upd_date: sql`NOW()`,
},
})
.returning({
name: forkliftCompanies.name,
}),

View File

@@ -0,0 +1,117 @@
import axios from "axios";
import { type DrizzleError, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import { db } from "../../../../pkg/db/db.js";
import {
forklifts,
newForkliftsSchema,
} from "../../../../pkg/db/schema/forklifts.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
// when a new server is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there
//res.status(200).json({ message: "Server added", ip: req.hostname });
const log = createLogger({ module: "forklift", subModule: "add forklift" });
const parsed = newForkliftsSchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ errors: parsed.error.flatten() });
}
const { data, error } = await tryCatch(
db
.insert(forklifts)
.values({
...parsed.data,
add_user: req.user?.username,
add_date: sql`NOW()`,
upd_user: req.user?.username,
upd_date: sql`NOW()`,
})
.onConflictDoUpdate({
target: forklifts.serialNumber,
set: {
...parsed.data,
add_user: req.user?.username,
add_date: sql`NOW()`,
upd_user: req.user?.username,
upd_date: sql`NOW()`,
},
})
.returning({
serialNumber: forklifts.serialNumber,
}),
);
if (error) {
console.log(error);
const err: DrizzleError = error;
return res.status(400).json({
message: `Error adding forklift`,
error: err.cause,
});
}
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
// log.info({}, "Running in dev server about to add in a new server");
// const axiosInstance = axios.create({
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
// baseURL: process.env.MAIN_SERVER, // e.g. "https://example.com"
// withCredentials: true,
// });
// const loginRes = (await axiosInstance.post(
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
// {
// username: process.env.MAIN_SERVER_USERNAME,
// password: process.env.MAIN_SERVER_PASSWORD,
// },
// {
// headers: { "Content-Type": "application/json" },
// },
// )) as any;
// const setCookie = loginRes.headers["set-cookie"][0];
// if (!setCookie) {
// throw new Error("Did not receive a Set-Cookie header from login");
// }
// const { data, error } = await tryCatch(
// axios.post(
// `${process.env.MAIN_SERVER}/lst/api/forklifts/companies`,
// parsed.data,
// {
// headers: {
// "Content-Type": "application/json",
// Cookie: setCookie.split(";")[0],
// },
// withCredentials: true,
// },
// ),
// );
// if (error) {
// log.error(
// { stack: error },
// "There was an error adding the company to Main Server",
// );
// }
// log.info(
// { stack: data?.data },
// "A new Company was just added to the server.",
// );
// }
return res
.status(201)
.json({ message: `Forklift ${data[0]?.serialNumber} added`, data: data });
});
export default router;

View File

@@ -0,0 +1,26 @@
import { Router } from "express";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
import addForklift from "./addForklift.js";
import getForklifts from "./getForklifts.js";
import updateForklift from "./updateForklift.js";
const router = Router();
router.use(
"/",
requireAuth("forklifts", ["systemAdmin", "admin", "manager", "supervisor"]),
getForklifts,
);
router.use(
"/",
requireAuth("forklifts", ["systemAdmin", "admin"]),
addForklift,
);
router.use(
"/",
requireAuth("forklifts", ["systemAdmin", "admin"]),
updateForklift,
);
export default router;

View File

@@ -0,0 +1,38 @@
import { and, asc, eq } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import { db } from "../../../../pkg/db/db.js";
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
import { forklifts } from "../../../../pkg/db/schema/forklifts.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.get("/", async (req: Request, res: Response) => {
const plant = req.query.plant;
const conditions = [];
if (plant !== undefined) {
conditions.push(eq(leases.leaseNumber, `${plant}`));
}
//conditions.push(eq(forkliftCompanies.active, true));
const { data, error } = await tryCatch(
db
.select()
.from(forklifts)
//.innerJoin(forkliftCompanies, eq(forkliftCompanies.id, leases.companyId))
.where(and(...conditions))
.orderBy(asc(forklifts.serialNumber)),
);
if (error) {
return res.status(400).json({ error: error });
}
res.status(200).json({ message: "Current Forklifts", data: data });
});
export default router;

View File

@@ -0,0 +1,136 @@
import axios from "axios";
import { eq, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import { db } from "../../../../pkg/db/db.js";
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
import { forklifts } from "../../../../pkg/db/schema/forklifts.js";
import { serverData } from "../../../../pkg/db/schema/servers.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.patch("/:id", async (req: Request, res: Response) => {
const log = createLogger({
module: "forklifts",
subModule: "update leases",
});
// when a server is updated and is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there, we want to insert with update on conflict.
const id = req.params.id;
const updates: Record<string, any> = {};
if (req.body?.model !== undefined) {
updates.model = req.body.model;
}
if (req.body?.plant !== undefined) {
updates.plant = req.body.plant;
}
if (req.body?.glCode !== undefined) {
updates.glCode = req.body.glCode;
}
if (req.body?.profitCenter !== undefined) {
updates.profitCenter = req.body.profitCenter;
}
if (req.body?.manufacturer !== undefined) {
updates.manufacturer = req.body.manufacturer;
}
if (req.body?.manufacturerYear !== undefined) {
updates.manufacturerYear = req.body.manufacturerYear;
}
if (req.body?.engine !== undefined) {
updates.engine = req.body.engine;
}
if (req.body?.batteryType !== undefined) {
updates.batteryType = req.body.batteryType;
}
if (req.body?.dataPlate !== undefined) {
updates.dataPlate = req.body.dataPlate;
}
if (req.body?.forkliftNumber !== undefined) {
updates.forkliftNumber = req.body.forkliftNumber;
}
updates.upd_user = req.user!.username || "lst_user";
updates.upd_date = sql`NOW()`;
try {
if (Object.keys(updates).length > 0) {
await db
.update(forklifts)
.set(updates)
.where(eq(forklifts.forklift_id, id));
}
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
// log.info({}, "Running in dev server about to add in a new server");
// const axiosInstance = axios.create({
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
// baseURL: process.env.MAIN_SERVER,
// withCredentials: true,
// });
// const loginRes = (await axiosInstance.post(
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
// {
// username: process.env.MAIN_SERVER_USERNAME,
// password: process.env.MAIN_SERVER_PASSWORD,
// },
// {
// headers: { "Content-Type": "application/json" },
// },
// )) as any;
// const setCookie = loginRes?.headers["set-cookie"][0];
// //console.log(setCookie.split(";")[0].replace("__Secure-", ""));
// if (!setCookie) {
// throw new Error("Did not receive a Set-Cookie header from login");
// }
// const { data, error } = await tryCatch(
// axios.patch(
// `${process.env.MAIN_SERVER}/lst/api/forklifts/companies/${id}`,
// updates,
// {
// headers: {
// "Content-Type": "application/json",
// Cookie: setCookie.split(";")[0],
// },
// withCredentials: true,
// },
// ),
// );
// if (error) {
// //console.log(error);
// log.error(
// { stack: error },
// "There was an error updating the lease to Main Server",
// );
// }
// log.info(
// { stack: data?.data },
// "A new lease was just updated to the server.",
// );
// }
res.status(200).json({ message: `${id} was just updated` });
} catch (error) {
//console.log(error);
res.status(200).json({ message: "Error updating lease", error });
}
});
export default router;

View File

@@ -0,0 +1,160 @@
import axios from "axios";
import { type DrizzleError, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import type z from "zod";
import { db } from "../../../../pkg/db/db.js";
import {
leaseInvoiceForklifts,
newForkliftInvoiceSchema,
} from "../../../../pkg/db/schema/forkliftLeasesInvoice.js";
import {
leaseInvoices,
newInvoiceSchema,
} from "../../../../pkg/db/schema/leaseInvoices.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
// when a new server is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there
//res.status(200).json({ message: "Server added", ip: req.hostname });
const log = createLogger({ module: "forklift", subModule: "add invoice" });
const parsedInvoice = newInvoiceSchema.safeParse({
leaseId: req.body.leaseId,
companyId: req.body.companyId,
invoiceNumber: req.body.invoiceNumber,
invoiceDate: req.body.invoiceDate,
uploadedBy: req.body.uploadedBy,
totalAmount: req.body.totalAmount,
});
if (!parsedInvoice.success)
return res.status(400).json({ error: parsedInvoice.error.flatten });
const invoiceData = parsedInvoice.data;
const forkliftItems = Array.isArray(req.body.forklifts)
? req.body.forklifts
: [];
const validatedForklifts = []; //z.infer<typeof newForkliftInvoiceSchema>[] = [];
for (const item of forkliftItems) {
// const parsedItem = newForkliftInvoiceSchema.safeParse(item);
// if (parsedItem.success) {
validatedForklifts.push(item);
//} else {
//return res.status(400).json({ error: parsedItem.error.flatten() });
//}
}
// 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 = (
validatedForklifts.reduce((sum, f) => sum + Number(f.amount || 0), 0) -
req.body.totalInvoice
).toString();
const { data, error } = await tryCatch(
db
.insert(leaseInvoices)
.values({
...invoiceData,
uploadedBy: req.user!.username || "lst_user",
})
.onConflictDoUpdate({
target: leaseInvoices.invoiceNumber,
set: {
totalAmount,
invoiceDate: invoiceData.invoiceDate,
uploadedBy: req.user!.username || "lst_user",
},
})
.returning(),
);
if (error) {
const err: DrizzleError = error;
return res.status(400).json({
message: `Error adding lease`,
error: err.cause,
});
}
const invoiceId = data[0]?.id;
const forkliftInvoices = validatedForklifts.map((f) => ({
invoiceId,
forkliftId: f.forklift_Id,
amount: f.amount,
}));
console.log(forkliftInvoices);
if (validatedForklifts.length > 0) {
await db.insert(leaseInvoiceForklifts).values(forkliftInvoices);
// .onConflictDoUpdate({
// target: [
// leaseInvoiceForklifts.invoiceId,
// leaseInvoiceForklifts.forkliftId,
// ],
// set: { amount: (excluded) => excluded.amount },
// });
}
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
// log.info({}, "Running in dev server about to add in a new server");
// const axiosInstance = axios.create({
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
// baseURL: process.env.MAIN_SERVER, // e.g. "https://example.com"
// withCredentials: true,
// });
// const loginRes = (await axiosInstance.post(
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
// {
// username: process.env.MAIN_SERVER_USERNAME,
// password: process.env.MAIN_SERVER_PASSWORD,
// },
// {
// headers: { "Content-Type": "application/json" },
// },
// )) as any;
// const setCookie = loginRes.headers["set-cookie"][0];
// if (!setCookie) {
// throw new Error("Did not receive a Set-Cookie header from login");
// }
// const { data, error } = await tryCatch(
// axios.post(
// `${process.env.MAIN_SERVER}/lst/api/forklifts/leases`,
// parsed.data,
// {
// headers: {
// "Content-Type": "application/json",
// Cookie: setCookie.split(";")[0],
// },
// withCredentials: true,
// },
// ),
// );
// if (error) {
// log.error(
// { stack: error },
// "There was an error adding the company to Main Server",
// );
// }
// log.info(
// { stack: data?.data },
// "A new Company was just added to the server.",
// );
// }
return res
.status(201)
.json({ message: `lease ${data[0]?.invoiceNumber} added`, data: data });
});
export default router;

View File

@@ -0,0 +1,52 @@
import { and, asc, eq, relations } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import { db } from "../../../../pkg/db/db.js";
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
import { forklifts } from "../../../../pkg/db/schema/forklifts.js";
import { leaseInvoices } from "../../../../pkg/db/schema/leaseInvoices.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.get("/", async (req: Request, res: Response) => {
const invoiceNumber = req.query.lease;
const conditions = [];
if (invoiceNumber !== undefined) {
conditions.push(eq(leaseInvoices.invoiceNumber, `${invoiceNumber}`));
}
//conditions.push(eq(forkliftCompanies.active, true));
const { data, error } = await tryCatch(
db
.select(
// {
// id: leases.id,
// leaseNumber: leases.leaseNumber,
// startDate: leases.startDate,
// endDate: leases.endDate,
// leaseLink: leases.leaseLink,
// companyName: forkliftCompanies.name,
// add_user: leases.add_user,
// add_date: leases.add_date,
// upd_user: leases.upd_user,
// upd_date: leases.upd_date,
// }
)
.from(leaseInvoices)
//.innerJoin(forkliftCompanies, eq(forkliftCompanies.id, leases.companyId))
.where(and(...conditions))
.orderBy(asc(leaseInvoices.invoiceNumber)),
);
if (error) {
return res.status(400).json({ error: error });
}
res.status(200).json({ message: "Current Leases", data: data });
});
export default router;

View File

@@ -0,0 +1,22 @@
import { Router } from "express";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
import addInvoice from "./addInvoice.js";
import getInvoices from "./getInvoices.js";
import updateInvoice from "./updateInvoices.js";
const router = Router();
router.use(
"/",
requireAuth("forklifts", ["systemAdmin", "admin", "manager"]),
getInvoices,
);
router.use("/", requireAuth("forklifts", ["systemAdmin", "admin"]), addInvoice);
router.use(
"/",
requireAuth("forklifts", ["systemAdmin", "admin"]),
updateInvoice,
);
export default router;

View File

@@ -0,0 +1,114 @@
import axios from "axios";
import { eq, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import { db } from "../../../../pkg/db/db.js";
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
import { serverData } from "../../../../pkg/db/schema/servers.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.patch("/:id", async (req: Request, res: Response) => {
const log = createLogger({
module: "forklifts",
subModule: "update invoice",
});
// when a server is updated and is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there, we want to insert with update on conflict.
const id = req.params.id;
const updates: Record<string, any> = {};
console.log(req.body);
if (req.body?.leaseNumber !== undefined) {
updates.leaseNumber = req.body.leaseNumber;
}
if (req.body?.startDate !== undefined) {
updates.startDate = req.body.startDate;
}
if (req.body?.endDate !== undefined) {
updates.endDate = req.body.endDate;
}
if (req.body?.companyId !== undefined) {
updates.companyId = req.body.companyId;
}
if (req.body?.leaseLink !== undefined) {
updates.leaseLink = req.body.leaseLink;
}
updates.upd_user = req.user!.username || "lst_user";
updates.upd_date = sql`NOW()`;
console.log(updates);
try {
if (Object.keys(updates).length > 0) {
await db.update(leases).set(updates).where(eq(leases.id, id));
}
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
// log.info({}, "Running in dev server about to add in a new server");
// const axiosInstance = axios.create({
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
// baseURL: process.env.MAIN_SERVER,
// withCredentials: true,
// });
// const loginRes = (await axiosInstance.post(
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
// {
// username: process.env.MAIN_SERVER_USERNAME,
// password: process.env.MAIN_SERVER_PASSWORD,
// },
// {
// headers: { "Content-Type": "application/json" },
// },
// )) as any;
// const setCookie = loginRes?.headers["set-cookie"][0];
// //console.log(setCookie.split(";")[0].replace("__Secure-", ""));
// if (!setCookie) {
// throw new Error("Did not receive a Set-Cookie header from login");
// }
// const { data, error } = await tryCatch(
// axios.patch(
// `${process.env.MAIN_SERVER}/lst/api/forklifts/leases/${id}`,
// updates,
// {
// headers: {
// "Content-Type": "application/json",
// Cookie: setCookie.split(";")[0],
// },
// withCredentials: true,
// },
// ),
// );
// if (error) {
// //console.log(error);
// log.error(
// { stack: error },
// "There was an error updating the lease to Main Server",
// );
// }
// log.info(
// { stack: data?.data },
// "A new lease was just updated to the server.",
// );
// }
res.status(200).json({ message: `${id} was just updated` });
} catch (error) {
//console.log(error);
res.status(200).json({ message: "Error updating lease", error });
}
});
export default router;

View File

@@ -0,0 +1,114 @@
import axios from "axios";
import { type DrizzleError, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import { db } from "../../../../pkg/db/db.js";
import {
insertLeasesCompanySchema,
leases,
} from "../../../../pkg/db/schema/forkliftLeases.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.post("/", async (req: Request, res: Response) => {
// when a new server is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there
//res.status(200).json({ message: "Server added", ip: req.hostname });
const log = createLogger({ module: "forklift", subModule: "add lease" });
const parsed = insertLeasesCompanySchema.safeParse(req.body);
if (!parsed.success) {
return res.status(400).json({ errors: parsed.error.flatten() });
}
const { data, error } = await tryCatch(
db
.insert(leases)
.values({
...parsed.data,
add_user: req.user?.username,
add_date: sql`NOW()`,
upd_user: req.user?.username,
upd_date: sql`NOW()`,
})
.onConflictDoUpdate({
target: leases.leaseNumber,
set: {
...parsed.data,
add_user: req.user?.username,
add_date: sql`NOW()`,
upd_user: req.user?.username,
upd_date: sql`NOW()`,
},
})
.returning({
leaseNumber: leases.leaseNumber,
}),
);
if (error) {
const err: DrizzleError = error;
return res.status(400).json({
message: `Error adding lease`,
error: err.cause,
});
}
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
// log.info({}, "Running in dev server about to add in a new server");
// const axiosInstance = axios.create({
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
// baseURL: process.env.MAIN_SERVER, // e.g. "https://example.com"
// withCredentials: true,
// });
// const loginRes = (await axiosInstance.post(
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
// {
// username: process.env.MAIN_SERVER_USERNAME,
// password: process.env.MAIN_SERVER_PASSWORD,
// },
// {
// headers: { "Content-Type": "application/json" },
// },
// )) as any;
// const setCookie = loginRes.headers["set-cookie"][0];
// if (!setCookie) {
// throw new Error("Did not receive a Set-Cookie header from login");
// }
// const { data, error } = await tryCatch(
// axios.post(
// `${process.env.MAIN_SERVER}/lst/api/forklifts/leases`,
// parsed.data,
// {
// headers: {
// "Content-Type": "application/json",
// Cookie: setCookie.split(";")[0],
// },
// withCredentials: true,
// },
// ),
// );
// if (error) {
// log.error(
// { stack: error },
// "There was an error adding the company to Main Server",
// );
// }
// log.info(
// { stack: data?.data },
// "A new Company was just added to the server.",
// );
// }
return res
.status(201)
.json({ message: `lease ${data[0]?.leaseNumber} added`, data: data });
});
export default router;

View File

@@ -0,0 +1,57 @@
import { and, asc, eq } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import { db } from "../../../../pkg/db/db.js";
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
import { forklifts } from "../../../../pkg/db/schema/forklifts.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.get("/", async (req: Request, res: Response) => {
const lease = req.query.lease;
const conditions = [];
if (lease !== undefined) {
conditions.push(eq(leases.leaseNumber, `${lease}`));
}
//conditions.push(eq(forkliftCompanies.active, true));
const { data, error } = (await tryCatch(
db
.select({
id: leases.id,
leaseNumber: leases.leaseNumber,
startDate: leases.startDate,
endDate: leases.endDate,
leaseLink: leases.leaseLink,
companyName: forkliftCompanies.name,
add_user: leases.add_user,
add_date: leases.add_date,
upd_user: leases.upd_user,
upd_date: leases.upd_date,
})
.from(leases)
.innerJoin(forkliftCompanies, eq(forkliftCompanies.id, leases.companyId))
.where(and(...conditions))
.orderBy(asc(leases.leaseNumber)),
)) as any;
// add the forklifts that are in this lease
const forkliftData = await db.select().from(forklifts);
if (error) {
return res.status(400).json({ error: error });
}
const leaseData = data.map((i: any) => ({
...i,
forklifts: forkliftData.filter((x) => x.leaseId === i.id),
}));
res.status(200).json({ message: "Current Leases", data: leaseData });
});
export default router;

View File

@@ -0,0 +1,18 @@
import { Router } from "express";
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
import addLeases from "./addLease.js";
import getLeases from "./getLeases.js";
import updateLeases from "./updateLease.js";
const router = Router();
router.use("/", requireAuth("forklifts", ["systemAdmin", "admin"]), getLeases);
router.use("/", requireAuth("forklifts", ["systemAdmin", "admin"]), addLeases);
router.use(
"/",
requireAuth("forklifts", ["systemAdmin", "admin"]),
updateLeases,
);
export default router;

View File

@@ -0,0 +1,114 @@
import axios from "axios";
import { eq, sql } from "drizzle-orm";
import type { Request, Response } from "express";
import { Router } from "express";
import https from "https";
import { db } from "../../../../pkg/db/db.js";
import { forkliftCompanies } from "../../../../pkg/db/schema/forkliftLeaseCompanys.js";
import { leases } from "../../../../pkg/db/schema/forkliftLeases.js";
import { serverData } from "../../../../pkg/db/schema/servers.js";
import { createLogger } from "../../../../pkg/logger/logger.js";
import { tryCatch } from "../../../../pkg/utils/tryCatch.js";
const router = Router();
router.patch("/:id", async (req: Request, res: Response) => {
const log = createLogger({
module: "forklifts",
subModule: "update leases",
});
// when a server is updated and is posted from localhost or 127.0.0.1 we also want to post it to the test server so we can see it from there, we want to insert with update on conflict.
const id = req.params.id;
const updates: Record<string, any> = {};
console.log(req.body);
if (req.body?.leaseNumber !== undefined) {
updates.leaseNumber = req.body.leaseNumber;
}
if (req.body?.startDate !== undefined) {
updates.startDate = req.body.startDate;
}
if (req.body?.endDate !== undefined) {
updates.endDate = req.body.endDate;
}
if (req.body?.companyId !== undefined) {
updates.companyId = req.body.companyId;
}
if (req.body?.leaseLink !== undefined) {
updates.leaseLink = req.body.leaseLink;
}
updates.upd_user = req.user!.username || "lst_user";
updates.upd_date = sql`NOW()`;
console.log(updates);
try {
if (Object.keys(updates).length > 0) {
await db.update(leases).set(updates).where(eq(leases.id, id));
}
// if (req.hostname === "localhost" && process.env.MAIN_SERVER) {
// log.info({}, "Running in dev server about to add in a new server");
// const axiosInstance = axios.create({
// httpsAgent: new https.Agent({ rejectUnauthorized: false }),
// baseURL: process.env.MAIN_SERVER,
// withCredentials: true,
// });
// const loginRes = (await axiosInstance.post(
// `${process.env.MAIN_SERVER}/lst/api/auth/sign-in/username`,
// {
// username: process.env.MAIN_SERVER_USERNAME,
// password: process.env.MAIN_SERVER_PASSWORD,
// },
// {
// headers: { "Content-Type": "application/json" },
// },
// )) as any;
// const setCookie = loginRes?.headers["set-cookie"][0];
// //console.log(setCookie.split(";")[0].replace("__Secure-", ""));
// if (!setCookie) {
// throw new Error("Did not receive a Set-Cookie header from login");
// }
// const { data, error } = await tryCatch(
// axios.patch(
// `${process.env.MAIN_SERVER}/lst/api/forklifts/leases/${id}`,
// updates,
// {
// headers: {
// "Content-Type": "application/json",
// Cookie: setCookie.split(";")[0],
// },
// withCredentials: true,
// },
// ),
// );
// if (error) {
// //console.log(error);
// log.error(
// { stack: error },
// "There was an error updating the lease to Main Server",
// );
// }
// log.info(
// { stack: data?.data },
// "A new lease was just updated to the server.",
// );
// }
res.status(200).json({ message: `${id} was just updated` });
} catch (error) {
//console.log(error);
res.status(200).json({ message: "Error updating lease", error });
}
});
export default router;

View File

@@ -1,9 +1,24 @@
import type { Express, Request, Response } from "express";
import { requireAuth } from "../../../pkg/middleware/authMiddleware.js";
import companies from "./companies/companiesRoutes.js";
import forklifts from "./forklifts/forkliftRoutes.js";
import invoices from "./invoices/invoiceRoutes.js";
import leases from "./leases/leaseRoutes.js";
export const setupForkliftRoutes = (app: Express, basePath: string) => {
app.use(
basePath + "/api/forklifts", // will pass bc system admin but this is just telling us we need this
forklifts,
);
app.use(
basePath + "/api/forklifts/companies", // will pass bc system admin but this is just telling us we need this
companies,
);
app.use(
basePath + "/api/forklifts/leases", // will pass bc system admin but this is just telling us we need this
leases,
);
app.use(
basePath + "/api/forklifts/invoices", // will pass bc system admin but this is just telling us we need this
invoices,
);
};

View File

@@ -69,6 +69,8 @@ const addProdLabel = async (
if (prodLabelError) {
log.error({ error: error }, "Error adding the label");
}
return;
};
export const preprintLabels = async (preprint: Preprint, username?: string) => {
@@ -85,12 +87,12 @@ export const preprintLabels = async (preprint: Preprint, username?: string) => {
"POST",
"/public/v1.0/Warehousing/GenerateAndPrintLabel",
{
scannerId: preprint.scannerId,
lotNr: preprint.lotNr,
machineId: preprint.machineId, // 457=22, 458=23
printerId: preprint.printerId, // 457=22, 458=23
layoutId: preprint.layoutId,
numberOfCopies: preprint.numberOfCopies,
scannerId: preprint.scannerId.toString(),
lotNr: preprint.lotNr.toString(),
machineId: preprint.machineId.toString(),
printerId: preprint.printerId.toString(),
layoutId: preprint.layoutId.toString(),
numberOfCopies: preprint.numberOfCopies.toString(),
},
);
if (labels?.data.Result === 1) {
@@ -116,7 +118,7 @@ export const preprintLabels = async (preprint: Preprint, username?: string) => {
}
labelsPrinted.push(parseInt(labels?.data.SSCC.slice(10, -1)));
// add the label to our label db for tracking purposes
addProdLabel(
await addProdLabel(
preprint,
parseInt(labels?.data.SSCC.slice(10, -1)),
username || "lst",
@@ -141,12 +143,12 @@ export const preprintLabels = async (preprint: Preprint, username?: string) => {
"POST",
"/public/v1.0/Warehousing/GenerateAndPrintLabel",
{
scannerId: preprint.scannerId,
lotNr: preprint.lotNr,
machineId: preprint.machineId, // 457=22, 458=23
printerId: preprint.printerId, // 457=22, 458=23
layoutId: preprint.layoutId,
numberOfCopies: preprint.numberOfCopies,
scannerId: preprint.scannerId.toString(),
lotNr: preprint.lotNr.toString(),
machineId: preprint.machineId.toString(),
printerId: preprint.printerId.toString(),
layoutId: preprint.layoutId.toString(),
numberOfCopies: preprint.numberOfCopies.toString(),
},
);
@@ -173,7 +175,7 @@ export const preprintLabels = async (preprint: Preprint, username?: string) => {
}
labelsPrinted.push(parseInt(labels.data.SSCC.slice(10, -1)));
addProdLabel(
await addProdLabel(
preprint,
parseInt(labels?.data.SSCC.slice(10, -1)),
username || "lst",

View File

@@ -1,13 +1,26 @@
import { date, pgTable, text, uuid } from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod";
import { date, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import z from "zod";
import { forkliftCompanies } from "./forkliftLeaseCompanys.js";
export const leases = pgTable("leases", {
id: uuid("id").defaultRandom().primaryKey(),
leaseNumber: text("lease_number").notNull(),
leaseNumber: text("lease_number").unique().notNull(),
companyId: uuid("company_id").references(() => forkliftCompanies.id),
startDate: date("start_date"),
endDate: date("end_date"),
leaseLink: text("lease_link"),
add_date: timestamp("add_date").defaultNow(),
add_user: text("add_user").default("LST"),
upd_date: timestamp("upd_date").defaultNow(),
upd_user: text("upd_user").default("LST"),
});
export const selectLeasesDataSchema = createSelectSchema(leases);
export const insertLeasesCompanySchema = createInsertSchema(leases).extend({
leaseNumber: z.string().min(3),
// zipcode: z
// .string()
// .regex(/^\d{5}$/)
// .optional(),
});

View File

@@ -1,9 +1,10 @@
import { numeric, pgTable, serial, uuid } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { forklifts } from "./forklifts.js";
import { leaseInvoices } from "./leaseInvoices.js";
export const leaseInvoiceForklifts = pgTable("lease_invoice_forklifts", {
id: serial("id").primaryKey(),
id: uuid("id").defaultRandom().primaryKey(),
invoiceId: uuid("invoice_id")
.notNull()
.references(() => leaseInvoices.id, { onDelete: "cascade" }),
@@ -12,3 +13,7 @@ export const leaseInvoiceForklifts = pgTable("lease_invoice_forklifts", {
.references(() => forklifts.forklift_id, { onDelete: "cascade" }),
amount: numeric("amount"), // optional: amount of invoice allocated to this lift
});
export const newForkliftInvoiceSchema = createInsertSchema(
leaseInvoiceForklifts,
);

View File

@@ -23,7 +23,7 @@ const status = pgEnum("forklift_status", [
export const forklifts = pgTable("forklifts", {
forklift_id: uuid("forklift_id").defaultRandom().primaryKey(),
forkliftNumber: serial("forklift_number").notNull(),
serialNumber: text("serial_number").notNull(),
serialNumber: text("serial_number").unique().notNull(),
model: text("model").notNull(),
plant: text("plant")
.notNull()
@@ -41,8 +41,8 @@ export const forklifts = pgTable("forklifts", {
dataPlate: text("data_plate"),
add_date: timestamp("add_date").defaultNow(),
add_user: text("add_user").default("LST"),
upd_date: timestamp("add_date").defaultNow(),
upd_user: text("add_user").default("LST"),
upd_date: timestamp("upd_date").defaultNow(),
upd_user: text("upd_user").default("LST"),
});
export const forkliftsSchema = createSelectSchema(forklifts);

View File

@@ -1,19 +1,24 @@
import { date, numeric, pgTable, text, uuid } from "drizzle-orm/pg-core";
import { forkliftCompanies } from "./forkliftLeaseCompanys.js";
import {
date,
numeric,
pgTable,
text,
timestamp,
uuid,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { leases } from "./forkliftLeases.js";
import { forklifts } from "./forklifts.js";
export const leaseInvoices = pgTable("lease_invoices", {
id: uuid("id").defaultRandom().primaryKey(),
leaseId: uuid("lease_id")
.notNull()
.references(() => leases.id, { onDelete: "cascade" }),
companyId: uuid("company_id").references(() => forkliftCompanies.id),
invoiceNumber: text("invoice_number").notNull(),
invoiceNumber: text("invoice_number").unique().notNull(),
invoiceDate: date("invoice_date").notNull(),
forkliftId: uuid("forklift_id")
.notNull()
.references(() => forklifts.forklift_id, { onDelete: "cascade" }),
totalAmount: numeric("total_amount"),
add_date: timestamp("add_date"),
uploadedBy: text("uploaded_by"),
});
export const newInvoiceSchema = createInsertSchema(leaseInvoices);

View File

@@ -19,7 +19,7 @@
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
@@ -1720,6 +1720,24 @@
}
}
},
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -1786,6 +1804,24 @@
}
}
},
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-direction": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
@@ -1978,6 +2014,24 @@
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
@@ -2015,6 +2069,24 @@
}
}
},
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
@@ -2118,6 +2190,24 @@
}
}
},
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
@@ -2223,6 +2313,24 @@
}
}
},
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-separator": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
@@ -2247,9 +2355,9 @@
}
},
"node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz",
"integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
@@ -2357,6 +2465,24 @@
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",

View File

@@ -21,7 +21,7 @@
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",

View File

@@ -29,7 +29,7 @@ export default function ForkliftSideBar() {
},
{
title: "Leases",
url: "/lst/app/admin/settings",
url: "/lst/app/forklifts/leases",
icon: ReceiptText,
role: ["systemAdmin", "admin"],
module: "forklifts",

View File

@@ -1,6 +1,7 @@
import { Link, useRouterState } from "@tanstack/react-router";
import { useState } from "react";
import NewCompanyForm from "@/routes/_app/_forklifts/-components/NewCompany";
import NewLeaseForm from "@/routes/_app/_forklifts/-components/NewLease";
import { useAuth, useLogout } from "../../lib/authClient";
import { ModeToggle } from "../mode-toggle";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
@@ -21,6 +22,7 @@ export default function Nav() {
const router = useRouterState();
const currentPath = router.location.href;
const [openDialog, setOpenDialog] = useState(false);
const [openLeaseDialog, setOpenLeaseDialog] = useState(false);
return (
<nav className="flex justify-end w-full shadow ">
<div className="m-2 flex flex-row gap-1">
@@ -41,7 +43,7 @@ export default function Nav() {
{location.pathname.includes("forklifts") && (
<>
<DropdownMenu>
<DropdownMenuTrigger>
<DropdownMenuTrigger asChild>
<Button>Forklifts</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
@@ -55,9 +57,17 @@ export default function Nav() {
>
New Company
</DropdownMenuItem>
<DropdownMenuItem
onSelect={() => {
// just open the dialog when clicked
setOpenLeaseDialog(true);
}}
>
New Lease
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{/* Dialog mounted outside the menu */}
{/* Company */}
{openDialog && (
<Dialog open={openDialog} onOpenChange={setOpenDialog}>
<DialogContent className="sm:max-w-[425px]">
@@ -65,6 +75,16 @@ export default function Nav() {
</DialogContent>
</Dialog>
)}
{openLeaseDialog && (
<Dialog
open={openLeaseDialog}
onOpenChange={setOpenLeaseDialog}
>
<DialogContent className="sm:max-w-fit">
<NewLeaseForm setOpenDialog={setOpenLeaseDialog} />
</DialogContent>
</Dialog>
)}
</>
)}
</div>

View File

@@ -1,23 +1,23 @@
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "../../lib/utils";
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
@@ -25,6 +25,8 @@ const buttonVariants = cva(
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
@@ -32,7 +34,7 @@ const buttonVariants = cva(
size: "default",
},
}
);
)
function Button({
className,
@@ -42,9 +44,9 @@ function Button({
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button";
const Comp = asChild ? Slot : "button"
return (
<Comp
@@ -52,7 +54,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
)
}
export { Button, buttonVariants };
export { Button, buttonVariants }

View File

@@ -1,12 +1,13 @@
import * as React from "react";
import * as React from "react"
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "lucide-react";
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
import { buttonVariants, Button } from "./button";
import { cn } from "../../lib/utils";
} from "lucide-react"
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/ui/button"
function Calendar({
className,
@@ -18,9 +19,9 @@ function Calendar({
components,
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
}) {
const defaultClassNames = getDefaultClassNames();
const defaultClassNames = getDefaultClassNames()
return (
<DayPicker
@@ -43,10 +44,7 @@ function Calendar({
"flex gap-4 flex-col md:flex-row relative",
defaultClassNames.months
),
month: cn(
"flex flex-col w-full gap-4",
defaultClassNames.month
),
month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
nav: cn(
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
defaultClassNames.nav
@@ -100,21 +98,18 @@ function Calendar({
defaultClassNames.week_number
),
day: cn(
"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
"relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
props.showWeekNumber
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
defaultClassNames.day
),
range_start: cn(
"rounded-l-md bg-accent",
defaultClassNames.range_start
),
range_middle: cn(
"rounded-none",
defaultClassNames.range_middle
),
range_end: cn(
"rounded-r-md bg-accent",
defaultClassNames.range_end
),
range_middle: cn("rounded-none", defaultClassNames.range_middle),
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
today: cn(
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
defaultClassNames.today
@@ -139,16 +134,13 @@ function Calendar({
className={cn(className)}
{...props}
/>
);
)
},
Chevron: ({ className, orientation, ...props }) => {
if (orientation === "left") {
return (
<ChevronLeftIcon
className={cn("size-4", className)}
{...props}
/>
);
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
)
}
if (orientation === "right") {
@@ -157,15 +149,12 @@ function Calendar({
className={cn("size-4", className)}
{...props}
/>
);
)
}
return (
<ChevronDownIcon
className={cn("size-4", className)}
{...props}
/>
);
<ChevronDownIcon className={cn("size-4", className)} {...props} />
)
},
DayButton: CalendarDayButton,
WeekNumber: ({ children, ...props }) => {
@@ -175,13 +164,13 @@ function Calendar({
{children}
</div>
</td>
);
)
},
...components,
}}
{...props}
/>
);
)
}
function CalendarDayButton({
@@ -190,12 +179,12 @@ function CalendarDayButton({
modifiers,
...props
}: React.ComponentProps<typeof DayButton>) {
const defaultClassNames = getDefaultClassNames();
const defaultClassNames = getDefaultClassNames()
const ref = React.useRef<HTMLButtonElement>(null);
const ref = React.useRef<HTMLButtonElement>(null)
React.useEffect(() => {
if (modifiers.focused) ref.current?.focus();
}, [modifiers.focused]);
if (modifiers.focused) ref.current?.focus()
}, [modifiers.focused])
return (
<Button
@@ -219,7 +208,7 @@ function CalendarDayButton({
)}
{...props}
/>
);
)
}
export { Calendar, CalendarDayButton };
export { Calendar, CalendarDayButton }

View File

@@ -1,8 +1,8 @@
import { format } from "date-fns";
import { Calendar as CalendarIcon } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
import { Button } from "./button";
import { Calendar } from "./calendar";
import { Popover, PopoverContent, PopoverTrigger } from "./popover";
export function DatePicker({
date,

View File

@@ -1,24 +1,21 @@
import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import * as React from "react"
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
import { cn } from "../../lib/utils";
import { cn } from "@/lib/utils"
function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}
function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal
data-slot="dropdown-menu-portal"
{...props}
/>
);
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
)
}
function DropdownMenuTrigger({
@@ -29,7 +26,7 @@ function DropdownMenuTrigger({
data-slot="dropdown-menu-trigger"
{...props}
/>
);
)
}
function DropdownMenuContent({
@@ -49,18 +46,15 @@ function DropdownMenuContent({
{...props}
/>
</DropdownMenuPrimitive.Portal>
);
)
}
function DropdownMenuGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group
data-slot="dropdown-menu-group"
{...props}
/>
);
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
)
}
function DropdownMenuItem({
@@ -69,8 +63,8 @@ function DropdownMenuItem({
variant = "default",
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
variant?: "default" | "destructive";
inset?: boolean
variant?: "default" | "destructive"
}) {
return (
<DropdownMenuPrimitive.Item
@@ -83,7 +77,7 @@ function DropdownMenuItem({
)}
{...props}
/>
);
)
}
function DropdownMenuCheckboxItem({
@@ -109,7 +103,7 @@ function DropdownMenuCheckboxItem({
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
);
)
}
function DropdownMenuRadioGroup({
@@ -120,7 +114,7 @@ function DropdownMenuRadioGroup({
data-slot="dropdown-menu-radio-group"
{...props}
/>
);
)
}
function DropdownMenuRadioItem({
@@ -144,7 +138,7 @@ function DropdownMenuRadioItem({
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
);
)
}
function DropdownMenuLabel({
@@ -152,7 +146,7 @@ function DropdownMenuLabel({
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.Label
@@ -164,7 +158,7 @@ function DropdownMenuLabel({
)}
{...props}
/>
);
)
}
function DropdownMenuSeparator({
@@ -177,7 +171,7 @@ function DropdownMenuSeparator({
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
);
)
}
function DropdownMenuShortcut({
@@ -193,15 +187,13 @@ function DropdownMenuShortcut({
)}
{...props}
/>
);
)
}
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return (
<DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
);
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
}
function DropdownMenuSubTrigger({
@@ -210,14 +202,14 @@ function DropdownMenuSubTrigger({
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
@@ -225,7 +217,7 @@ function DropdownMenuSubTrigger({
{children}
<ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
);
)
}
function DropdownMenuSubContent({
@@ -241,7 +233,7 @@ function DropdownMenuSubContent({
)}
{...props}
/>
);
)
}
export {
@@ -260,4 +252,4 @@ export {
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
};
}

View File

@@ -0,0 +1,53 @@
import { ChevronDownIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Label } from "../../../components/ui/label";
import { useFieldContext } from "..";
import { FieldErrors } from "./FieldErrors";
type DateFieldProps = {
label: string;
};
export const DateField = ({ label }: DateFieldProps) => {
const field = useFieldContext<any>();
const [open, setOpen] = useState(false);
const date = field.state.value;
return (
<div className="grid gap-3">
<Label htmlFor={field.name}>{label}</Label>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
id="date"
className="w-48 justify-between font-normal"
>
{date ? date.toLocaleDateString() : "Select date"}
<ChevronDownIcon />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto overflow-hidden p-0" align="start">
<Calendar
mode="single"
selected={date}
captionLayout="dropdown"
startMonth={new Date(new Date().getFullYear() - 10, 0)}
endMonth={new Date(new Date().getFullYear() + 20, 0)}
onSelect={(selected) => {
field.handleChange(selected ?? undefined);
setOpen(false);
}}
/>
</PopoverContent>
</Popover>
<FieldErrors meta={field.state.meta} />
</div>
);
};

View File

@@ -1,9 +1,10 @@
import { createFormHook, createFormHookContexts } from "@tanstack/react-form";
import { SubmitButton } from "./components/SubmitButton";
import { InputField } from "./components/InputField";
import { SelectField } from "./components/SelectField";
import { DateField } from "./components/CalenderSelect";
import { CheckboxField } from "./components/CheckBox";
import { InputField } from "./components/InputField";
import { InputPasswordField } from "./components/InputPasswordField";
import { SelectField } from "./components/SelectField";
import { SubmitButton } from "./components/SubmitButton";
export const { fieldContext, useFieldContext, formContext, useFormContext } =
createFormHookContexts();
@@ -14,6 +15,7 @@ export const { useAppForm } = createFormHook({
InputPasswordField,
SelectField,
CheckboxField,
DateField,
},
formComponents: { SubmitButton },
fieldContext,

View File

@@ -0,0 +1,17 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getLeases() {
return queryOptions({
queryKey: ["getLeases"],
queryFn: () => fetch(),
staleTime: 5000,
refetchOnWindowFocus: true,
});
}
const fetch = async () => {
const { data } = await axios.get("/lst/api/forklifts/leases");
return data.data;
};

View File

@@ -0,0 +1,112 @@
import {
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
export default function TableNoExpand({
data,
columns,
}: {
data: any;
columns: any;
}) {
const [sorting, setSorting] = useState<SortingState>([]);
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
//renderSubComponent: ({ row }: { row: any }) => <ExpandedRow row={row} />,
//getRowCanExpand: () => true,
state: {
sorting,
},
});
return (
<div className="p-4">
<div className="w-fit">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<React.Fragment key={row.id}>
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
{/* {row.getIsExpanded() && (
<TableRow>
<TableCell colSpan={row.getVisibleCells().length}>
{renderSubComponent({ row })}
</TableCell>
</TableRow>
)} */}
</React.Fragment>
))}
</TableBody>
</Table>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
);
}

View File

@@ -28,6 +28,7 @@ import { Route as AppauthUserIndexRouteImport } from './routes/_app/(auth)/user/
import { Route as MobileMobileLayoutMRelocateRouteImport } from './routes/_mobile/_mobileLayout/m/relocate'
import { Route as MobileMobileLayoutMDeliveryRouteImport } from './routes/_mobile/_mobileLayout/m/delivery'
import { Route as MobileMobileLayoutMCyclecountsRouteImport } from './routes/_mobile/_mobileLayout/m/cyclecounts'
import { Route as AppForkliftsForkliftsLeasesRouteImport } from './routes/_app/_forklifts/forklifts/leases'
import { Route as AppForkliftsForkliftsCompaniesRouteImport } from './routes/_app/_forklifts/forklifts/companies'
import { Route as AppAdminLayoutAdminServersRouteImport } from './routes/_app/_adminLayout/admin/servers'
import { Route as ApplogisticsLogisticsDeliveryScheduleRouteImport } from './routes/_app/(logistics)/logistics/deliverySchedule'
@@ -145,6 +146,12 @@ const MobileMobileLayoutMCyclecountsRoute =
path: '/m/cyclecounts',
getParentRoute: () => MobileMobileLayoutRouteRoute,
} as any)
const AppForkliftsForkliftsLeasesRoute =
AppForkliftsForkliftsLeasesRouteImport.update({
id: '/forklifts/leases',
path: '/forklifts/leases',
getParentRoute: () => AppForkliftsRouteRoute,
} as any)
const AppForkliftsForkliftsCompaniesRoute =
AppForkliftsForkliftsCompaniesRouteImport.update({
id: '/forklifts/companies',
@@ -280,6 +287,7 @@ export interface FileRoutesByFullPath {
'/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
'/admin/servers': typeof AppAdminLayoutAdminServersRoute
'/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
'/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
'/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
'/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
'/m/relocate': typeof MobileMobileLayoutMRelocateRoute
@@ -314,6 +322,7 @@ export interface FileRoutesByTo {
'/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
'/admin/servers': typeof AppAdminLayoutAdminServersRoute
'/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
'/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
'/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
'/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
'/m/relocate': typeof MobileMobileLayoutMRelocateRoute
@@ -356,6 +365,7 @@ export interface FileRoutesById {
'/_app/(logistics)/logistics/deliverySchedule': typeof ApplogisticsLogisticsDeliveryScheduleRoute
'/_app/_adminLayout/admin/servers': typeof AppAdminLayoutAdminServersRoute
'/_app/_forklifts/forklifts/companies': typeof AppForkliftsForkliftsCompaniesRoute
'/_app/_forklifts/forklifts/leases': typeof AppForkliftsForkliftsLeasesRoute
'/_mobile/_mobileLayout/m/cyclecounts': typeof MobileMobileLayoutMCyclecountsRoute
'/_mobile/_mobileLayout/m/delivery': typeof MobileMobileLayoutMDeliveryRoute
'/_mobile/_mobileLayout/m/relocate': typeof MobileMobileLayoutMRelocateRoute
@@ -393,6 +403,7 @@ export interface FileRouteTypes {
| '/logistics/deliverySchedule'
| '/admin/servers'
| '/forklifts/companies'
| '/forklifts/leases'
| '/m/cyclecounts'
| '/m/delivery'
| '/m/relocate'
@@ -427,6 +438,7 @@ export interface FileRouteTypes {
| '/logistics/deliverySchedule'
| '/admin/servers'
| '/forklifts/companies'
| '/forklifts/leases'
| '/m/cyclecounts'
| '/m/delivery'
| '/m/relocate'
@@ -468,6 +480,7 @@ export interface FileRouteTypes {
| '/_app/(logistics)/logistics/deliverySchedule'
| '/_app/_adminLayout/admin/servers'
| '/_app/_forklifts/forklifts/companies'
| '/_app/_forklifts/forklifts/leases'
| '/_mobile/_mobileLayout/m/cyclecounts'
| '/_mobile/_mobileLayout/m/delivery'
| '/_mobile/_mobileLayout/m/relocate'
@@ -625,6 +638,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof MobileMobileLayoutMCyclecountsRouteImport
parentRoute: typeof MobileMobileLayoutRouteRoute
}
'/_app/_forklifts/forklifts/leases': {
id: '/_app/_forklifts/forklifts/leases'
path: '/forklifts/leases'
fullPath: '/forklifts/leases'
preLoaderRoute: typeof AppForkliftsForkliftsLeasesRouteImport
parentRoute: typeof AppForkliftsRouteRoute
}
'/_app/_forklifts/forklifts/companies': {
id: '/_app/_forklifts/forklifts/companies'
path: '/forklifts/companies'
@@ -840,11 +860,13 @@ const AppAdminLayoutRouteRouteWithChildren =
interface AppForkliftsRouteRouteChildren {
AppForkliftsForkliftsCompaniesRoute: typeof AppForkliftsForkliftsCompaniesRoute
AppForkliftsForkliftsLeasesRoute: typeof AppForkliftsForkliftsLeasesRoute
AppForkliftsForkliftsIndexRoute: typeof AppForkliftsForkliftsIndexRoute
}
const AppForkliftsRouteRouteChildren: AppForkliftsRouteRouteChildren = {
AppForkliftsForkliftsCompaniesRoute: AppForkliftsForkliftsCompaniesRoute,
AppForkliftsForkliftsLeasesRoute: AppForkliftsForkliftsLeasesRoute,
AppForkliftsForkliftsIndexRoute: AppForkliftsForkliftsIndexRoute,
}

View File

@@ -0,0 +1,128 @@
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 { getCompanies } from "@/lib/querys/forklifts/getCompanies";
import { getLeases } from "@/lib/querys/forklifts/getLeases";
import { useAppForm } from "../../../../lib/formStuff";
type CompanyData = {
id: string;
name: string;
};
export default function NewLeaseForm({
setOpenDialog,
}: {
setOpenDialog: any;
}) {
//const search = useSearch({ from: "/_app/(auth)/login" });
const { data, isLoading } = useQuery(getCompanies());
const { refetch } = useQuery(getLeases());
const form = useAppForm({
defaultValues: {
companyId: "",
leaseNumber: "",
startDate: "",
endDate: "",
},
onSubmit: async ({ value }) => {
const data = {
leaseNumber: value.leaseNumber.trimStart().trimEnd(),
startDate: format(value.startDate, "MM/dd/yyyy"),
endDate: format(value.endDate, "MM/dd/yyyy"),
companyId: value.companyId,
};
console.log(data);
try {
await axios.post("/lst/api/forklifts/leases", data);
form.reset();
setOpenDialog(false);
refetch();
toast.success(`${value.leaseNumber} was just created `);
} catch (error) {
// @ts-ignore
if (!error.response.data.success) {
// @ts-ignore
toast.error(error?.response?.data.message);
} else {
// @ts-ignore
toast.error(error?.message);
}
}
},
});
if (isLoading) return <div>Loading Companies</div>;
// remap the companies to fit out select field
const companyMap = data.map((i: CompanyData) => {
return { value: i.id, label: i.name };
});
//const currentYear = new Date().getFullYear();
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="companyId"
children={(field) => (
<field.SelectField
label="Select Company"
placeholder="Companies"
options={companyMap}
/>
)}
/>
<form.AppField
name="leaseNumber"
children={(field) => (
<field.InputField
label="Lease Number"
inputType="string"
required={false}
/>
)}
/>
<div className="flex flex-row gap-2 mt-2 mb-2">
<form.AppField
name="startDate"
children={(field) => <field.DateField label="Start Date" />}
/>
<form.AppField
name="endDate"
children={(field) => <field.DateField label="End Date" />}
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">Submit</Button>
</DialogFooter>
</form>
</>
);
}

View File

@@ -1,17 +1,9 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import {
createColumnHelper,
flexRender,
getCoreRowModel,
getPaginationRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import { createColumnHelper } from "@tanstack/react-table";
import axios from "axios";
import { Activity, ArrowDown, ArrowUp } from "lucide-react";
import React, { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -22,15 +14,9 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { getCompanies } from "@/lib/querys/forklifts/getCompanies";
import TableNoExpand from "@/lib/tableStuff/TableNoExpand";
import { cn } from "@/lib/utils";
type Company = {
@@ -64,7 +50,6 @@ function RouteComponent() {
isLoading,
refetch,
} = useQuery(getCompanies());
const [sorting, setSorting] = useState<SortingState>([]);
const columnHelper = createColumnHelper<Company>();
const submitting = useRef(false);
@@ -192,91 +177,8 @@ function RouteComponent() {
}),
];
const table = useReactTable({
data: companyData,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
//renderSubComponent: ({ row }: { row: any }) => <ExpandedRow row={row} />,
//getRowCanExpand: () => true,
state: {
sorting,
},
});
if (isLoading) {
return <div className="m-auto">Loading user data</div>;
}
return (
<div className="p-4">
<div className="w-fit">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows.map((row) => (
<React.Fragment key={row.id}>
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
{/* {row.getIsExpanded() && (
<TableRow>
<TableCell colSpan={row.getVisibleCells().length}>
{renderSubComponent({ row })}
</TableCell>
</TableRow>
)} */}
</React.Fragment>
))}
</TableBody>
</Table>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
);
return <TableNoExpand data={companyData} columns={columns} />;
}

View File

@@ -0,0 +1,124 @@
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 { useRef } from "react";
import { Button } from "@/components/ui/button";
import { getLeases } from "@/lib/querys/forklifts/getLeases";
import TableNoExpand from "@/lib/tableStuff/TableNoExpand";
type Leases = {
id: string;
leaseNumber: string;
startDate: Date;
endDate: Date;
leaseLink: string | null;
companyName: string;
add_user: string;
add_date: Date;
upd_user: string;
upd_date: Date;
};
export const Route = createFileRoute("/_app/_forklifts/forklifts/leases")({
component: RouteComponent,
});
function RouteComponent() {
const { data: leaseData = [], isLoading } = useQuery(getLeases());
const columnHelper = createColumnHelper<Leases>();
//const submitting = useRef(false);
const columns = [
columnHelper.accessor("companyName", {
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
<span className="flex flex-row gap-2">Company</span>
{column.getIsSorted() === "asc" ? (
<ArrowUp className="ml-2 h-4 w-4" />
) : (
<ArrowDown className="ml-2 h-4 w-4" />
)}
</Button>
);
},
}),
columnHelper.accessor("leaseNumber", {
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
<span className="flex flex-row gap-2">Lease Number</span>
{column.getIsSorted() === "asc" ? (
<ArrowUp className="ml-2 h-4 w-4" />
) : (
<ArrowDown className="ml-2 h-4 w-4" />
)}
</Button>
);
},
cell: ({ getValue }) => {
return <span>{getValue()}</span>;
},
}),
columnHelper.accessor("startDate", {
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>;
},
}),
columnHelper.accessor("endDate", {
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
<span className="flex flex-row gap-2">End 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={leaseData} columns={columns} />;
}

View File

@@ -0,0 +1,4 @@
ALTER TABLE "leases" ADD COLUMN "add_date" timestamp DEFAULT now();--> statement-breakpoint
ALTER TABLE "leases" ADD COLUMN "add_user" text DEFAULT 'LST';--> statement-breakpoint
ALTER TABLE "leases" ADD COLUMN "upd_date" timestamp DEFAULT now();--> statement-breakpoint
ALTER TABLE "leases" ADD COLUMN "upd_user" text DEFAULT 'LST';

View File

@@ -0,0 +1 @@
ALTER TABLE "leases" ADD CONSTRAINT "leases_lease_number_unique" UNIQUE("lease_number");

View File

@@ -0,0 +1,2 @@
ALTER TABLE "forklifts" ADD COLUMN "upd_date" timestamp DEFAULT now();--> statement-breakpoint
ALTER TABLE "forklifts" ADD COLUMN "upd_user" text DEFAULT 'LST';

View File

@@ -0,0 +1 @@
ALTER TABLE "forklifts" ADD CONSTRAINT "forklifts_serial_number_unique" UNIQUE("serial_number");

View File

@@ -0,0 +1,6 @@
ALTER TABLE "lease_invoices" DROP CONSTRAINT "lease_invoices_company_id_forklift_companies_id_fk";
--> statement-breakpoint
ALTER TABLE "lease_invoices" DROP CONSTRAINT "lease_invoices_forklift_id_forklifts_forklift_id_fk";
--> statement-breakpoint
ALTER TABLE "lease_invoices" DROP COLUMN "company_id";--> statement-breakpoint
ALTER TABLE "lease_invoices" DROP COLUMN "forklift_id";

View File

@@ -0,0 +1 @@
ALTER TABLE "lease_invoices" ADD COLUMN "add_date" timestamp;

View File

@@ -0,0 +1 @@
ALTER TABLE "lease_invoices" ADD CONSTRAINT "lease_invoices_invoice_number_unique" UNIQUE("invoice_number");

View File

@@ -0,0 +1,2 @@
ALTER TABLE "lease_invoice_forklifts" ALTER COLUMN "id" SET DATA TYPE uuid;--> statement-breakpoint
ALTER TABLE "lease_invoice_forklifts" ALTER COLUMN "id" SET DEFAULT gen_random_uuid();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -260,6 +260,62 @@
"when": 1762112909957,
"tag": "0036_sticky_brood",
"breakpoints": true
},
{
"idx": 37,
"version": "7",
"when": 1762298425546,
"tag": "0037_cold_blur",
"breakpoints": true
},
{
"idx": 38,
"version": "7",
"when": 1762298736944,
"tag": "0038_secret_luminals",
"breakpoints": true
},
{
"idx": 39,
"version": "7",
"when": 1762310353351,
"tag": "0039_strange_iron_man",
"breakpoints": true
},
{
"idx": 40,
"version": "7",
"when": 1762310431830,
"tag": "0040_keen_abomination",
"breakpoints": true
},
{
"idx": 41,
"version": "7",
"when": 1762311550798,
"tag": "0041_aspiring_chamber",
"breakpoints": true
},
{
"idx": 42,
"version": "7",
"when": 1762312885208,
"tag": "0042_true_thor_girl",
"breakpoints": true
},
{
"idx": 43,
"version": "7",
"when": 1762313144802,
"tag": "0043_thick_sebastian_shaw",
"breakpoints": true
},
{
"idx": 44,
"version": "7",
"when": 1762314701057,
"tag": "0044_melted_mole_man",
"breakpoints": true
}
]
}