Compare commits
9 Commits
7964cda197
...
817a5c6876
| Author | SHA1 | Date | |
|---|---|---|---|
| 817a5c6876 | |||
| 705f29e908 | |||
| 255ceaab85 | |||
| dbe84d5325 | |||
| 27fa45614e | |||
| 94e1198f63 | |||
| dfff8fc166 | |||
| 2023c2fc35 | |||
| da1127057c |
48
.vscode/settings copy.json.bak
vendored
Normal file
48
.vscode/settings copy.json.bak
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"workbench.colorTheme": "Default Dark+",
|
||||
"prettier.tabWidth": 4,
|
||||
"terminal.integrated.env.windows": {},
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[json]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[graphql]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[handlebars]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[go]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "golang.go"
|
||||
},
|
||||
"[powershell]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "ms-vscode.powershell" // requires PowerShell extension
|
||||
},
|
||||
"[bat]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "foxundermoon.shell-format" // supports .sh, .bat, .cmd
|
||||
},
|
||||
"[cmd]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "foxundermoon.shell-format"
|
||||
},
|
||||
|
||||
// Optional: Configure goimports instead of gofmt
|
||||
"go.formatTool": "goimports",
|
||||
"cSpell.words": ["alpla", "alplamart", "alplaprod", "ppoo"]
|
||||
}
|
||||
45
.vscode/settings.json
vendored
45
.vscode/settings.json
vendored
@@ -1,48 +1,9 @@
|
||||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"workbench.colorTheme": "Default Dark+",
|
||||
"prettier.tabWidth": 4,
|
||||
"terminal.integrated.env.windows": {},
|
||||
"editor.formatOnSave": true,
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[json]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[graphql]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[handlebars]": {
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[go]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "golang.go"
|
||||
},
|
||||
"[powershell]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "ms-vscode.powershell" // requires PowerShell extension
|
||||
},
|
||||
"[bat]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "foxundermoon.shell-format" // supports .sh, .bat, .cmd
|
||||
},
|
||||
"[cmd]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "foxundermoon.shell-format"
|
||||
},
|
||||
|
||||
// Optional: Configure goimports instead of gofmt
|
||||
"go.formatTool": "goimports",
|
||||
"editor.codeActionsOnSave": {"source.fixAll.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit" },
|
||||
"cSpell.words": ["alpla", "alplamart", "alplaprod", "ppoo"]
|
||||
}
|
||||
|
||||
21
LogisticsSupportTool_API_DOCS/LstV2/EOM/Get Consumption.bru
Normal file
21
LogisticsSupportTool_API_DOCS/LstV2/EOM/Get Consumption.bru
Normal file
@@ -0,0 +1,21 @@
|
||||
meta {
|
||||
name: Get Consumption
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{urlv2}}/api/eom/productionconsumption?startDate=9/1/2025&endDate=9/30/2025&includePlantToken=true
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
params:query {
|
||||
startDate: 9/1/2025
|
||||
endDate: 9/30/2025
|
||||
includePlantToken: true
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
}
|
||||
8
LogisticsSupportTool_API_DOCS/LstV2/EOM/folder.bru
Normal file
8
LogisticsSupportTool_API_DOCS/LstV2/EOM/folder.bru
Normal file
@@ -0,0 +1,8 @@
|
||||
meta {
|
||||
name: EOM
|
||||
seq: 4
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: inherit
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
meta {
|
||||
name: GrantROle by ID
|
||||
type: http
|
||||
seq: 8
|
||||
seq: 7
|
||||
}
|
||||
|
||||
post {
|
||||
8
LogisticsSupportTool_API_DOCS/app/admin/User/folder.bru
Normal file
8
LogisticsSupportTool_API_DOCS/app/admin/User/folder.bru
Normal file
@@ -0,0 +1,8 @@
|
||||
meta {
|
||||
name: User
|
||||
seq: 2
|
||||
}
|
||||
|
||||
auth {
|
||||
mode: inherit
|
||||
}
|
||||
@@ -16,10 +16,10 @@ headers {
|
||||
|
||||
body:json {
|
||||
{
|
||||
"name": "Bethlehem",
|
||||
"serverDNS": "USBET1VMS006",
|
||||
"plantToken": "usbet1",
|
||||
"ipAddress": "10.204.0.26",
|
||||
"name": "Bowling Green 2",
|
||||
"serverDNS": "USBOW2VMS006",
|
||||
"plantToken": "usbow2",
|
||||
"ipAddress": "10.30.0.26",
|
||||
"greatPlainsPlantCode": 0,
|
||||
"lstServerPort": 4000,
|
||||
"serverLoc": "E$\\LST"
|
||||
|
||||
@@ -14,6 +14,10 @@ params:path {
|
||||
token: usbet1
|
||||
}
|
||||
|
||||
headers {
|
||||
Cookie: {{session_cookie}}
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"zipcode": 45245
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
meta {
|
||||
name: Get user Roles
|
||||
type: http
|
||||
seq: 9
|
||||
seq: 7
|
||||
}
|
||||
|
||||
get {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
meta {
|
||||
name: Request Resetpassword
|
||||
type: http
|
||||
seq: 10
|
||||
seq: 8
|
||||
}
|
||||
|
||||
post {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
meta {
|
||||
name: Resetpassword
|
||||
type: http
|
||||
seq: 11
|
||||
seq: 9
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://localhost:4200/lst/api/auth/reset-password
|
||||
url: {{url}}/lst/api/auth/reset-password
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
vars {
|
||||
url: http://localhost:4200
|
||||
session_cookie:
|
||||
urlv2: http://usksc1vms006:3000
|
||||
urlv2: http://usiow1vms006:3001
|
||||
jwtV2:
|
||||
}
|
||||
vars:secret [
|
||||
|
||||
11
app/main.ts
11
app/main.ts
@@ -19,10 +19,13 @@ import { sendNotify } from "./src/pkg/utils/notify.js";
|
||||
import { toNodeHandler } from "better-auth/node";
|
||||
import { auth } from "./src/pkg/auth/auth.js";
|
||||
import { apiHitMiddleware } from "./src/pkg/middleware/apiHits.js";
|
||||
import { setupIoServer } from "./src/ws/server.js";
|
||||
import { schedulerManager } from "./src/internal/logistics/controller/schedulerManager.js";
|
||||
|
||||
const main = async () => {
|
||||
const env = validateEnv(process.env);
|
||||
const PORT = Number(env.VITE_PORT) || 4200;
|
||||
|
||||
//create the logger
|
||||
const log = createLogger({ module: "system", subModule: "main start" });
|
||||
|
||||
@@ -130,16 +133,18 @@ const main = async () => {
|
||||
express.static(join(__dirname, "../frontend/dist"))
|
||||
);
|
||||
|
||||
// server setup
|
||||
const server = createServer(app);
|
||||
|
||||
// register app
|
||||
setupRoutes(app, basePath);
|
||||
|
||||
// ws stuff
|
||||
|
||||
// ws + server stuff
|
||||
const server = createServer(app);
|
||||
setupIoServer(server, basePath);
|
||||
|
||||
// sub systems
|
||||
printers();
|
||||
schedulerManager();
|
||||
|
||||
// start the server up
|
||||
server.listen(PORT, "0.0.0.0", () =>
|
||||
|
||||
122
app/src/internal/logistics/controller/schedulerManager.ts
Normal file
122
app/src/internal/logistics/controller/schedulerManager.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { subMinutes } from "date-fns";
|
||||
import { format, formatInTimeZone } from "date-fns-tz";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { db } from "../../../pkg/db/db.js";
|
||||
import {
|
||||
type OrderScheduler,
|
||||
orderScheduler,
|
||||
} from "../../../pkg/db/schema/orderScheduler.js";
|
||||
import { createLogger } from "../../../pkg/logger/logger.js";
|
||||
import { prodQuery } from "../../../pkg/prodSql/prodQuery.js";
|
||||
import { scheduler } from "../../../pkg/prodSql/querys/scheduler/scheduler.js";
|
||||
import { tryCatch } from "../../../pkg/utils/tryCatch.js";
|
||||
|
||||
/*
|
||||
will monitor the incoming goods and the orders and update lst as they change or get updated.
|
||||
*/
|
||||
export const schedulerManager = async () => {
|
||||
const log = createLogger({
|
||||
module: "logistics",
|
||||
subModule: "scheduleManager",
|
||||
});
|
||||
|
||||
log.info({}, "Starting the scheduler manager up.");
|
||||
|
||||
setInterval(async () => {
|
||||
const targetTimeZone = "America/New_York";
|
||||
const now = new Date();
|
||||
|
||||
// console.log(formatInTimeZone(now, targetTimeZone, "yyyy-M-d HH:mm"));
|
||||
// console.log(format(now, "yyyy-M-d HH:mm"));
|
||||
const { data, error } = (await tryCatch(
|
||||
prodQuery(
|
||||
scheduler.replace(
|
||||
"[dateCheck]",
|
||||
formatInTimeZone(
|
||||
subMinutes(now, 1), // dealing with the 1 min difference in case we have something missed.
|
||||
targetTimeZone,
|
||||
"yyyy-M-d HH:mm",
|
||||
),
|
||||
),
|
||||
"scheduler",
|
||||
),
|
||||
)) as any;
|
||||
// do the updates to the db so we can pull the info up to the frontend
|
||||
if (error) {
|
||||
log.error({ error: error }, "there was an error getting the data");
|
||||
return;
|
||||
}
|
||||
const orderData = data.data || ([] as OrderScheduler);
|
||||
//console.log(data);
|
||||
|
||||
if (orderData.length === 0) {
|
||||
log.info({}, "There are no new orders or incoming to be updated");
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < orderData.length; i++) {
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.insert(orderScheduler)
|
||||
.values({
|
||||
av: orderData[i].av,
|
||||
description: orderData[i].description,
|
||||
orderType: orderData[i].type, //orders || incoming
|
||||
orderNumber: orderData[i].orderNumber,
|
||||
header: orderData[i].av,
|
||||
lineItemNumber: orderData[i].lineItemNumber,
|
||||
customerReleaseNumber: orderData[i].customerReleaseNumber,
|
||||
deliveryDate: new Date(orderData[i].deliveryDate),
|
||||
loadingDate: new Date(orderData[i].loadingDate),
|
||||
orderQTY: orderData[i].orderQTY,
|
||||
orderLu: orderData[i].orderLu,
|
||||
deliveredQTY: orderData[i].deliveredQTY,
|
||||
deliveredLu: orderData[i].deliveredLu,
|
||||
remark: orderData[i].remark,
|
||||
createdAsEDI: orderData[i].createdAsEDI,
|
||||
currentState: orderData[i].currentState,
|
||||
lstDateCheck: new Date(orderData[i].deliveryDate), //this will match the delivery date to start
|
||||
customerAddressId: orderData[i].customerAddressId,
|
||||
customerDescription: orderData[i].customerDescription,
|
||||
orderFrom: "prod", // manual or prod.
|
||||
// being edited change to true so it will essential lock all others from editing
|
||||
// carrier
|
||||
// carrier email. -- when dropped we can email the carrier this could be considered there confirmation.
|
||||
addDate: sql`NOW()`,
|
||||
updDate: sql`NOW()`,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: orderScheduler.orderNumber,
|
||||
set: {
|
||||
deliveryDate: new Date(orderData[i].deliveryDate),
|
||||
loadingDate: new Date(orderData[i].loadingDate),
|
||||
orderQTY: orderData[i].orderQTY,
|
||||
orderLu: orderData[i].orderLu,
|
||||
remark: orderData[i].remark,
|
||||
currentState: orderData[i].currentState,
|
||||
deliveredQTY: orderData[i].deliveredQTY,
|
||||
deliveredLu: orderData[i].deliveredLu,
|
||||
lstDateCheck: new Date(orderData[i].deliveryDate),
|
||||
updDate: sql`NOW()`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
console.log(error);
|
||||
log.error(
|
||||
{ error: error },
|
||||
`There was an error inserting/updating the order ${orderData[i].orderNumber}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
log.info(
|
||||
{ data: data },
|
||||
`${orderData[i].orderNumber} was inserted or updated`,
|
||||
);
|
||||
|
||||
//await delay
|
||||
}
|
||||
}, 60 * 1000);
|
||||
};
|
||||
16
app/src/internal/logistics/routes.ts
Normal file
16
app/src/internal/logistics/routes.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { Express, Request, Response } from "express";
|
||||
import { requireAuth } from "../../pkg/middleware/authMiddleware.js";
|
||||
import schedule from "./routes/scheduler/scheduleRoutes.js";
|
||||
|
||||
export const setupLogisticsRoutes = (app: Express, basePath: string) => {
|
||||
app.use(basePath + "/api/logistics/schedule", schedule);
|
||||
|
||||
app.use(
|
||||
basePath + "/api/admin/users",
|
||||
requireAuth("user", ["systemAdmin"]) // will pass bc system admin but this is just telling us we need this
|
||||
);
|
||||
app.use(
|
||||
basePath + "/api/admin",
|
||||
requireAuth("user", ["systemAdmin", "admin"]) // will pass bc system admin but this is just telling us we need this
|
||||
);
|
||||
};
|
||||
12
app/src/internal/logistics/routes/scheduler/getSchedule.ts
Normal file
12
app/src/internal/logistics/routes/scheduler/getSchedule.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Router } from "express";
|
||||
import type { Request, Response } from "express";
|
||||
import { schedulerChange } from "../../../../ws/channels/scheduler.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
schedulerChange({ name: "something" });
|
||||
res.status(200).json({ message: "Something " });
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Router } from "express";
|
||||
|
||||
import getSchedule from "./getSchedule.js";
|
||||
|
||||
import { restrictToHosts } from "../../../../pkg/middleware/restrictToHosts.js";
|
||||
import { requireAuth } from "../../../../pkg/middleware/authMiddleware.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.use("/", getSchedule);
|
||||
// router.use(
|
||||
// "/",
|
||||
// requireAuth("user", ["systemAdmin", "admin"]),
|
||||
// restrictToHosts([
|
||||
// "usmcd1vms036.alpla.net",
|
||||
// "USMCD1VMS036.alpla.net",
|
||||
// "https://usmcd1vms036.alpla.net",
|
||||
// ]),
|
||||
// addServer
|
||||
// );
|
||||
// router.use(
|
||||
// "/",
|
||||
// requireAuth("user", ["systemAdmin", "admin"]),
|
||||
// restrictToHosts([
|
||||
// "usmcd1vms036.alpla.net",
|
||||
// "USMCD1VMS036.alpla.net",
|
||||
// "https://usmcd1vms036.alpla.net",
|
||||
// ]),
|
||||
// updateServer
|
||||
// );
|
||||
|
||||
export default router;
|
||||
@@ -2,12 +2,14 @@ import type { Express, Request, Response } from "express";
|
||||
import { setupAuthRoutes } from "../auth/routes/routes.js";
|
||||
import { setupAdminRoutes } from "../admin/routes.js";
|
||||
import { setupSystemRoutes } from "../system/routes.js";
|
||||
import { setupLogisticsRoutes } from "../logistics/routes.js";
|
||||
|
||||
export const setupRoutes = (app: Express, basePath: string) => {
|
||||
// all routes
|
||||
setupAuthRoutes(app, basePath);
|
||||
setupAdminRoutes(app, basePath);
|
||||
setupSystemRoutes(app, basePath);
|
||||
setupLogisticsRoutes(app, basePath);
|
||||
|
||||
// always try to go to the app weather we are in dev or in production.
|
||||
app.get(basePath + "/", (req: Request, res: Response) => {
|
||||
|
||||
51
app/src/pkg/db/schema/orderScheduler.ts
Normal file
51
app/src/pkg/db/schema/orderScheduler.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
boolean,
|
||||
date,
|
||||
integer,
|
||||
pgTable,
|
||||
real,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import type z from "zod";
|
||||
|
||||
export const orderScheduler = pgTable(
|
||||
"orderScheduler",
|
||||
{
|
||||
schedule_id: uuid("schedule_id").defaultRandom().primaryKey(),
|
||||
av: integer("av"),
|
||||
description: text("description"),
|
||||
orderType: text("order_type").notNull(), //orders || incoming
|
||||
orderNumber: integer("order_number").notNull(),
|
||||
header: text("header").notNull(),
|
||||
lineItemNumber: text("line_item_number"),
|
||||
customerReleaseNumber: text("customer_release_number"),
|
||||
deliveryDate: timestamp("delivery_date").notNull(),
|
||||
loadingDate: timestamp("loading_date"),
|
||||
orderQTY: real("order_qty").notNull(),
|
||||
orderLu: real("order_lu").notNull(),
|
||||
deliveredQTY: real("delivered_qty").default(0.0),
|
||||
deliveredLu: real("delivered_lu").default(0.0),
|
||||
remark: text("remark"),
|
||||
createdAsEDI: boolean("created_as_EDI"),
|
||||
currentState: integer("current_state"),
|
||||
lstDateCheck: timestamp("lst_date_check"), //this will match the delivery date to start and when moved in the front end run a function to alert or update via api
|
||||
customerAddressId: integer("customer_address_id"),
|
||||
customerDescription: text("customer_description"),
|
||||
dock: text("dock"),
|
||||
orderFrom: text("order_from"), // manual or prod.
|
||||
// being edited change to true so it will essential lock all others from editing
|
||||
// carrier
|
||||
// carrier email. -- when dropped we can email the carrier this could be considered there confirmation.
|
||||
addDate: timestamp("add_date").defaultNow(),
|
||||
updDate: timestamp("upd_date").defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
uniqueIndex("orderNumber").on(table.orderNumber),
|
||||
],
|
||||
);
|
||||
|
||||
export type OrderScheduler = z.infer<typeof orderScheduler>;
|
||||
@@ -1,42 +1,42 @@
|
||||
import {
|
||||
text,
|
||||
pgTable,
|
||||
timestamp,
|
||||
uuid,
|
||||
uniqueIndex,
|
||||
jsonb,
|
||||
boolean,
|
||||
boolean,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
export const settings = pgTable(
|
||||
"settings",
|
||||
{
|
||||
settings_id: uuid("settings_id").defaultRandom().primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
value: text("value").notNull(), // this is used in junction with active, only needed if the setting isnt a bool
|
||||
description: text("description"),
|
||||
moduleName: text("moduleName"), // what part of lst dose it belong to this is used to split the settings out later
|
||||
active: boolean("active").default(true),
|
||||
roles: jsonb("roles").notNull().default(["systemAdmin"]), // role or roles to see this goes along with the moduleName, need to have a x role in module to see this setting.
|
||||
add_User: text("add_User").default("LST_System").notNull(),
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
upd_user: text("upd_User").default("LST_System").notNull(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
uniqueIndex("name").on(table.name),
|
||||
]
|
||||
"settings",
|
||||
{
|
||||
settings_id: uuid("settings_id").defaultRandom().primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
value: text("value").notNull(), // this is used in junction with active, only needed if the setting isnt a bool
|
||||
description: text("description"),
|
||||
moduleName: text("moduleName"), // what part of lst dose it belong to this is used to split the settings out later
|
||||
active: boolean("active").default(true),
|
||||
roles: jsonb("roles").notNull().default(["systemAdmin"]), // role or roles to see this goes along with the moduleName, need to have a x role in module to see this setting.
|
||||
add_User: text("add_User").default("LST_System").notNull(),
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
upd_user: text("upd_User").default("LST_System").notNull(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
uniqueIndex("name").on(table.name),
|
||||
],
|
||||
);
|
||||
|
||||
export const settingSchema = createSelectSchema(settings);
|
||||
export const newSettingSchema = createInsertSchema(settings, {
|
||||
name: z.string().min(3, {
|
||||
message: "The name of the setting must be longer than 3 letters",
|
||||
}),
|
||||
name: z.string().min(3, {
|
||||
message: "The name of the setting must be longer than 3 letters",
|
||||
}),
|
||||
});
|
||||
|
||||
export type Setting = z.infer<typeof settingSchema>;
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import build from "pino-abstract-transport";
|
||||
import { db } from "../db/db.js";
|
||||
import { logs, type Log } from "../db/schema/logs.js";
|
||||
import { type Log, logs } from "../db/schema/logs.js";
|
||||
import { tryCatch } from "../utils/tryCatch.js";
|
||||
|
||||
const pinoLogLevels: any = {
|
||||
10: "trace",
|
||||
20: "debug",
|
||||
30: "info",
|
||||
40: "warn",
|
||||
50: "error",
|
||||
60: "fatal",
|
||||
10: "trace",
|
||||
20: "debug",
|
||||
30: "info",
|
||||
40: "warn",
|
||||
50: "error",
|
||||
60: "fatal",
|
||||
};
|
||||
// Create a custom transport function
|
||||
export default async function (log: Log) {
|
||||
//const {username, service, level, msg, ...extra} = log;
|
||||
try {
|
||||
return build(async function (source) {
|
||||
for await (let obj of source) {
|
||||
// convert to the name to make it more easy to find later :P
|
||||
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||
//const {username, service, level, msg, ...extra} = log;
|
||||
try {
|
||||
return build(async (source) => {
|
||||
for await (const obj of source) {
|
||||
// convert to the name to make it more easy to find later :P
|
||||
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||
|
||||
const res = await tryCatch(
|
||||
db.insert(logs).values({
|
||||
level: levelName,
|
||||
module: obj?.module?.toLowerCase(),
|
||||
subModule: obj?.subModule?.toLowerCase(),
|
||||
hostname: obj?.hostname?.toLowerCase(),
|
||||
message: obj.msg,
|
||||
stack: obj?.stack,
|
||||
})
|
||||
);
|
||||
const res = await tryCatch(
|
||||
db.insert(logs).values({
|
||||
level: levelName,
|
||||
module: obj?.module?.toLowerCase(),
|
||||
subModule: obj?.subModule?.toLowerCase(),
|
||||
hostname: obj?.hostname?.toLowerCase(),
|
||||
message: obj.msg,
|
||||
stack: obj?.stack,
|
||||
}),
|
||||
);
|
||||
|
||||
if (res.error) {
|
||||
console.log(res.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error inserting log into database:", err);
|
||||
}
|
||||
if (res.error) {
|
||||
console.log(res.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error inserting log into database:", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import pino, { type Logger } from "pino";
|
||||
|
||||
export let logLevel = process.env.LOG_LEVEL || "info";
|
||||
export const logLevel = process.env.LOG_LEVEL || "info";
|
||||
|
||||
const transport = pino.transport({
|
||||
targets: [
|
||||
{
|
||||
target: "pino-pretty",
|
||||
options: {
|
||||
colorize: true,
|
||||
singleLine: true,
|
||||
// customPrettifiers: {
|
||||
// time: (time) => `🕰 ${time}`,
|
||||
// },
|
||||
destination: process.stdout.fd,
|
||||
},
|
||||
},
|
||||
{
|
||||
target: "./dbTransport.js",
|
||||
},
|
||||
{
|
||||
target: "./notification.js",
|
||||
},
|
||||
// Only log to Go if LST_USE_GO=true
|
||||
...(process.env.LST_USE_GO === "true"
|
||||
? [
|
||||
{
|
||||
target: "./goTransport.js", // New transport for Go
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
targets: [
|
||||
{
|
||||
target: "pino-pretty",
|
||||
options: {
|
||||
colorize: true,
|
||||
singleLine: true,
|
||||
// customPrettifiers: {
|
||||
// time: (time) => `🕰 ${time}`,
|
||||
// },
|
||||
destination: process.stdout.fd,
|
||||
},
|
||||
},
|
||||
{
|
||||
target: "./dbTransport.js",
|
||||
},
|
||||
{
|
||||
target: "./notification.js",
|
||||
},
|
||||
// Only log to Go if LST_USE_GO=true
|
||||
...(process.env.LST_USE_GO === "true"
|
||||
? [
|
||||
{
|
||||
target: "./goTransport.js", // New transport for Go
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
});
|
||||
|
||||
export const rootLogger: Logger = pino(
|
||||
{
|
||||
level: logLevel,
|
||||
redact: { paths: ["email", "password"], remove: true },
|
||||
},
|
||||
transport
|
||||
{
|
||||
level: logLevel,
|
||||
redact: { paths: ["email", "password"], remove: true },
|
||||
},
|
||||
transport,
|
||||
);
|
||||
|
||||
/**
|
||||
* factory to create child to log things for us
|
||||
*/
|
||||
export function createLogger(bindings: Record<string, unknown>): Logger {
|
||||
return rootLogger.child(bindings);
|
||||
return rootLogger.child(bindings);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
import build from "pino-abstract-transport";
|
||||
import { type Log } from "../db/schema/logs.js";
|
||||
import type { Log } from "../db/schema/logs.js";
|
||||
import { validateEnv } from "../utils/envValidator.js";
|
||||
import { sendNotify } from "../utils/notify.js";
|
||||
|
||||
const env = validateEnv(process.env);
|
||||
|
||||
const pinoLogLevels: any = {
|
||||
10: "trace",
|
||||
20: "debug",
|
||||
30: "info",
|
||||
40: "warn",
|
||||
50: "error",
|
||||
60: "fatal",
|
||||
10: "trace",
|
||||
20: "debug",
|
||||
30: "info",
|
||||
40: "warn",
|
||||
50: "error",
|
||||
60: "fatal",
|
||||
};
|
||||
// discord function
|
||||
|
||||
export default async function (log: Log) {
|
||||
//const {username, service, level, msg, ...extra} = log;
|
||||
try {
|
||||
return build(async function (source) {
|
||||
for await (let obj of source) {
|
||||
// convert to the name to make it more easy to find later :P
|
||||
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||
//const {username, service, level, msg, ...extra} = log;
|
||||
try {
|
||||
return build(async (source) => {
|
||||
for await (const obj of source) {
|
||||
// convert to the name to make it more easy to find later :P
|
||||
const levelName = pinoLogLevels[obj.level] || "unknown";
|
||||
|
||||
const newlog = {
|
||||
level: levelName,
|
||||
module: obj.module
|
||||
? String(obj.module).toLowerCase()
|
||||
: undefined,
|
||||
subModule: obj.subModule
|
||||
? String(obj.subModule).toLowerCase()
|
||||
: undefined,
|
||||
hostname: obj.hostname
|
||||
? String(obj.hostname).toLowerCase()
|
||||
: undefined,
|
||||
message: obj.msg,
|
||||
stack: obj.stack ? obj.stack : undefined,
|
||||
};
|
||||
if (!process.env.WEBHOOK_URL) {
|
||||
console.log("WebHook is missing we wont move foward.");
|
||||
return;
|
||||
}
|
||||
const newlog = {
|
||||
level: levelName,
|
||||
module: obj.module ? String(obj.module).toLowerCase() : undefined,
|
||||
subModule: obj.subModule
|
||||
? String(obj.subModule).toLowerCase()
|
||||
: undefined,
|
||||
hostname: obj.hostname
|
||||
? String(obj.hostname).toLowerCase()
|
||||
: undefined,
|
||||
message: obj.msg,
|
||||
stack: obj.stack ? obj.stack : undefined,
|
||||
};
|
||||
if (!process.env.WEBHOOK_URL) {
|
||||
console.log("WebHook is missing we wont move foward.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj.level >= 60 && obj.notify) {
|
||||
sendNotify(newlog as Log);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error inserting log into database:", err);
|
||||
}
|
||||
if (obj.level >= 60 && obj.notify) {
|
||||
sendNotify(newlog as Log);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Error inserting log into database:", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,16 +23,15 @@ export async function prodQuery(queryToRun: string, name: string) {
|
||||
});
|
||||
}
|
||||
const query = queryToRun.replaceAll("test1", env.PROD_PLANT_TOKEN);
|
||||
|
||||
try {
|
||||
const result = await pool.request().query(query);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Query results for: ${name}`,
|
||||
data: result.recordset,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.log(error);
|
||||
if (error.code === "ETIMEOUT") {
|
||||
return returnFunc({
|
||||
success: false,
|
||||
@@ -51,7 +50,7 @@ export async function prodQuery(queryToRun: string, name: string) {
|
||||
module: "prodSql",
|
||||
subModule: "query",
|
||||
level: "error",
|
||||
message: `${name} encoutnered an error ${error.originalError.info.message}`,
|
||||
message: `${name} encountered an error ${error.originalError.info.message}`,
|
||||
data: [],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const initializeProdPool = async () => {
|
||||
});
|
||||
}
|
||||
try {
|
||||
pool = sql.connect(sqlConfig);
|
||||
pool = await sql.connect(sqlConfig);
|
||||
|
||||
log.info(
|
||||
`Connected to ${sqlConfig?.server}, using DB: ${sqlConfig?.database}`
|
||||
|
||||
109
app/src/pkg/prodSql/querys/scheduler/scheduler.ts
Normal file
109
app/src/pkg/prodSql/querys/scheduler/scheduler.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export const scheduler = `
|
||||
use AlplaPROD_test1
|
||||
/*
|
||||
This query will combine both the incoming goods and the deliveries in 1 as it will be the query that populates into lst db then later updated
|
||||
*/
|
||||
DECLARE @checkDateString NVARCHAR(MAX) = '[dateCheck]' --'2025-10-14 08:00'; -- will be passed over to validate new updates.
|
||||
DECLARE @checkDate DATETIME2 = CONVERT(datetime2, @checkDateString);
|
||||
|
||||
-- orders
|
||||
select * from (select
|
||||
type = 'orders'
|
||||
,l.ArticleHumanReadableId as av
|
||||
,l.ArticleDescription as description
|
||||
,[ReleaseNumber] as orderNumber
|
||||
,h.CustomerOrderNumber as header
|
||||
,l.CustomerLineItemNumber as lineItemNumber
|
||||
,CustomerReleaseNumber as customerReleaseNumber
|
||||
,FORMAT(r.[Deliverydate], 'yyyy-MM-dd HH:mm') as deliveryDate
|
||||
,FORMAT([LoadingDate], 'yyyy-MM-dd HH:mm') as loadingDate
|
||||
,[Quantity] as orderQTY
|
||||
,[LoadingUnits] as orderLu
|
||||
,case when t.GelieferteMenge is null then 0 else t.GelieferteMenge end as deliveredQTY
|
||||
,case when t.GelieferteMengeVPK is null then 0 else t.GelieferteMengeVPK end as deliveredLu
|
||||
,r.Remark as remark
|
||||
,h.CreatedByEdi as createdAsEDI -- if 1 then we run the new function to change this in true edi as well as lstdb.
|
||||
,r.ReleaseState as currentState -- anything other than 0 should lock this and not allow for changes
|
||||
--,lstDate = getdate() --'this is a place holder for date see edi comment'
|
||||
--,dock = 'dock3' --another place holder for what dock we will go to'
|
||||
--,orderType = 'maunal or prod' -- this is for if we manually add in the order from lst. this way the dates will be ignored if this is manual.
|
||||
,h.CustomerHumanReadableId as addressId
|
||||
,h.CustomerDescription as customer
|
||||
,orderType = 20
|
||||
,r.[Add_User]
|
||||
,r.[Add_Date]
|
||||
,r.[Upd_User]
|
||||
,r.[Upd_Date]
|
||||
FROM [test1_AlplaPROD2.0_Reporting].[reporting_order].[Release] as r
|
||||
|
||||
left join
|
||||
[test1_AlplaPROD2.0_Reporting].[reporting_order].LineItem as l on
|
||||
l.id = r.LineItemId
|
||||
|
||||
left join
|
||||
[test1_AlplaPROD2.0_Read].[order].Header as h on
|
||||
h.Id = l.HeaderId
|
||||
|
||||
left join
|
||||
dbo.V_TrackerAuftragsAbrufe (nolock) as t on
|
||||
t.IdAuftragsAbruf = r.ReleaseNumber
|
||||
|
||||
--where r.[Upd_Date] >= getdate() -@age
|
||||
where CAST(r.[Upd_Date] AS datetime2) >= @checkDate
|
||||
|
||||
|
||||
union all
|
||||
-- incoming goods
|
||||
select
|
||||
type = 'incoming'
|
||||
,inc.IdArtikelVarianten as av
|
||||
,av.Bezeichnung as description
|
||||
,IdBestellung as orderNumber
|
||||
,case when inc.Bemerkung <> '' then case when CHARINDEX(',',inc.Bemerkung) = 0 then inc.Bemerkung else left(inc.Bemerkung, CHARINDEX(',',inc.Bemerkung) -1) end else 'Missing PO' end as header
|
||||
,case when inc.Bemerkung <> '' then case when CHARINDEX(',',inc.Bemerkung) = 0 then inc.Bemerkung else left(inc.Bemerkung, CHARINDEX(',',inc.Bemerkung) -1) end else 'Missing PO' end as lineItemNumber
|
||||
,case when inc.Bemerkung <> '' then case when CHARINDEX(',',inc.Bemerkung) = 0 then inc.Bemerkung else left(inc.Bemerkung, CHARINDEX(',',inc.Bemerkung) -1) end else 'Missing PO' end as customerReleaseNumber
|
||||
,FORMAT(inc.Datum, 'yyyy-MM-dd HH:mm') as deliveryDate
|
||||
,FORMAT(inc.Datum, 'yyyy-MM-dd HH:mm') as loadingDate
|
||||
,sollMenge as orderQty
|
||||
,sollmengevpk as orderLu
|
||||
,case when l.EntladeMenge is null then 0 else l.EntladeMenge end as deliveredQTY
|
||||
,case when l.EntladeMengeVPK is null then 0 else l.EntladeMengeVPK end as deliveredLu
|
||||
,inc.Bemerkung as remark
|
||||
,createdAsEDI = 0
|
||||
,inc.Status as currentState -- anything other than 0 should lock this and not allow for changes
|
||||
--,lstDate = getdate() --'this is a place holder for date see edi comment'
|
||||
--,dock = 'dock3' --another place holder for what dock we will go to'
|
||||
--,orderType = 'maunal or prod' -- this is for if we manually add in the order from lst. this way the dates will be ignored if this is manual.
|
||||
,inc.IdAdresse as addressId
|
||||
,a.Bezeichnung
|
||||
,inc.Typ as orderType -- this is just the id
|
||||
,inc.Add_User
|
||||
,inc.Add_Date
|
||||
,inc.Upd_User
|
||||
,inc.Upd_Date
|
||||
from T_Wareneingaenge (nolock) as inc
|
||||
|
||||
--article stuff
|
||||
left join
|
||||
T_Artikelvarianten (nolock) as av on
|
||||
av.IdArtikelvarianten = inc.IdArtikelVarianten
|
||||
|
||||
left join
|
||||
T_WareneingangAuftraege (nolock) as w on
|
||||
w.Beleg = inc.Beleg
|
||||
|
||||
left join
|
||||
T_WareneingangPlanungen (nolock) as l on
|
||||
l.IdWareneingangAuftrag = w.IdWareneingangAuftrag
|
||||
|
||||
left join
|
||||
T_adressen as a on
|
||||
a.idadressen = inc.IdAdresse
|
||||
|
||||
--where inc.Upd_Date >= getdate() -@age
|
||||
where inc.Upd_Date >= @checkDate
|
||||
--and inc.Typ not in (40)
|
||||
)a
|
||||
|
||||
order by upd_date desc
|
||||
`;
|
||||
15
app/src/pkg/utils/corsController.ts
Normal file
15
app/src/pkg/utils/corsController.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { validateEnv } from "./envValidator.js";
|
||||
|
||||
const env = validateEnv(process.env);
|
||||
// export const allowedOrigins = [
|
||||
// /^https?:\/\/localhost:(5173|5500|4200|3000|4000)$/, // all the allowed backend ports
|
||||
// /^http?:\/\/localhost:(5173|5500|4200|3000|4000)$/,
|
||||
// /^https?:\/\/.*\.alpla\.net$/,
|
||||
// env.BETTER_AUTH_URL, // prod
|
||||
// ];
|
||||
|
||||
export const allowedOrigins: (string | RegExp)[] = [
|
||||
/^https?:\/\/localhost:(5173|5500|4200|3000|4000)$/, // all local dev ports
|
||||
/^https?:\/\/.*\.alpla\.net$/, // any subdomain of alpla.net
|
||||
env.BETTER_AUTH_URL, // production URL
|
||||
];
|
||||
0
app/src/ws/channels/labelLogs.ts
Normal file
0
app/src/ws/channels/labelLogs.ts
Normal file
43
app/src/ws/channels/logs.ts
Normal file
43
app/src/ws/channels/logs.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Namespace, Socket } from "socket.io";
|
||||
import { requireAuth } from "../../pkg/middleware/authMiddleware.js";
|
||||
|
||||
export const setupAllLogs = (io: Namespace) => {
|
||||
// Wrap middleware for Socket.IO
|
||||
io.use(async (socket: Socket, next) => {
|
||||
try {
|
||||
// Mock Express req/res object
|
||||
const req = socket.request as any;
|
||||
|
||||
// Create a mini next function that throws an error if unauthorized
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const fakeRes: any = {
|
||||
status: () => ({
|
||||
json: (obj: any) => reject(new Error(obj.error)),
|
||||
}),
|
||||
};
|
||||
const nextFn = (err?: any) => (err ? reject(err) : resolve());
|
||||
|
||||
// Call your middleware
|
||||
requireAuth("", ["systemAdmin", "admin"])(req, fakeRes, nextFn);
|
||||
});
|
||||
|
||||
// Auth passed
|
||||
next();
|
||||
} catch (err: any) {
|
||||
next(new Error(err.message || "Unauthorized"));
|
||||
}
|
||||
});
|
||||
|
||||
io.on("connection", (socket: Socket) => {
|
||||
console.log(
|
||||
"✅ Authenticated client connected to channel2:",
|
||||
socket.id
|
||||
);
|
||||
|
||||
socket.on("ping", () => socket.emit("pong"));
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log("🔴 Client disconnected from channel2:", socket.id);
|
||||
});
|
||||
});
|
||||
};
|
||||
59
app/src/ws/channels/scheduler.ts
Normal file
59
app/src/ws/channels/scheduler.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { formatDate } from "date-fns";
|
||||
import type { Server, Socket } from "socket.io";
|
||||
import { db } from "../../pkg/db/db.js";
|
||||
import { orderScheduler } from "../../pkg/db/schema/orderScheduler.js";
|
||||
import { tryCatch } from "../../pkg/utils/tryCatch.js";
|
||||
|
||||
let ioInstance: Server | null = null;
|
||||
|
||||
export const setupScheduler = (io: Server, socket: Socket) => {
|
||||
ioInstance = io;
|
||||
|
||||
socket.on("joinScheduler", async () => {
|
||||
socket.join("scheduler");
|
||||
//console.log(`Socket ${socket.id} joined room "scheduler"`);
|
||||
|
||||
const { data, error } = await tryCatch(db.select().from(orderScheduler));
|
||||
|
||||
if (error) {
|
||||
setTimeout(() => {
|
||||
io.to("scheduler").emit("scheduler:update", {
|
||||
type: "init",
|
||||
error: error,
|
||||
});
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
// initial push
|
||||
setTimeout(() => {
|
||||
io.to("scheduler").emit("scheduler:update", { type: "init", data: data });
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// socket.on("ping", () => {
|
||||
// io.to("scheduler").emit("pong", { from: socket.id });
|
||||
// });
|
||||
|
||||
// setInterval(() => {
|
||||
// io.to("scheduler").emit("heartbeat", {
|
||||
// ts: formatDate(Date.now(), "M/d/yyyy HH:mm"),
|
||||
// });
|
||||
// }, 60_000);
|
||||
|
||||
// add in listen for changes to the db with pg listen?
|
||||
};
|
||||
|
||||
// --- Change Broadcast Function ---
|
||||
export const schedulerChange = async (data: any) => {
|
||||
if (!ioInstance) {
|
||||
console.error("⚠️ schedulerChange called before setupScheduler initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
ioInstance.to("scheduler").emit("scheduler:update", {
|
||||
ts: formatDate(Date.now(), "M/d/yyyy HH:mm"),
|
||||
data,
|
||||
});
|
||||
console.log("📢 Scheduler update broadcasted:", data);
|
||||
};
|
||||
66
app/src/ws/server.ts
Normal file
66
app/src/ws/server.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Server } from "socket.io";
|
||||
import type { Server as HttpServer } from "http";
|
||||
import { allowedOrigins } from "../pkg/utils/corsController.js";
|
||||
import { setupScheduler } from "./channels/scheduler.js";
|
||||
|
||||
export const setupIoServer = async (server: HttpServer, basePath: string) => {
|
||||
const io = new Server(server, {
|
||||
path: `${basePath}/api/ws`,
|
||||
cors: {
|
||||
// origin: ["http://localhost:5500"],
|
||||
origin: (origin, callback) => {
|
||||
// Allow non-browser clients (Postman, direct websocket, etc.)
|
||||
if (!origin) return callback(null, true);
|
||||
|
||||
try {
|
||||
// Check if origin matches any allowed string or regex
|
||||
const allowed = allowedOrigins.some((o) => {
|
||||
if (typeof o === "string") return o === origin;
|
||||
if (o instanceof RegExp) return o.test(origin);
|
||||
return false;
|
||||
});
|
||||
|
||||
if (allowed) {
|
||||
return callback(null, true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Invalid Origin header:", origin);
|
||||
}
|
||||
|
||||
// Deny all others
|
||||
return callback(new Error("Not allowed by CORS: " + origin));
|
||||
},
|
||||
// methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
||||
credentials: true,
|
||||
},
|
||||
pingInterval: 25_000, // send ping every 25s (default 25s)
|
||||
pingTimeout: 60_000, // wait 60s before disconnecting (default 60s)
|
||||
});
|
||||
|
||||
io.on("connection", (socket) => {
|
||||
console.log("🟢 User connected:", socket.id);
|
||||
|
||||
// Join a room
|
||||
socket.on("joinRoom", (room: string) => {
|
||||
socket.join(room);
|
||||
console.log(`Socket ${socket.id} joined room ${room}`);
|
||||
});
|
||||
|
||||
// Leave a room
|
||||
socket.on("leaveRoom", (room: string) => {
|
||||
socket.leave(room);
|
||||
console.log(`Socket ${socket.id} left room ${room}`);
|
||||
});
|
||||
|
||||
// Example: broadcast a message to a room
|
||||
socket.on("message", ({ room, payload }) => {
|
||||
io.to(room).emit("message", payload);
|
||||
});
|
||||
|
||||
socket.on("disconnect", (reason) => {
|
||||
console.log("🔴 User disconnected:", socket.id, reason);
|
||||
});
|
||||
|
||||
setupScheduler(io, socket);
|
||||
});
|
||||
};
|
||||
45
biome.json
Normal file
45
biome.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.6/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "main"
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab"
|
||||
},
|
||||
"linter": {
|
||||
"enabled": false,
|
||||
"rules": {
|
||||
"suspicious": {
|
||||
"noConsole": {
|
||||
"options": {
|
||||
"allow": ["error", "info"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"correctness": {
|
||||
"useJsxKeyInIterable": "error"
|
||||
}
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
"source": {
|
||||
"recommended": true,
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/(logistics)/logistics/deliverySchedule')(
|
||||
{
|
||||
component: RouteComponent,
|
||||
},
|
||||
)
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(logistics)/logistics/deliverySchedule"!</div>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/(mobileStuff)/_mobileLayout/')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(mobileStuff)/_mobileLayout/"!</div>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/(mobileStuff)/_mobileLayout/m/')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(mobileStuff)/_mobileLayout/m/"!</div>
|
||||
}
|
||||
407
frontend/package-lock.json
generated
407
frontend/package-lock.json
generated
@@ -8,11 +8,14 @@
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@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",
|
||||
@@ -22,14 +25,22 @@
|
||||
"@tanstack/react-query": "^5.89.0",
|
||||
"@tanstack/react-router": "^1.131.36",
|
||||
"@tanstack/react-router-devtools": "^1.131.36",
|
||||
"@types/react-calendar-timeline": "^0.28.6",
|
||||
"axios": "^1.12.2",
|
||||
"better-auth": "^1.3.11",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"is-mobile": "^5.0.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.542.0",
|
||||
"moment": "^2.30.1",
|
||||
"r": "^0.0.5",
|
||||
"react": "^19.1.1",
|
||||
"react-calendar-timeline": "^0.30.0-beta.3",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.13",
|
||||
@@ -527,6 +538,51 @@
|
||||
"resolved": "https://registry.npmjs.org/@better-fetch/fetch/-/fetch-1.1.18.tgz",
|
||||
"integrity": "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="
|
||||
},
|
||||
"node_modules/@date-fns/tz": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.4.1.tgz",
|
||||
"integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/utilities": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
|
||||
@@ -1207,6 +1263,13 @@
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@interactjs/types": {
|
||||
"version": "1.10.27",
|
||||
"resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.27.tgz",
|
||||
"integrity": "sha512-BUdv0cvs4H5ODuwft2Xp4eL8Vmi3LcihK42z0Ft/FbVJZoRioBsxH+LlsBdK4tAie7PqlKGy+1oyOncu1nQ6eA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
@@ -1864,6 +1927,43 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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",
|
||||
"integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||
"@radix-ui/react-focus-guards": "1.1.3",
|
||||
"@radix-ui/react-focus-scope": "1.1.7",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-popper": "1.2.8",
|
||||
"@radix-ui/react-portal": "1.1.9",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"aria-hidden": "^1.2.4",
|
||||
"react-remove-scroll": "^2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"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",
|
||||
@@ -1998,6 +2098,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-scroll-area": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz",
|
||||
"integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/number": "1.1.1",
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-select": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz",
|
||||
@@ -2591,6 +2722,12 @@
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/core": {
|
||||
"version": "1.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz",
|
||||
@@ -3444,12 +3581,21 @@
|
||||
"version": "19.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz",
|
||||
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-calendar-timeline": {
|
||||
"version": "0.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-calendar-timeline/-/react-calendar-timeline-0.28.6.tgz",
|
||||
"integrity": "sha512-43ttmxoxfi9IdMBjU+My8Aa5ArX0fq2DCOJSol6Bt/fbRKHS/fEmIP/GGeXYavpt+vmCjeToHHLPTw/EXqqAFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"moment": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-dom": {
|
||||
"version": "19.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.8.tgz",
|
||||
@@ -3898,6 +4044,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/batch-processor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz",
|
||||
"integrity": "sha512-xoLQD8gmmR32MeuBHgH0Tzd5PuSZx71ZsbhVxOCRbgktZEPe4SQy7s9Z50uPp0F/f7iw2XmkHN2xkgbMfckMDA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/better-auth": {
|
||||
"version": "1.3.11",
|
||||
"resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.3.11.tgz",
|
||||
@@ -4165,6 +4317,12 @@
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
@@ -4247,6 +4405,29 @@
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns-jalali": {
|
||||
"version": "4.1.0-0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz",
|
||||
"integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.18",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz",
|
||||
"integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
@@ -4345,6 +4526,54 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/element-resize-detector": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz",
|
||||
"integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"batch-processor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
|
||||
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
@@ -5058,6 +5287,16 @@
|
||||
"node": ">=0.8.19"
|
||||
}
|
||||
},
|
||||
"node_modules/interactjs": {
|
||||
"version": "1.10.27",
|
||||
"resolved": "https://registry.npmjs.org/interactjs/-/interactjs-1.10.27.tgz",
|
||||
"integrity": "sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@interactjs/types": "1.10.27"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@@ -5094,6 +5333,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-mobile": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz",
|
||||
"integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
@@ -5491,6 +5736,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -5535,6 +5786,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@@ -5629,11 +5886,19 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
@@ -5904,6 +6169,14 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/r": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/r/-/r-0.0.5.tgz",
|
||||
"integrity": "sha512-rO+FXCKIUiLEVCP4HjIEkq8V5gRHc1N15/2K97GSOE/P5evYj6uOOgvHlINCwMHnEmHouBdeR6PBDzZDqmEH2A==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
|
||||
@@ -5913,6 +6186,45 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-calendar-timeline": {
|
||||
"version": "0.30.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/react-calendar-timeline/-/react-calendar-timeline-0.30.0-beta.3.tgz",
|
||||
"integrity": "sha512-TckfoAzJvK5FEQo83vejbVtSDX1XNxcFmfCq92lMZKQiEuzbk7adcQi+ySlL9uSZ1ulFZS6YMAS6rzEGsVtZ2A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"classnames": "^2.5.1",
|
||||
"element-resize-detector": "^1.2.4",
|
||||
"lodash": "^4.17.21",
|
||||
"memoize-one": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"dayjs": ">=1.10.0",
|
||||
"interactjs": "1.10.27",
|
||||
"react": "^18 || ^19.0.0-rc-66855b96-20241106",
|
||||
"react-dom": "^18 || ^19.0.0-rc-66855b96-20241106"
|
||||
}
|
||||
},
|
||||
"node_modules/react-day-picker": {
|
||||
"version": "9.11.1",
|
||||
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.11.1.tgz",
|
||||
"integrity": "sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@date-fns/tz": "^1.4.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-jalali": "^4.1.0-0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/gpbl"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
|
||||
@@ -6209,6 +6521,68 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/solid-js": {
|
||||
"version": "1.9.9",
|
||||
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.9.tgz",
|
||||
@@ -6802,6 +7176,35 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
|
||||
@@ -10,11 +10,14 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@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",
|
||||
@@ -24,14 +27,22 @@
|
||||
"@tanstack/react-query": "^5.89.0",
|
||||
"@tanstack/react-router": "^1.131.36",
|
||||
"@tanstack/react-router-devtools": "^1.131.36",
|
||||
"@types/react-calendar-timeline": "^0.28.6",
|
||||
"axios": "^1.12.2",
|
||||
"better-auth": "^1.3.11",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"is-mobile": "^5.0.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.542.0",
|
||||
"moment": "^2.30.1",
|
||||
"r": "^0.0.5",
|
||||
"react": "^19.1.1",
|
||||
"react-calendar-timeline": "^0.30.0-beta.3",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.13",
|
||||
|
||||
225
frontend/src/components/ui/calendar.tsx
Normal file
225
frontend/src/components/ui/calendar.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
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";
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
showOutsideDays = true,
|
||||
captionLayout = "label",
|
||||
buttonVariant = "ghost",
|
||||
formatters,
|
||||
components,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayPicker> & {
|
||||
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
||||
}) {
|
||||
const defaultClassNames = getDefaultClassNames();
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn(
|
||||
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
||||
className
|
||||
)}
|
||||
captionLayout={captionLayout}
|
||||
formatters={{
|
||||
formatMonthDropdown: (date) =>
|
||||
date.toLocaleString("default", { month: "short" }),
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
root: cn("w-fit", defaultClassNames.root),
|
||||
months: cn(
|
||||
"flex gap-4 flex-col md:flex-row relative",
|
||||
defaultClassNames.months
|
||||
),
|
||||
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
|
||||
),
|
||||
button_previous: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
||||
defaultClassNames.button_previous
|
||||
),
|
||||
button_next: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
|
||||
defaultClassNames.button_next
|
||||
),
|
||||
month_caption: cn(
|
||||
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
|
||||
defaultClassNames.month_caption
|
||||
),
|
||||
dropdowns: cn(
|
||||
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
|
||||
defaultClassNames.dropdowns
|
||||
),
|
||||
dropdown_root: cn(
|
||||
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
|
||||
defaultClassNames.dropdown_root
|
||||
),
|
||||
dropdown: cn(
|
||||
"absolute bg-popover inset-0 opacity-0",
|
||||
defaultClassNames.dropdown
|
||||
),
|
||||
caption_label: cn(
|
||||
"select-none font-medium",
|
||||
captionLayout === "label"
|
||||
? "text-sm"
|
||||
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
|
||||
defaultClassNames.caption_label
|
||||
),
|
||||
table: "w-full border-collapse",
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
|
||||
defaultClassNames.weekday
|
||||
),
|
||||
week: cn("flex w-full mt-2", defaultClassNames.week),
|
||||
week_number_header: cn(
|
||||
"select-none w-(--cell-size)",
|
||||
defaultClassNames.week_number_header
|
||||
),
|
||||
week_number: cn(
|
||||
"text-[0.8rem] select-none text-muted-foreground",
|
||||
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",
|
||||
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
|
||||
),
|
||||
today: cn(
|
||||
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
|
||||
defaultClassNames.today
|
||||
),
|
||||
outside: cn(
|
||||
"text-muted-foreground aria-selected:text-muted-foreground",
|
||||
defaultClassNames.outside
|
||||
),
|
||||
disabled: cn(
|
||||
"text-muted-foreground opacity-50",
|
||||
defaultClassNames.disabled
|
||||
),
|
||||
hidden: cn("invisible", defaultClassNames.hidden),
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
Root: ({ className, rootRef, ...props }) => {
|
||||
return (
|
||||
<div
|
||||
data-slot="calendar"
|
||||
ref={rootRef}
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
Chevron: ({ className, orientation, ...props }) => {
|
||||
if (orientation === "left") {
|
||||
return (
|
||||
<ChevronLeftIcon
|
||||
className={cn("size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (orientation === "right") {
|
||||
return (
|
||||
<ChevronRightIcon
|
||||
className={cn("size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ChevronDownIcon
|
||||
className={cn("size-4", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
DayButton: CalendarDayButton,
|
||||
WeekNumber: ({ children, ...props }) => {
|
||||
return (
|
||||
<td {...props}>
|
||||
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
},
|
||||
...components,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CalendarDayButton({
|
||||
className,
|
||||
day,
|
||||
modifiers,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayButton>) {
|
||||
const defaultClassNames = getDefaultClassNames();
|
||||
|
||||
const ref = React.useRef<HTMLButtonElement>(null);
|
||||
React.useEffect(() => {
|
||||
if (modifiers.focused) ref.current?.focus();
|
||||
}, [modifiers.focused]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
ref={ref}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
data-day={day.date.toLocaleDateString()}
|
||||
data-selected-single={
|
||||
modifiers.selected &&
|
||||
!modifiers.range_start &&
|
||||
!modifiers.range_end &&
|
||||
!modifiers.range_middle
|
||||
}
|
||||
data-range-start={modifiers.range_start}
|
||||
data-range-end={modifiers.range_end}
|
||||
data-range-middle={modifiers.range_middle}
|
||||
className={cn(
|
||||
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||
defaultClassNames.day,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Calendar, CalendarDayButton };
|
||||
31
frontend/src/components/ui/datePicker.tsx
Normal file
31
frontend/src/components/ui/datePicker.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
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";
|
||||
|
||||
export function DatePicker({
|
||||
date,
|
||||
onChange,
|
||||
}: {
|
||||
date?: Date;
|
||||
onChange?: (d: Date | undefined) => void;
|
||||
}) {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
data-empty={!date}
|
||||
className="data-[empty=true]:text-muted-foreground w-[200px] justify-start text-left font-normal"
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar mode="single" selected={date} onSelect={onChange} />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
45
frontend/src/components/ui/popover.tsx
Normal file
45
frontend/src/components/ui/popover.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import * as React from "react";
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
function Popover({
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
|
||||
return <PopoverPrimitive.Root data-slot="popover" {...props} />;
|
||||
}
|
||||
|
||||
function PopoverTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
|
||||
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
|
||||
}
|
||||
|
||||
function PopoverContent({
|
||||
className,
|
||||
align = "center",
|
||||
sideOffset = 4,
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
|
||||
return (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
data-slot="popover-content"
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
);
|
||||
}
|
||||
|
||||
function PopoverAnchor({
|
||||
...props
|
||||
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
|
||||
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
|
||||
}
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
||||
56
frontend/src/components/ui/scroll-area.tsx
Normal file
56
frontend/src/components/ui/scroll-area.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function ScrollArea({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.Root
|
||||
data-slot="scroll-area"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
data-slot="scroll-area-viewport"
|
||||
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function ScrollBar({
|
||||
className,
|
||||
orientation = "vertical",
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
data-slot="scroll-area-scrollbar"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none p-px transition-colors select-none",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
data-slot="scroll-area-thumb"
|
||||
className="bg-border relative flex-1 rounded-full"
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
)
|
||||
}
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
@@ -4,117 +4,117 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
59
frontend/src/lib/socket.io/socket.tsx
Normal file
59
frontend/src/lib/socket.io/socket.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { io } from "socket.io-client";
|
||||
|
||||
export const coreSocket = io(
|
||||
window.location.origin || "http://localhost:3000",
|
||||
{
|
||||
path: "/lst/api/ws",
|
||||
autoConnect: true,
|
||||
// transports: ["websocket", "polling"],
|
||||
withCredentials: true,
|
||||
reconnectionAttempts: 5,
|
||||
timeout: 10_000,
|
||||
}
|
||||
);
|
||||
|
||||
// --- Core events ---
|
||||
coreSocket.on("connect", () => {
|
||||
console.log("✅ Core connected:", coreSocket.id);
|
||||
console.log("🔗 Transport:", coreSocket.io.engine.transport.name);
|
||||
});
|
||||
|
||||
coreSocket.on("disconnect", (reason) => {
|
||||
console.warn("🔴 Core disconnected:", reason);
|
||||
if (reason === "io server disconnect") coreSocket.connect();
|
||||
});
|
||||
|
||||
coreSocket.on("connect_error", (err) => {
|
||||
console.error("❌ Core connect error:", err.message);
|
||||
});
|
||||
|
||||
coreSocket.io.on("reconnect_attempt", (attempt) => {
|
||||
console.log("♻️ Core reconnect attempt:", attempt);
|
||||
});
|
||||
|
||||
coreSocket.io.on("reconnect_failed", () => {
|
||||
console.error("💀 Core reconnect failed after max attempts");
|
||||
});
|
||||
|
||||
coreSocket.io.engine.on("upgrade", (transport) => {
|
||||
console.log("🚀 Core upgraded to:", transport.name);
|
||||
});
|
||||
|
||||
coreSocket.io.engine.on("close", (reason) => {
|
||||
console.warn("🧨 Core engine closed:", reason);
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
//console.log(window.location.origin);
|
||||
if (coreSocket.connected) {
|
||||
coreSocket.emit("keepalive");
|
||||
console.log("🏓 Ping sent to server");
|
||||
} else {
|
||||
//console.warn("⚠️ Socket not connected, skipping ping");
|
||||
}
|
||||
}, 30 * 1000);
|
||||
|
||||
// Optional: listen for server acknowledgment
|
||||
coreSocket.on("pongCheck", (data) => {
|
||||
console.log("🏓 Pong received from server:", data);
|
||||
});
|
||||
@@ -14,16 +14,27 @@ import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as AdminLayoutRouteRouteImport } from './routes/_adminLayout/route'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as authLoginRouteImport } from './routes/(auth)/login'
|
||||
import { Route as mobileStuffMobileLayoutRouteRouteImport } from './routes/(mobileStuff)/_mobileLayout/route'
|
||||
import { Route as AdminLayoutAdminSettingsRouteImport } from './routes/_adminLayout/admin/settings'
|
||||
import { Route as AdminLayoutAdminServersRouteImport } from './routes/_adminLayout/admin/servers'
|
||||
import { Route as logisticsLogisticsDeliveryScheduleRouteImport } from './routes/(logistics)/logistics/deliverySchedule'
|
||||
import { Route as authUserSignupRouteImport } from './routes/(auth)/user/signup'
|
||||
import { Route as authUserResetpasswordRouteImport } from './routes/(auth)/user/resetpassword'
|
||||
import { Route as AdminLayoutAdminUsersRouteRouteImport } from './routes/_adminLayout/admin/_users/route'
|
||||
import { Route as mobileStuffMobileLayoutMIndexRouteImport } from './routes/(mobileStuff)/_mobileLayout/m/index'
|
||||
import { Route as AdminLayoutAdminUsersUsersRouteImport } from './routes/_adminLayout/admin/_users/users'
|
||||
import { Route as AdminLayoutAdminUsersProdUsersRouteImport } from './routes/_adminLayout/admin/_users/prodUsers'
|
||||
import { Route as mobileStuffMobileLayoutMRelocateRouteImport } from './routes/(mobileStuff)/_mobileLayout/m/relocate'
|
||||
import { Route as mobileStuffMobileLayoutMDeliveryRouteImport } from './routes/(mobileStuff)/_mobileLayout/m/delivery'
|
||||
import { Route as mobileStuffMobileLayoutMCyclecountsRouteImport } from './routes/(mobileStuff)/_mobileLayout/m/cyclecounts'
|
||||
|
||||
const mobileStuffRouteImport = createFileRoute('/(mobileStuff)')()
|
||||
const AdminLayoutAdminRouteImport = createFileRoute('/_adminLayout/admin')()
|
||||
|
||||
const mobileStuffRoute = mobileStuffRouteImport.update({
|
||||
id: '/(mobileStuff)',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AdminLayoutRouteRoute = AdminLayoutRouteRouteImport.update({
|
||||
id: '/_adminLayout',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
@@ -43,6 +54,11 @@ const authLoginRoute = authLoginRouteImport.update({
|
||||
path: '/login',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const mobileStuffMobileLayoutRouteRoute =
|
||||
mobileStuffMobileLayoutRouteRouteImport.update({
|
||||
id: '/_mobileLayout',
|
||||
getParentRoute: () => mobileStuffRoute,
|
||||
} as any)
|
||||
const AdminLayoutAdminSettingsRoute =
|
||||
AdminLayoutAdminSettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
@@ -54,6 +70,12 @@ const AdminLayoutAdminServersRoute = AdminLayoutAdminServersRouteImport.update({
|
||||
path: '/servers',
|
||||
getParentRoute: () => AdminLayoutAdminRoute,
|
||||
} as any)
|
||||
const logisticsLogisticsDeliveryScheduleRoute =
|
||||
logisticsLogisticsDeliveryScheduleRouteImport.update({
|
||||
id: '/(logistics)/logistics/deliverySchedule',
|
||||
path: '/logistics/deliverySchedule',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const authUserSignupRoute = authUserSignupRouteImport.update({
|
||||
id: '/(auth)/user/signup',
|
||||
path: '/user/signup',
|
||||
@@ -69,6 +91,12 @@ const AdminLayoutAdminUsersRouteRoute =
|
||||
id: '/_users',
|
||||
getParentRoute: () => AdminLayoutAdminRoute,
|
||||
} as any)
|
||||
const mobileStuffMobileLayoutMIndexRoute =
|
||||
mobileStuffMobileLayoutMIndexRouteImport.update({
|
||||
id: '/m/',
|
||||
path: '/m/',
|
||||
getParentRoute: () => mobileStuffMobileLayoutRouteRoute,
|
||||
} as any)
|
||||
const AdminLayoutAdminUsersUsersRoute =
|
||||
AdminLayoutAdminUsersUsersRouteImport.update({
|
||||
id: '/users',
|
||||
@@ -81,42 +109,77 @@ const AdminLayoutAdminUsersProdUsersRoute =
|
||||
path: '/prodUsers',
|
||||
getParentRoute: () => AdminLayoutAdminUsersRouteRoute,
|
||||
} as any)
|
||||
const mobileStuffMobileLayoutMRelocateRoute =
|
||||
mobileStuffMobileLayoutMRelocateRouteImport.update({
|
||||
id: '/m/relocate',
|
||||
path: '/m/relocate',
|
||||
getParentRoute: () => mobileStuffMobileLayoutRouteRoute,
|
||||
} as any)
|
||||
const mobileStuffMobileLayoutMDeliveryRoute =
|
||||
mobileStuffMobileLayoutMDeliveryRouteImport.update({
|
||||
id: '/m/delivery',
|
||||
path: '/m/delivery',
|
||||
getParentRoute: () => mobileStuffMobileLayoutRouteRoute,
|
||||
} as any)
|
||||
const mobileStuffMobileLayoutMCyclecountsRoute =
|
||||
mobileStuffMobileLayoutMCyclecountsRouteImport.update({
|
||||
id: '/m/cyclecounts',
|
||||
path: '/m/cyclecounts',
|
||||
getParentRoute: () => mobileStuffMobileLayoutRouteRoute,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/': typeof mobileStuffMobileLayoutRouteRouteWithChildren
|
||||
'/login': typeof authLoginRoute
|
||||
'/admin': typeof AdminLayoutAdminUsersRouteRouteWithChildren
|
||||
'/user/resetpassword': typeof authUserResetpasswordRoute
|
||||
'/user/signup': typeof authUserSignupRoute
|
||||
'/logistics/deliverySchedule': typeof logisticsLogisticsDeliveryScheduleRoute
|
||||
'/admin/servers': typeof AdminLayoutAdminServersRoute
|
||||
'/admin/settings': typeof AdminLayoutAdminSettingsRoute
|
||||
'/m/cyclecounts': typeof mobileStuffMobileLayoutMCyclecountsRoute
|
||||
'/m/delivery': typeof mobileStuffMobileLayoutMDeliveryRoute
|
||||
'/m/relocate': typeof mobileStuffMobileLayoutMRelocateRoute
|
||||
'/admin/prodUsers': typeof AdminLayoutAdminUsersProdUsersRoute
|
||||
'/admin/users': typeof AdminLayoutAdminUsersUsersRoute
|
||||
'/m': typeof mobileStuffMobileLayoutMIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/': typeof mobileStuffMobileLayoutRouteRouteWithChildren
|
||||
'/login': typeof authLoginRoute
|
||||
'/admin': typeof AdminLayoutAdminUsersRouteRouteWithChildren
|
||||
'/user/resetpassword': typeof authUserResetpasswordRoute
|
||||
'/user/signup': typeof authUserSignupRoute
|
||||
'/logistics/deliverySchedule': typeof logisticsLogisticsDeliveryScheduleRoute
|
||||
'/admin/servers': typeof AdminLayoutAdminServersRoute
|
||||
'/admin/settings': typeof AdminLayoutAdminSettingsRoute
|
||||
'/m/cyclecounts': typeof mobileStuffMobileLayoutMCyclecountsRoute
|
||||
'/m/delivery': typeof mobileStuffMobileLayoutMDeliveryRoute
|
||||
'/m/relocate': typeof mobileStuffMobileLayoutMRelocateRoute
|
||||
'/admin/prodUsers': typeof AdminLayoutAdminUsersProdUsersRoute
|
||||
'/admin/users': typeof AdminLayoutAdminUsersUsersRoute
|
||||
'/m': typeof mobileStuffMobileLayoutMIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
'/_adminLayout': typeof AdminLayoutRouteRouteWithChildren
|
||||
'/(mobileStuff)': typeof mobileStuffRouteWithChildren
|
||||
'/(mobileStuff)/_mobileLayout': typeof mobileStuffMobileLayoutRouteRouteWithChildren
|
||||
'/(auth)/login': typeof authLoginRoute
|
||||
'/_adminLayout/admin': typeof AdminLayoutAdminRouteWithChildren
|
||||
'/_adminLayout/admin/_users': typeof AdminLayoutAdminUsersRouteRouteWithChildren
|
||||
'/(auth)/user/resetpassword': typeof authUserResetpasswordRoute
|
||||
'/(auth)/user/signup': typeof authUserSignupRoute
|
||||
'/(logistics)/logistics/deliverySchedule': typeof logisticsLogisticsDeliveryScheduleRoute
|
||||
'/_adminLayout/admin/servers': typeof AdminLayoutAdminServersRoute
|
||||
'/_adminLayout/admin/settings': typeof AdminLayoutAdminSettingsRoute
|
||||
'/(mobileStuff)/_mobileLayout/m/cyclecounts': typeof mobileStuffMobileLayoutMCyclecountsRoute
|
||||
'/(mobileStuff)/_mobileLayout/m/delivery': typeof mobileStuffMobileLayoutMDeliveryRoute
|
||||
'/(mobileStuff)/_mobileLayout/m/relocate': typeof mobileStuffMobileLayoutMRelocateRoute
|
||||
'/_adminLayout/admin/_users/prodUsers': typeof AdminLayoutAdminUsersProdUsersRoute
|
||||
'/_adminLayout/admin/_users/users': typeof AdminLayoutAdminUsersUsersRoute
|
||||
'/(mobileStuff)/_mobileLayout/m/': typeof mobileStuffMobileLayoutMIndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
@@ -126,10 +189,15 @@ export interface FileRouteTypes {
|
||||
| '/admin'
|
||||
| '/user/resetpassword'
|
||||
| '/user/signup'
|
||||
| '/logistics/deliverySchedule'
|
||||
| '/admin/servers'
|
||||
| '/admin/settings'
|
||||
| '/m/cyclecounts'
|
||||
| '/m/delivery'
|
||||
| '/m/relocate'
|
||||
| '/admin/prodUsers'
|
||||
| '/admin/users'
|
||||
| '/m'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
@@ -137,35 +205,56 @@ export interface FileRouteTypes {
|
||||
| '/admin'
|
||||
| '/user/resetpassword'
|
||||
| '/user/signup'
|
||||
| '/logistics/deliverySchedule'
|
||||
| '/admin/servers'
|
||||
| '/admin/settings'
|
||||
| '/m/cyclecounts'
|
||||
| '/m/delivery'
|
||||
| '/m/relocate'
|
||||
| '/admin/prodUsers'
|
||||
| '/admin/users'
|
||||
| '/m'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/_adminLayout'
|
||||
| '/(mobileStuff)'
|
||||
| '/(mobileStuff)/_mobileLayout'
|
||||
| '/(auth)/login'
|
||||
| '/_adminLayout/admin'
|
||||
| '/_adminLayout/admin/_users'
|
||||
| '/(auth)/user/resetpassword'
|
||||
| '/(auth)/user/signup'
|
||||
| '/(logistics)/logistics/deliverySchedule'
|
||||
| '/_adminLayout/admin/servers'
|
||||
| '/_adminLayout/admin/settings'
|
||||
| '/(mobileStuff)/_mobileLayout/m/cyclecounts'
|
||||
| '/(mobileStuff)/_mobileLayout/m/delivery'
|
||||
| '/(mobileStuff)/_mobileLayout/m/relocate'
|
||||
| '/_adminLayout/admin/_users/prodUsers'
|
||||
| '/_adminLayout/admin/_users/users'
|
||||
| '/(mobileStuff)/_mobileLayout/m/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
AdminLayoutRouteRoute: typeof AdminLayoutRouteRouteWithChildren
|
||||
mobileStuffRoute: typeof mobileStuffRouteWithChildren
|
||||
authLoginRoute: typeof authLoginRoute
|
||||
authUserResetpasswordRoute: typeof authUserResetpasswordRoute
|
||||
authUserSignupRoute: typeof authUserSignupRoute
|
||||
logisticsLogisticsDeliveryScheduleRoute: typeof logisticsLogisticsDeliveryScheduleRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/(mobileStuff)': {
|
||||
id: '/(mobileStuff)'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof mobileStuffRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/_adminLayout': {
|
||||
id: '/_adminLayout'
|
||||
path: ''
|
||||
@@ -194,6 +283,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof authLoginRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/(mobileStuff)/_mobileLayout': {
|
||||
id: '/(mobileStuff)/_mobileLayout'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof mobileStuffMobileLayoutRouteRouteImport
|
||||
parentRoute: typeof mobileStuffRoute
|
||||
}
|
||||
'/_adminLayout/admin/settings': {
|
||||
id: '/_adminLayout/admin/settings'
|
||||
path: '/settings'
|
||||
@@ -208,6 +304,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof AdminLayoutAdminServersRouteImport
|
||||
parentRoute: typeof AdminLayoutAdminRoute
|
||||
}
|
||||
'/(logistics)/logistics/deliverySchedule': {
|
||||
id: '/(logistics)/logistics/deliverySchedule'
|
||||
path: '/logistics/deliverySchedule'
|
||||
fullPath: '/logistics/deliverySchedule'
|
||||
preLoaderRoute: typeof logisticsLogisticsDeliveryScheduleRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/(auth)/user/signup': {
|
||||
id: '/(auth)/user/signup'
|
||||
path: '/user/signup'
|
||||
@@ -229,6 +332,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof AdminLayoutAdminUsersRouteRouteImport
|
||||
parentRoute: typeof AdminLayoutAdminRoute
|
||||
}
|
||||
'/(mobileStuff)/_mobileLayout/m/': {
|
||||
id: '/(mobileStuff)/_mobileLayout/m/'
|
||||
path: '/m'
|
||||
fullPath: '/m'
|
||||
preLoaderRoute: typeof mobileStuffMobileLayoutMIndexRouteImport
|
||||
parentRoute: typeof mobileStuffMobileLayoutRouteRoute
|
||||
}
|
||||
'/_adminLayout/admin/_users/users': {
|
||||
id: '/_adminLayout/admin/_users/users'
|
||||
path: '/users'
|
||||
@@ -243,6 +353,27 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof AdminLayoutAdminUsersProdUsersRouteImport
|
||||
parentRoute: typeof AdminLayoutAdminUsersRouteRoute
|
||||
}
|
||||
'/(mobileStuff)/_mobileLayout/m/relocate': {
|
||||
id: '/(mobileStuff)/_mobileLayout/m/relocate'
|
||||
path: '/m/relocate'
|
||||
fullPath: '/m/relocate'
|
||||
preLoaderRoute: typeof mobileStuffMobileLayoutMRelocateRouteImport
|
||||
parentRoute: typeof mobileStuffMobileLayoutRouteRoute
|
||||
}
|
||||
'/(mobileStuff)/_mobileLayout/m/delivery': {
|
||||
id: '/(mobileStuff)/_mobileLayout/m/delivery'
|
||||
path: '/m/delivery'
|
||||
fullPath: '/m/delivery'
|
||||
preLoaderRoute: typeof mobileStuffMobileLayoutMDeliveryRouteImport
|
||||
parentRoute: typeof mobileStuffMobileLayoutRouteRoute
|
||||
}
|
||||
'/(mobileStuff)/_mobileLayout/m/cyclecounts': {
|
||||
id: '/(mobileStuff)/_mobileLayout/m/cyclecounts'
|
||||
path: '/m/cyclecounts'
|
||||
fullPath: '/m/cyclecounts'
|
||||
preLoaderRoute: typeof mobileStuffMobileLayoutMCyclecountsRouteImport
|
||||
parentRoute: typeof mobileStuffMobileLayoutRouteRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,12 +419,51 @@ const AdminLayoutRouteRouteChildren: AdminLayoutRouteRouteChildren = {
|
||||
const AdminLayoutRouteRouteWithChildren =
|
||||
AdminLayoutRouteRoute._addFileChildren(AdminLayoutRouteRouteChildren)
|
||||
|
||||
interface mobileStuffMobileLayoutRouteRouteChildren {
|
||||
mobileStuffMobileLayoutMCyclecountsRoute: typeof mobileStuffMobileLayoutMCyclecountsRoute
|
||||
mobileStuffMobileLayoutMDeliveryRoute: typeof mobileStuffMobileLayoutMDeliveryRoute
|
||||
mobileStuffMobileLayoutMRelocateRoute: typeof mobileStuffMobileLayoutMRelocateRoute
|
||||
mobileStuffMobileLayoutMIndexRoute: typeof mobileStuffMobileLayoutMIndexRoute
|
||||
}
|
||||
|
||||
const mobileStuffMobileLayoutRouteRouteChildren: mobileStuffMobileLayoutRouteRouteChildren =
|
||||
{
|
||||
mobileStuffMobileLayoutMCyclecountsRoute:
|
||||
mobileStuffMobileLayoutMCyclecountsRoute,
|
||||
mobileStuffMobileLayoutMDeliveryRoute:
|
||||
mobileStuffMobileLayoutMDeliveryRoute,
|
||||
mobileStuffMobileLayoutMRelocateRoute:
|
||||
mobileStuffMobileLayoutMRelocateRoute,
|
||||
mobileStuffMobileLayoutMIndexRoute: mobileStuffMobileLayoutMIndexRoute,
|
||||
}
|
||||
|
||||
const mobileStuffMobileLayoutRouteRouteWithChildren =
|
||||
mobileStuffMobileLayoutRouteRoute._addFileChildren(
|
||||
mobileStuffMobileLayoutRouteRouteChildren,
|
||||
)
|
||||
|
||||
interface mobileStuffRouteChildren {
|
||||
mobileStuffMobileLayoutRouteRoute: typeof mobileStuffMobileLayoutRouteRouteWithChildren
|
||||
}
|
||||
|
||||
const mobileStuffRouteChildren: mobileStuffRouteChildren = {
|
||||
mobileStuffMobileLayoutRouteRoute:
|
||||
mobileStuffMobileLayoutRouteRouteWithChildren,
|
||||
}
|
||||
|
||||
const mobileStuffRouteWithChildren = mobileStuffRoute._addFileChildren(
|
||||
mobileStuffRouteChildren,
|
||||
)
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AdminLayoutRouteRoute: AdminLayoutRouteRouteWithChildren,
|
||||
mobileStuffRoute: mobileStuffRouteWithChildren,
|
||||
authLoginRoute: authLoginRoute,
|
||||
authUserResetpasswordRoute: authUserResetpasswordRoute,
|
||||
authUserSignupRoute: authUserSignupRoute,
|
||||
logisticsLogisticsDeliveryScheduleRoute:
|
||||
logisticsLogisticsDeliveryScheduleRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
|
||||
24
frontend/src/routes/(logistics)/-components/DragCell.tsx
Normal file
24
frontend/src/routes/(logistics)/-components/DragCell.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
// src/routes/traffic/DropCell.tsx
|
||||
import { useDroppable } from "@dnd-kit/core";
|
||||
|
||||
export function DropCell({ day, hour, children }: any) {
|
||||
const { setNodeRef, isOver } = useDroppable({
|
||||
id: `${day.toDateString()}-${hour}`,
|
||||
data: { day, hour },
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
className={`relative border h-20 p-1 ${
|
||||
isOver ? "bg-blue-400" : ""
|
||||
} overflow-visible`}
|
||||
style={{
|
||||
maxWidth: 340, // your allotted width
|
||||
alignContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
69
frontend/src/routes/(logistics)/-components/Grid.tsx
Normal file
69
frontend/src/routes/(logistics)/-components/Grid.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
// src/routes/traffic/Grid.tsx
|
||||
import { format } from "date-fns";
|
||||
import React from "react";
|
||||
import { ScrollArea, ScrollBar } from "../../../components/ui/scroll-area";
|
||||
|
||||
export const days = Array.from(
|
||||
{ length: 5 },
|
||||
(_, i) => new Date(Date.now() + i * 24 * 60 * 60 * 1000),
|
||||
);
|
||||
|
||||
// the layout of the hours
|
||||
const hoursBefore = 3;
|
||||
const totalHours = 24;
|
||||
|
||||
// get the current hour
|
||||
const currentHour = new Date().getHours();
|
||||
const startHour = (currentHour - hoursBefore + 24) % 24;
|
||||
|
||||
// generate the hours array
|
||||
const hours = Array.from(
|
||||
{ length: totalHours },
|
||||
(_, i) => (startHour + i) % 24,
|
||||
);
|
||||
|
||||
export function Grid({
|
||||
days,
|
||||
children,
|
||||
}: {
|
||||
days: any;
|
||||
children?: (day: Date, hour: number) => React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<ScrollArea className={`h-[80vh]`}>
|
||||
<div
|
||||
className="grid"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `100px repeat(${days.length}, 350px)`, // each day = 180 px wide
|
||||
//minWidth: `${100 + days.length * 350}px`,
|
||||
}}
|
||||
>
|
||||
{/* Empty top-left corner */}
|
||||
<div className="sticky top-0 left-0 bg-background z-30"></div>
|
||||
|
||||
{/* Date headers */}
|
||||
{days.map((d: any) => (
|
||||
<div
|
||||
key={d.toDateString()}
|
||||
className="sticky top-0 bg-background z-20 p-2 font-semibold text-center"
|
||||
>
|
||||
{format(d, "EEEE M/d/yyyy")}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{hours.map((hour) => (
|
||||
<React.Fragment key={hour}>
|
||||
<div className="border p-1 text-right text-sm">{hour}:00</div>
|
||||
{days.map((d: any) => (
|
||||
<div key={`${d}-${hour}`} className="relative border h-20">
|
||||
{children && children(d, hour)}
|
||||
</div>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
);
|
||||
}
|
||||
34
frontend/src/routes/(logistics)/-components/GridBody.tsx
Normal file
34
frontend/src/routes/(logistics)/-components/GridBody.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
|
||||
// GridBody.tsx
|
||||
export function GridBody({ days, children }: { days: Date[]; children: any }) {
|
||||
const hours = Array.from({ length: 24 }, (_, i) => i);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="grid overflow-x-auto"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `100px repeat(${days.length},340px)`,
|
||||
}}
|
||||
>
|
||||
{hours.map((hour) => (
|
||||
<React.Fragment key={hour}>
|
||||
{/* time label, sticky left */}
|
||||
<div className="sticky left-0 bg-background border p-1 text-right text-sm z-10">
|
||||
{hour}:00
|
||||
</div>
|
||||
|
||||
{days.map((day) => (
|
||||
<div
|
||||
key={`${day}-${hour}`}
|
||||
className="border h-20 relative"
|
||||
>
|
||||
{children && children(day, hour)}
|
||||
</div>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
frontend/src/routes/(logistics)/-components/GridHeader.tsx
Normal file
24
frontend/src/routes/(logistics)/-components/GridHeader.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
// GridHeader.tsx
|
||||
import { format } from "date-fns";
|
||||
|
||||
export function GridHeader({ days }: { days: Date[] }) {
|
||||
return (
|
||||
<div
|
||||
className="grid sticky top-0 z-30 bg-background"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `100px repeat(${days.length},340px)`,
|
||||
}}
|
||||
>
|
||||
<div /> {/* Empty corner for time labels */}
|
||||
{days.map((d) => (
|
||||
<div
|
||||
key={d.toDateString()}
|
||||
className="p-2 font-semibold text-center border-b"
|
||||
>
|
||||
{format(d, "EEE M/d/yyyy")}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
44
frontend/src/routes/(logistics)/-components/Shipments.tsx
Normal file
44
frontend/src/routes/(logistics)/-components/Shipments.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
// ShipmentItem.tsx
|
||||
import { useDraggable } from "@dnd-kit/core";
|
||||
|
||||
interface ShipmentItemProps {
|
||||
shipment: any;
|
||||
index?: number;
|
||||
perm?: boolean;
|
||||
}
|
||||
|
||||
export function ShipmentItem({
|
||||
shipment,
|
||||
index = 0,
|
||||
perm = true,
|
||||
}: ShipmentItemProps) {
|
||||
const { setNodeRef, listeners, attributes, transform } = useDraggable({
|
||||
id: shipment.orderNumber,
|
||||
data: shipment,
|
||||
});
|
||||
|
||||
const offsetX = index * 10;
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
transform: transform
|
||||
? `translate(${transform.x}px, ${transform.y}px)`
|
||||
: `translateX(${offsetX}px)`,
|
||||
transition: transform ? "none" : "transform 0.2s ease",
|
||||
zIndex: 10 + index,
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
cursor: "grab",
|
||||
};
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
{...listeners}
|
||||
{...attributes}
|
||||
style={style}
|
||||
className="w-[160px] p-2 text-xs rounded shadow select-none "
|
||||
>
|
||||
{shipment.orderNumber}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
125
frontend/src/routes/(logistics)/-components/style.css
Normal file
125
frontend/src/routes/(logistics)/-components/style.css
Normal file
@@ -0,0 +1,125 @@
|
||||
.react-calendar-timeline * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.react-calendar-timeline .rct-outer {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.react-calendar-timeline .rct-scroll {
|
||||
display: inline-block;
|
||||
white-space: normal;
|
||||
vertical-align: top;
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.react-calendar-timeline .rct-item:hover {
|
||||
z-index: 88;
|
||||
}
|
||||
|
||||
.react-calendar-timeline .rct-item .rct-item-content {
|
||||
position: sticky;
|
||||
position: -webkit-sticky; /* still fine — helps with drag display */
|
||||
left: 0;
|
||||
|
||||
/* Let multiple lines render */
|
||||
display: block;
|
||||
white-space: normal; /* ⬅ allow wrapping */
|
||||
overflow: visible; /* ⬅ don't clip longer content */
|
||||
height: auto; /* ⬅ expand with content */
|
||||
padding: 4px 6px; /* ⬅ spacing inside item */
|
||||
line-height: 1.3; /* ⬅ readable multi-line spacing */
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box; /* consistent height/padding */
|
||||
}
|
||||
|
||||
.react-calendar-timeline .rct-sidebar {
|
||||
overflow: hidden;
|
||||
white-space: normal;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
border-right: 1px solid #bbb;
|
||||
}
|
||||
.react-calendar-timeline .rct-sidebar.rct-sidebar-right {
|
||||
border-right: 0;
|
||||
border-left: 1px solid #bbb;
|
||||
}
|
||||
.react-calendar-timeline .rct-sidebar .rct-sidebar-row {
|
||||
padding: 0 4px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #bbb;
|
||||
}
|
||||
.react-calendar-timeline .rct-sidebar .rct-sidebar-row.rct-sidebar-row-odd {
|
||||
background: #0000000d;
|
||||
}
|
||||
.react-calendar-timeline .rct-sidebar .rct-sidebar-row.rct-sidebar-row-even {
|
||||
background: transparent;
|
||||
}
|
||||
.react-calendar-timeline .rct-vertical-lines .rct-vl {
|
||||
position: absolute;
|
||||
border-left: 1px solid #bbb;
|
||||
z-index: 30;
|
||||
}
|
||||
.react-calendar-timeline .rct-vertical-lines .rct-vl.rct-vl-first {
|
||||
border-left-width: 2px;
|
||||
}
|
||||
.react-calendar-timeline .rct-vertical-lines .rct-vl.rct-day-6,
|
||||
.react-calendar-timeline .rct-vertical-lines .rct-vl.rct-day-0 {
|
||||
background: #faf6e180;
|
||||
}
|
||||
.react-calendar-timeline .rct-horizontal-lines {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: -moz-none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.react-calendar-timeline .rct-horizontal-lines .rct-hl-even,
|
||||
.react-calendar-timeline .rct-horizontal-lines .rct-hl-odd {
|
||||
border-bottom: 1px solid #bbb;
|
||||
box-sizing: border-box;
|
||||
z-index: 40;
|
||||
}
|
||||
.react-calendar-timeline .rct-horizontal-lines .rct-hl-odd {
|
||||
background: #0000000d;
|
||||
}
|
||||
.react-calendar-timeline .rct-horizontal-lines .rct-hl-even {
|
||||
background: transparent;
|
||||
}
|
||||
.react-calendar-timeline .rct-cursor-line {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
background: #2196f3;
|
||||
z-index: 51;
|
||||
}
|
||||
.react-calendar-timeline .rct-dateHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
border-bottom: 1px solid #bbb;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
background-color: #f0f0f0;
|
||||
border-left: 2px solid #bbb;
|
||||
}
|
||||
.react-calendar-timeline .rct-dateHeader-primary {
|
||||
background-color: initial;
|
||||
border-left: 1px solid #bbb;
|
||||
border-right: 1px solid #bbb;
|
||||
color: #fff;
|
||||
}
|
||||
.react-calendar-timeline .rct-header-root {
|
||||
background: #c52020;
|
||||
border-bottom: 1px solid #bbb;
|
||||
}
|
||||
.react-calendar-timeline .rct-calendar-header {
|
||||
border: 1px solid #bbb;
|
||||
}
|
||||
35
frontend/src/routes/(logistics)/-hooks/useDateWindow.tsx
Normal file
35
frontend/src/routes/(logistics)/-hooks/useDateWindow.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export function useDateWindow(initialStart = new Date()) {
|
||||
const [startDate, setStartDate] = useState(initialStart);
|
||||
const [endDayCount, setEndDayCount] = useState<string>(
|
||||
localStorage.getItem("endDayCount") || "5",
|
||||
);
|
||||
const [startDayCount, startEndDayCount] = useState<string>(
|
||||
localStorage.getItem("startDayCount") || "0",
|
||||
);
|
||||
|
||||
const days = useMemo(() => {
|
||||
const startOffset = parseInt(startDayCount, 10);
|
||||
const endOffset = parseInt(endDayCount, 10);
|
||||
|
||||
const start = new Date(startDate);
|
||||
start.setDate(start.getDate() - startOffset);
|
||||
|
||||
return Array.from({ length: endOffset + startOffset + 1 }, (_, i) => {
|
||||
const d = new Date(start);
|
||||
d.setDate(start.getDate() + i);
|
||||
return d;
|
||||
});
|
||||
}, [startDate, startDayCount, endDayCount]);
|
||||
|
||||
return {
|
||||
days,
|
||||
startDate,
|
||||
setStartDate,
|
||||
endDayCount,
|
||||
setEndDayCount,
|
||||
startDayCount,
|
||||
startEndDayCount,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { coreSocket } from "../../../lib/socket.io/socket";
|
||||
import "../-components/style.css";
|
||||
import moment from "moment";
|
||||
import Timeline from "react-calendar-timeline";
|
||||
|
||||
export const Route = createFileRoute("/(logistics)/logistics/deliverySchedule")(
|
||||
{
|
||||
beforeLoad: async () => {
|
||||
coreSocket.emit("joinScheduler", "scheduler");
|
||||
// coreSocket.on("scheduler", (p) => {
|
||||
// console.log(`[scheduler] received:`, p);
|
||||
// });
|
||||
},
|
||||
component: RouteComponent,
|
||||
},
|
||||
);
|
||||
|
||||
function RouteComponent() {
|
||||
// connect to the channel
|
||||
const [shipments, setShipments] = useState([]) as any;
|
||||
//const [perm] = useState(true); // will check this for sure with a user permissions
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
// useEffect(() => {
|
||||
// const handleConnect = () => {
|
||||
// console.log("✅ Socket connected, joining scheduler");
|
||||
// coreSocket.emit("joinScheduler");
|
||||
// };
|
||||
|
||||
// coreSocket.on("connect", handleConnect);
|
||||
|
||||
// //const handler = (msg: any) => console.log("💓", msg);
|
||||
// const onUpdate = (msg: any) => {
|
||||
// console.log(msg.data);
|
||||
// setShipments(() => {
|
||||
// return msg.data.map((i: any) => ({
|
||||
// id: i.schedule_id,
|
||||
// title: i.orderNumber,
|
||||
// group: i.dock === "" ? 1 : 1, // this will just toss everything here for now will go to the actual dock id later
|
||||
// start_time: moment(parseISO(i.lstDateCheck)),
|
||||
// end_time: moment(addHours(parseISO(i.lstDateCheck), 1)),
|
||||
// data: i,
|
||||
// }));
|
||||
// });
|
||||
// if (!loaded) setLoaded(true);
|
||||
// };
|
||||
|
||||
// //coreSocket.on("data", onData);
|
||||
// coreSocket.on("scheduler:update", onUpdate);
|
||||
|
||||
// return () => {
|
||||
// // cleanup on unmount
|
||||
// //coreSocket.off("data", onData);
|
||||
// coreSocket.off("scheduler:update", onUpdate);
|
||||
// coreSocket.off("connect", handleConnect);
|
||||
// };
|
||||
// }, []);
|
||||
|
||||
// if (shipments.length === 0) {
|
||||
// return <div>Loading.....</div>;
|
||||
// }
|
||||
|
||||
return <div className="p-4 "></div>;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/(mobileStuff)/_mobileLayout/m/cyclecounts',
|
||||
)({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(mobileStuff)/_mobileLayout/m/cyclecounts"!</div>
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/(mobileStuff)/_mobileLayout/m/delivery')(
|
||||
{
|
||||
component: RouteComponent,
|
||||
},
|
||||
)
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(mobileStuff)/_mobileLayout/m/delivery"!</div>
|
||||
}
|
||||
78
frontend/src/routes/(mobileStuff)/_mobileLayout/m/index.tsx
Normal file
78
frontend/src/routes/(mobileStuff)/_mobileLayout/m/index.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { LstCard } from "../../../../components/ui/lstCard";
|
||||
import {
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../../../../components/ui/card";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import { cn } from "../../../../lib/utils";
|
||||
|
||||
export const Route = createFileRoute("/(mobileStuff)/_mobileLayout/m/")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
const commands = [
|
||||
{
|
||||
title: "Relocate",
|
||||
description: "Moves a pallet from one location to another",
|
||||
link: "/lst/app/m/relocate",
|
||||
},
|
||||
{
|
||||
title: "Cycle Counts",
|
||||
description: "Will do a cycle count on a specific lane",
|
||||
link: "/lst/app/m/cyclecounts",
|
||||
},
|
||||
{
|
||||
title: "Delivery",
|
||||
description:
|
||||
"Scan pallets to a delivery that has already been drag down in stock",
|
||||
link: "/lst/app/m/delivery",
|
||||
},
|
||||
];
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div className="p-3 space-y-3">
|
||||
<LstCard>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex justify-center">
|
||||
Commands
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-wrap justify-center gap-3">
|
||||
{commands.map((cmd) => {
|
||||
return (
|
||||
<Link
|
||||
key={cmd.title}
|
||||
to={cmd.link}
|
||||
className="block"
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"flex flex-col justify-center items-center",
|
||||
"w-36 h-28 p-3 text-center", // fixed width/height for uniform grid
|
||||
"border-muted bg-background hover:bg-accent/40",
|
||||
"transition-all active:scale-95"
|
||||
)}
|
||||
>
|
||||
<span className="text-base font-semibold leading-tight">
|
||||
{cmd.title}
|
||||
</span>
|
||||
{/* <div className="max-w-[75px]">
|
||||
<span className="text-xs text-wrap">
|
||||
{cmd.description}
|
||||
</span>
|
||||
</div> */}
|
||||
</Button>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</LstCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/(mobileStuff)/_mobileLayout/m/relocate')(
|
||||
{
|
||||
component: RouteComponent,
|
||||
},
|
||||
)
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/(mobileStuff)/_mobileLayout/m/relocate"!</div>
|
||||
}
|
||||
13
frontend/src/routes/(mobileStuff)/_mobileLayout/route.tsx
Normal file
13
frontend/src/routes/(mobileStuff)/_mobileLayout/route.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/(mobileStuff)/_mobileLayout")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
|
||||
import {
|
||||
createRootRouteWithContext,
|
||||
Outlet,
|
||||
useRouter,
|
||||
} from "@tanstack/react-router";
|
||||
|
||||
import type { QueryClient } from "@tanstack/react-query";
|
||||
import { Toaster } from "sonner";
|
||||
@@ -10,6 +14,9 @@ import { SidebarProvider } from "../components/ui/sidebar";
|
||||
import SideBarNav from "../components/navBar/SideBarNav";
|
||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
||||
import { userAccess } from "../lib/authClient";
|
||||
import mobile from "is-mobile";
|
||||
import { useEffect } from "react";
|
||||
import { coreSocket } from "../lib/socket.io/socket";
|
||||
|
||||
interface RootRouteContext {
|
||||
queryClient: QueryClient;
|
||||
@@ -21,6 +28,29 @@ interface RootRouteContext {
|
||||
const RootLayout = () => {
|
||||
//const { logout, login } = Route.useRouteContext();
|
||||
const defaultOpen = Cookies.get("sidebar_state") === "true";
|
||||
const router = useRouter();
|
||||
// console.log(mobile({ featureDetect: true, tablet: true }));
|
||||
|
||||
// if mobile lets move to the mobile section.
|
||||
useEffect(() => {
|
||||
if (mobile({ featureDetect: true, tablet: true })) {
|
||||
router.navigate({ to: "/m" });
|
||||
}
|
||||
|
||||
coreSocket.on("connect", () => {
|
||||
console.log("✅ Connected:", coreSocket.id);
|
||||
});
|
||||
|
||||
coreSocket.on("disconnect", () => {
|
||||
console.log("🔴 Disconnected");
|
||||
});
|
||||
|
||||
return () => {
|
||||
coreSocket.off("connect");
|
||||
coreSocket.off("disconnect");
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SessionGuard>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ export default defineConfig({
|
||||
},
|
||||
server: {
|
||||
port: 5500,
|
||||
host: true,
|
||||
allowedHosts: true,
|
||||
proxy: {
|
||||
"/lst/api": {
|
||||
target: `http://localhost:${Number(
|
||||
|
||||
@@ -1,67 +1,73 @@
|
||||
import { format } from "date-fns-tz";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { settings } from "../../../../database/schema/settings.js";
|
||||
import { tryCatch } from "../../../globalUtils/tryCatch.js";
|
||||
import { query } from "../../sqlServer/prodSqlServer.js";
|
||||
import { lastSalesPriceCheck } from "../../sqlServer/querys/eom/lastSalesprice.js";
|
||||
import { consumptionCheck } from "../../sqlServer/querys/eom/consumptionCheck.js";
|
||||
import { format } from "date-fns-tz";
|
||||
import { lastSalesPriceCheck } from "../../sqlServer/querys/eom/lastSalesprice.js";
|
||||
|
||||
type Consumption = {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
includePlantToken: boolean;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
includePlantToken: boolean;
|
||||
};
|
||||
|
||||
export const getProductionConsumption = async (consumption: Consumption) => {
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
consumptionCheck
|
||||
.replace("[startDate]", consumption.startDate)
|
||||
.replace("[endDate]", consumption.endDate),
|
||||
"Last sales price"
|
||||
)
|
||||
)) as any;
|
||||
const { data, error } = (await tryCatch(
|
||||
query(
|
||||
consumptionCheck
|
||||
.replace("[startDate]", consumption.startDate)
|
||||
.replace("[endDate]", consumption.endDate),
|
||||
"Last sales price",
|
||||
),
|
||||
)) as any;
|
||||
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the last sales price",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
console.log(
|
||||
consumptionCheck
|
||||
.replace("[startDate]", consumption.startDate)
|
||||
.replace("[endDate]", consumption.endDate),
|
||||
);
|
||||
console.log(data.data.length);
|
||||
if (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Error getting the last sales price",
|
||||
data: error,
|
||||
};
|
||||
}
|
||||
|
||||
if (consumption.includePlantToken) {
|
||||
const { data: s, error: se } = (await tryCatch(
|
||||
db.select().from(settings).where(eq(settings.name, "plantToken"))
|
||||
)) as any;
|
||||
if (consumption.includePlantToken) {
|
||||
const { data: s, error: se } = (await tryCatch(
|
||||
db.select().from(settings).where(eq(settings.name, "plantToken")),
|
||||
)) as any;
|
||||
|
||||
if (se) {
|
||||
console.log("Error getting articles");
|
||||
return data.data;
|
||||
}
|
||||
if (se) {
|
||||
console.log("Error getting articles");
|
||||
return data.data;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `consumption data`,
|
||||
data: data.data.map((n: any) => {
|
||||
return {
|
||||
plantToken: s[0].value,
|
||||
...n,
|
||||
Prod_Date: format(n.Prod_Date, "M/d/yyyy"),
|
||||
};
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
message: `consumption data`,
|
||||
data: data.data.map((n: any) => {
|
||||
return {
|
||||
...n,
|
||||
Prod_Date: format(n.Prod_Date, "M/d/yyyy"),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: `consumption data`,
|
||||
data: data.data.map((n: any) => {
|
||||
return {
|
||||
plantToken: s[0].value,
|
||||
...n,
|
||||
Prod_Date: format(n.Prod_Date, "M/d/yyyy"),
|
||||
};
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: true,
|
||||
message: `consumption data`,
|
||||
data: data.data.map((n: any) => {
|
||||
return {
|
||||
...n,
|
||||
Prod_Date: format(n.Prod_Date, "M/d/yyyy"),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -97,45 +97,82 @@ app.Run(async (HttpContext context) =>
|
||||
}
|
||||
|
||||
// Handle WebSocket requests
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
// if (context.WebSockets.IsWebSocketRequest)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var backendUri = new UriBuilder(backendWsBase)
|
||||
// {
|
||||
// Path = context.Request.Path,
|
||||
// Query = context.Request.QueryString.ToString()
|
||||
// }.Uri;
|
||||
|
||||
// using var backendSocket = new ClientWebSocket();
|
||||
|
||||
// // Forward incoming headers
|
||||
// foreach (var header in context.Request.Headers)
|
||||
// {
|
||||
// try { backendSocket.Options.SetRequestHeader(header.Key, header.Value); }
|
||||
// catch { /* ignore headers WS client doesn't like */ }
|
||||
// }
|
||||
|
||||
// await backendSocket.ConnectAsync(backendUri, context.RequestAborted);
|
||||
|
||||
// using var frontendSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
// var cts = new CancellationTokenSource();
|
||||
|
||||
// var forwardToBackend = ForwardWebSocketAsync(frontendSocket, backendSocket, cts.Token);
|
||||
// var forwardToFrontend = ForwardWebSocketAsync(backendSocket, frontendSocket, cts.Token);
|
||||
|
||||
// await Task.WhenAny(forwardToBackend, forwardToFrontend);
|
||||
// cts.Cancel();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// LogToFile($"WebSocket proxy error: {ex.Message}");
|
||||
// context.Response.StatusCode = (int)HttpStatusCode.BadGateway;
|
||||
// await context.Response.WriteAsync($"WebSocket proxy error: {ex.Message}");
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
// Accept the incoming client WebSocket
|
||||
using var frontendSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
// Build the backend WebSocket URI
|
||||
var backendUri = new UriBuilder(backendWsBase)
|
||||
{
|
||||
var backendUri = new UriBuilder(backendWsBase)
|
||||
{
|
||||
Path = context.Request.Path,
|
||||
Query = context.Request.QueryString.ToString()
|
||||
}.Uri;
|
||||
Path = context.Request.Path,
|
||||
Query = context.Request.QueryString.ToString()
|
||||
}.Uri;
|
||||
|
||||
using var backendSocket = new ClientWebSocket();
|
||||
// Connect to the Node.js backend
|
||||
using var backendSocket = new ClientWebSocket();
|
||||
await backendSocket.ConnectAsync(backendUri, context.RequestAborted);
|
||||
|
||||
// Forward incoming headers
|
||||
foreach (var header in context.Request.Headers)
|
||||
{
|
||||
try { backendSocket.Options.SetRequestHeader(header.Key, header.Value); }
|
||||
catch { /* ignore headers WS client doesn't like */ }
|
||||
}
|
||||
// Forward frames both ways until one side closes
|
||||
var cts = new CancellationTokenSource();
|
||||
var forwardToBackend = ForwardWebSocketAsync(frontendSocket, backendSocket, cts.Token);
|
||||
var forwardToFrontend = ForwardWebSocketAsync(backendSocket, frontendSocket, cts.Token);
|
||||
|
||||
await backendSocket.ConnectAsync(backendUri, context.RequestAborted);
|
||||
await Task.WhenAny(forwardToBackend, forwardToFrontend);
|
||||
|
||||
using var frontendSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
var forwardToBackend = ForwardWebSocketAsync(frontendSocket, backendSocket, cts.Token);
|
||||
var forwardToFrontend = ForwardWebSocketAsync(backendSocket, frontendSocket, cts.Token);
|
||||
|
||||
await Task.WhenAny(forwardToBackend, forwardToFrontend);
|
||||
cts.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogToFile($"WebSocket proxy error: {ex.Message}");
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadGateway;
|
||||
await context.Response.WriteAsync($"WebSocket proxy error: {ex.Message}");
|
||||
}
|
||||
return;
|
||||
// Cancel the other direction once one side closes
|
||||
cts.Cancel();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogToFile($"WebSocket proxy error: {ex.Message}");
|
||||
context.Response.StatusCode = (int)HttpStatusCode.BadGateway;
|
||||
await context.Response.WriteAsync($"WebSocket proxy error: {ex.Message}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
// Otherwise: normal HTTP request
|
||||
var client = context.RequestServices.GetRequiredService<IHttpClientFactory>().CreateClient();
|
||||
var targetUri = backendHttpBase + context.Request.Path + context.Request.QueryString;
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
arguments=".\lstWrapper.dll"
|
||||
stdoutLogEnabled="true"
|
||||
stdoutLogFile=".\logs\stdout"
|
||||
hostingModel="inprocess" />
|
||||
hostingModel="outofprocess" />
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
21
migrations/0012_wise_cardiac.sql
Normal file
21
migrations/0012_wise_cardiac.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
CREATE TABLE "orderScheduler" (
|
||||
"schedule_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"order_type" text NOT NULL,
|
||||
"order_number" integer NOT NULL,
|
||||
"header" text NOT NULL,
|
||||
"line_item_number" text,
|
||||
"customer_release_number" text,
|
||||
"delivery_date" date NOT NULL,
|
||||
"loading_date" date,
|
||||
"order_qty" real NOT NULL,
|
||||
"order_lu" real NOT NULL,
|
||||
"delivered_qty" real DEFAULT 0,
|
||||
"delivered_lu" real DEFAULT 0,
|
||||
"remark" text,
|
||||
"current_state" integer,
|
||||
"lst_date_check" date,
|
||||
"dock" text,
|
||||
"order_from" text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "orderNumber" ON "orderScheduler" USING btree ("order_number");
|
||||
2
migrations/0013_known_dragon_man.sql
Normal file
2
migrations/0013_known_dragon_man.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "av" integer;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "decription" text;
|
||||
2
migrations/0014_huge_mother_askani.sql
Normal file
2
migrations/0014_huge_mother_askani.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "add_date" date DEFAULT now();--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "upd_date" date DEFAULT now();
|
||||
1
migrations/0015_swift_rattler.sql
Normal file
1
migrations/0015_swift_rattler.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "orderScheduler" RENAME COLUMN "decription" TO "description";
|
||||
2
migrations/0016_glorious_wolf_cub.sql
Normal file
2
migrations/0016_glorious_wolf_cub.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "customer_address_id" integer;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "customer_description" text;
|
||||
1
migrations/0017_previous_kate_bishop.sql
Normal file
1
migrations/0017_previous_kate_bishop.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "orderScheduler" ADD COLUMN "created_as_EDI" boolean;
|
||||
7
migrations/0018_aspiring_silver_samurai.sql
Normal file
7
migrations/0018_aspiring_silver_samurai.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "delivery_date" SET DATA TYPE timestamp;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "loading_date" SET DATA TYPE timestamp;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "lst_date_check" SET DATA TYPE timestamp;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "add_date" SET DATA TYPE timestamp;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "add_date" SET DEFAULT now();--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "upd_date" SET DATA TYPE timestamp;--> statement-breakpoint
|
||||
ALTER TABLE "orderScheduler" ALTER COLUMN "upd_date" SET DEFAULT now();
|
||||
1190
migrations/meta/0012_snapshot.json
Normal file
1190
migrations/meta/0012_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1202
migrations/meta/0013_snapshot.json
Normal file
1202
migrations/meta/0013_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1216
migrations/meta/0014_snapshot.json
Normal file
1216
migrations/meta/0014_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1216
migrations/meta/0015_snapshot.json
Normal file
1216
migrations/meta/0015_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1228
migrations/meta/0016_snapshot.json
Normal file
1228
migrations/meta/0016_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1234
migrations/meta/0017_snapshot.json
Normal file
1234
migrations/meta/0017_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
1234
migrations/meta/0018_snapshot.json
Normal file
1234
migrations/meta/0018_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -85,6 +85,55 @@
|
||||
"when": 1759195276875,
|
||||
"tag": "0011_careless_banshee",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 12,
|
||||
"version": "7",
|
||||
"when": 1760374807920,
|
||||
"tag": "0012_wise_cardiac",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 13,
|
||||
"version": "7",
|
||||
"when": 1760377601945,
|
||||
"tag": "0013_known_dragon_man",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 14,
|
||||
"version": "7",
|
||||
"when": 1760388532973,
|
||||
"tag": "0014_huge_mother_askani",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 15,
|
||||
"version": "7",
|
||||
"when": 1760388593956,
|
||||
"tag": "0015_swift_rattler",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "7",
|
||||
"when": 1760444321051,
|
||||
"tag": "0016_glorious_wolf_cub",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 17,
|
||||
"version": "7",
|
||||
"when": 1760457733998,
|
||||
"tag": "0017_previous_kate_bishop",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 18,
|
||||
"version": "7",
|
||||
"when": 1760480733009,
|
||||
"tag": "0018_aspiring_silver_samurai",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
658
package-lock.json
generated
658
package-lock.json
generated
@@ -9,42 +9,45 @@
|
||||
"version": "1.5.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@dotenvx/dotenvx": "^1.49.0",
|
||||
"@dotenvx/dotenvx": "^1.51.0",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@types/cors": "^2.8.19",
|
||||
"axios": "^1.12.2",
|
||||
"better-auth": "^1.3.9",
|
||||
"better-auth": "^1.3.27",
|
||||
"cors": "^2.8.5",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"drizzle-orm": "^0.44.5",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"drizzle-orm": "^0.44.6",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"express": "^5.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"morgan": "^1.10.1",
|
||||
"mssql": "^11.0.1",
|
||||
"nodemailer": "^7.0.6",
|
||||
"mssql": "^12.0.0",
|
||||
"nodemailer": "^7.0.9",
|
||||
"nodemailer-express-handlebars": "^7.0.0",
|
||||
"npm-check-updates": "^19.0.0",
|
||||
"pg": "^8.16.3",
|
||||
"pino": "^9.9.0",
|
||||
"pino-pretty": "^13.1.1",
|
||||
"pino": "^10.0.0",
|
||||
"pino-pretty": "^13.1.2",
|
||||
"postgres": "^3.4.7",
|
||||
"zod": "^4.1.5"
|
||||
"socket.io": "^4.8.1",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.2.6",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/morgan": "^1.9.10",
|
||||
"@types/mssql": "^9.1.7",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/nodemailer": "^7.0.1",
|
||||
"@types/mssql": "^9.1.8",
|
||||
"@types/node": "^24.7.1",
|
||||
"@types/nodemailer": "^7.0.2",
|
||||
"@types/nodemailer-express-handlebars": "^4.0.5",
|
||||
"concurrently": "^9.2.1",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"tsx": "^4.20.4",
|
||||
"typescript": "^5.9.2"
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-crypto/sha256-browser": {
|
||||
@@ -1055,20 +1058,189 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/utils": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.2.6.tgz",
|
||||
"integrity": "sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA==",
|
||||
"license": "MIT",
|
||||
"node_modules/@better-auth/core": {
|
||||
"version": "1.3.27",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.3.27.tgz",
|
||||
"integrity": "sha512-3Sfdax6MQyronY+znx7bOsfQHI6m1SThvJWb0RDscFEAhfqLy95k1sl+/PgGyg0cwc2cUXoEiAOSqYdFYrg3vA==",
|
||||
"dependencies": {
|
||||
"uncrypto": "^0.1.3"
|
||||
"better-call": "1.0.19",
|
||||
"zod": "^4.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz",
|
||||
"integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@better-fetch/fetch": {
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/@better-fetch/fetch/-/fetch-1.1.18.tgz",
|
||||
"integrity": "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="
|
||||
},
|
||||
"node_modules/@biomejs/biome": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.6.tgz",
|
||||
"integrity": "sha512-yKTCNGhek0rL5OEW1jbLeZX8LHaM8yk7+3JRGv08my+gkpmtb5dDE+54r2ZjZx0ediFEn1pYBOJSmOdDP9xtFw==",
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"bin": {
|
||||
"biome": "bin/biome"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/biome"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@biomejs/cli-darwin-arm64": "2.2.6",
|
||||
"@biomejs/cli-darwin-x64": "2.2.6",
|
||||
"@biomejs/cli-linux-arm64": "2.2.6",
|
||||
"@biomejs/cli-linux-arm64-musl": "2.2.6",
|
||||
"@biomejs/cli-linux-x64": "2.2.6",
|
||||
"@biomejs/cli-linux-x64-musl": "2.2.6",
|
||||
"@biomejs/cli-win32-arm64": "2.2.6",
|
||||
"@biomejs/cli-win32-x64": "2.2.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-darwin-arm64": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.6.tgz",
|
||||
"integrity": "sha512-UZPmn3M45CjTYulgcrFJFZv7YmK3pTxTJDrFYlNElT2FNnkkX4fsxjExTSMeWKQYoZjvekpH5cvrYZZlWu3yfA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-darwin-x64": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.6.tgz",
|
||||
"integrity": "sha512-HOUIquhHVgh/jvxyClpwlpl/oeMqntlteL89YqjuFDiZ091P0vhHccwz+8muu3nTyHWM5FQslt+4Jdcd67+xWQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-arm64": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.6.tgz",
|
||||
"integrity": "sha512-BpGtuMJGN+o8pQjvYsUKZ+4JEErxdSmcRD/JG3mXoWc6zrcA7OkuyGFN1mDggO0Q1n7qXxo/PcupHk8gzijt5g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-arm64-musl": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.6.tgz",
|
||||
"integrity": "sha512-TjCenQq3N6g1C+5UT3jE1bIiJb5MWQvulpUngTIpFsL4StVAUXucWD0SL9MCW89Tm6awWfeXBbZBAhJwjyFbRQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-x64": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.6.tgz",
|
||||
"integrity": "sha512-1HaM/dpI/1Z68zp8ZdT6EiBq+/O/z97a2AiHMl+VAdv5/ELckFt9EvRb8hDHpk8hUMoz03gXkC7VPXOVtU7faA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-linux-x64-musl": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.6.tgz",
|
||||
"integrity": "sha512-1ZcBux8zVM3JhWN2ZCPaYf0+ogxXG316uaoXJdgoPZcdK/rmRcRY7PqHdAos2ExzvjIdvhQp72UcveI98hgOog==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-win32-arm64": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.6.tgz",
|
||||
"integrity": "sha512-h3A88G8PGM1ryTeZyLlSdfC/gz3e95EJw9BZmA6Po412DRqwqPBa2Y9U+4ZSGUAXCsnSQE00jLV8Pyrh0d+jQw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@biomejs/cli-win32-x64": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.6.tgz",
|
||||
"integrity": "sha512-yx0CqeOhPjYQ5ZXgPfu8QYkgBhVJyvWe36as7jRuPrKPO5ylVDfwVtPQ+K/mooNTADW0IhxOZm3aPu16dP8yNQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@commitlint/config-validator": {
|
||||
"version": "19.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz",
|
||||
@@ -1192,9 +1364,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@dotenvx/dotenvx": {
|
||||
"version": "1.49.0",
|
||||
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.49.0.tgz",
|
||||
"integrity": "sha512-M1cyP6YstFQCjih54SAxCqHLMMi8QqV8tenpgGE48RTXWD7vfMYJiw/6xcCDpS2h28AcLpTsFCZA863Ge9yxzA==",
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.51.0.tgz",
|
||||
"integrity": "sha512-CbMGzyOYSyFF7d4uaeYwO9gpSBzLTnMmSmTVpCZjvpJFV69qYbjYPpzNnCz1mb2wIvEhjWjRwQWuBzTO0jITww==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"commander": "^11.1.0",
|
||||
@@ -3007,6 +3179,12 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tanstack/react-table": {
|
||||
"version": "8.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
|
||||
@@ -3041,9 +3219,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tediousjs/connection-string": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz",
|
||||
"integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.6.0.tgz",
|
||||
"integrity": "sha512-GxlsW354Vi6QqbUgdPyQVcQjI7cZBdGV5vOYVYuCVDTylx2wl3WHR2HlhcxxHTrMigbelpXsdcZso+66uxPfow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
@@ -3157,9 +3335,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mssql": {
|
||||
"version": "9.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-9.1.7.tgz",
|
||||
"integrity": "sha512-eIOEe78nuSW5KctDHImDhLZ9a+jV/z/Xs5RBhcG/jrk+YWqhdNmzBmHVWV7aWQ5fW+jbIGtX6Ph+bbVqfhzafg==",
|
||||
"version": "9.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-9.1.8.tgz",
|
||||
"integrity": "sha512-mt9h5jWj+DYE5jxnKaWSV/GqDf9FV52XYVk6T3XZF69noEe+JJV6MKirii48l81+cjmAkSq+qeKX+k61fHkYrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3169,16 +3347,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.3.0",
|
||||
"version": "24.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.1.tgz",
|
||||
"integrity": "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.10.0"
|
||||
"undici-types": "~7.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/nodemailer": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.1.tgz",
|
||||
"integrity": "sha512-UfHAghPmGZVzaL8x9y+mKZMWyHC399+iq0MOmya5tIyenWX3lcdSb60vOmp0DocR6gCDTYTozv/ULQnREyyjkg==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.2.tgz",
|
||||
"integrity": "sha512-Zo6uOA9157WRgBk/ZhMpTQ/iCWLMk7OIs/Q9jvHarMvrzUUP/MDdPHL2U1zpf57HrrWGv4nYQn5uIxna0xY3xw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3500,6 +3680,15 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64id": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^4.5.0 || >= 5.9"
|
||||
}
|
||||
},
|
||||
"node_modules/basic-auth": {
|
||||
"version": "2.0.1",
|
||||
"license": "MIT",
|
||||
@@ -3515,38 +3704,49 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/better-auth": {
|
||||
"version": "1.3.9",
|
||||
"resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.3.9.tgz",
|
||||
"integrity": "sha512-Ty6BHzuShlqSs7I4RMlBRQ3duOWNB7WWriIu2FJVGjQAOtTVvamzFCR4/j5ROFLoNkpvNTRF7BJozsrMICL1gw==",
|
||||
"version": "1.3.27",
|
||||
"resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.3.27.tgz",
|
||||
"integrity": "sha512-SwiGAJ7yU6dBhNg0NdV1h5M8T5sa7/AszZVc4vBfMDrLLmvUfbt9JoJ0uRUJUEdKRAAxTyl9yA+F3+GhtAD80w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@better-auth/utils": "0.2.6",
|
||||
"@better-fetch/fetch": "^1.1.18",
|
||||
"@better-auth/core": "1.3.27",
|
||||
"@better-auth/utils": "0.3.0",
|
||||
"@better-fetch/fetch": "1.1.18",
|
||||
"@noble/ciphers": "^2.0.0",
|
||||
"@noble/hashes": "^2.0.0",
|
||||
"@simplewebauthn/browser": "^13.1.2",
|
||||
"@simplewebauthn/server": "^13.1.2",
|
||||
"better-call": "1.0.18",
|
||||
"better-call": "1.0.19",
|
||||
"defu": "^6.1.4",
|
||||
"jose": "^6.1.0",
|
||||
"kysely": "^0.28.5",
|
||||
"nanostores": "^0.11.4",
|
||||
"nanostores": "^1.0.1",
|
||||
"zod": "^4.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@lynx-js/react": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@lynx-js/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@sveltejs/kit": {
|
||||
"optional": true
|
||||
},
|
||||
"next": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"solid-js": {
|
||||
"optional": true
|
||||
},
|
||||
"svelte": {
|
||||
"optional": true
|
||||
},
|
||||
"vue": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3575,10 +3775,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/better-call": {
|
||||
"version": "1.0.18",
|
||||
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.0.18.tgz",
|
||||
"integrity": "sha512-Ojyck3P3fs/egBmCW50tvfbCJorNV5KphfPOKrkCxPfOr8Brth1ruDtAJuhHVHEUiWrXv+vpEgWQk7m7FzhbbQ==",
|
||||
"version": "1.0.19",
|
||||
"resolved": "https://registry.npmjs.org/better-call/-/better-call-1.0.19.tgz",
|
||||
"integrity": "sha512-sI3GcA1SCVa3H+CDHl8W8qzhlrckwXOTKhqq3OOPXjgn5aTOMIqGY34zLY/pHA6tRRMjTUC3lz5Mi7EbDA24Kw==",
|
||||
"dependencies": {
|
||||
"@better-auth/utils": "^0.3.0",
|
||||
"@better-fetch/fetch": "^1.1.4",
|
||||
"rou3": "^0.5.1",
|
||||
"set-cookie-parser": "^2.7.1",
|
||||
@@ -4950,9 +5151,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/drizzle-kit": {
|
||||
"version": "0.31.4",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz",
|
||||
"integrity": "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==",
|
||||
"version": "0.31.5",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.5.tgz",
|
||||
"integrity": "sha512-+CHgPFzuoTQTt7cOYCV6MOw2w8vqEn/ap1yv4bpZOWL03u7rlVRQhUY0WYT3rHsgVTXwYQDZaSUJSQrMBUKuWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@drizzle-team/brocli": "^0.10.2",
|
||||
@@ -4965,9 +5166,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/drizzle-orm": {
|
||||
"version": "0.44.5",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.5.tgz",
|
||||
"integrity": "sha512-jBe37K7d8ZSKptdKfakQFdeljtu3P2Cbo7tJoJSVZADzIKOBo9IAJPOmMsH2bZl90bZgh8FQlD8BjxXA/zuBkQ==",
|
||||
"version": "0.44.6",
|
||||
"resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.6.tgz",
|
||||
"integrity": "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-rds-data": ">=3",
|
||||
@@ -5178,6 +5379,95 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
|
||||
"integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/node": ">=10.0.0",
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "2.0.0",
|
||||
"cookie": "~0.7.2",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
@@ -5513,15 +5803,6 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/fast-redact": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
|
||||
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-safe-stringify": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||
@@ -7598,17 +7879,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mssql": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mssql/-/mssql-11.0.1.tgz",
|
||||
"integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==",
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mssql/-/mssql-12.0.0.tgz",
|
||||
"integrity": "sha512-FcDQ1Gwe4g3Mhw25R1Onr8N+jmqBTWE/pmtcgxYnAUSIf/vBQMvJfMnyMY8ruOICtBch5+Wgbcfd3REDQSlWpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tediousjs/connection-string": "^0.5.0",
|
||||
"@tediousjs/connection-string": "^0.6.0",
|
||||
"commander": "^11.0.0",
|
||||
"debug": "^4.3.3",
|
||||
"rfdc": "^1.3.0",
|
||||
"tarn": "^3.0.2",
|
||||
"tedious": "^18.2.1"
|
||||
"tedious": "^19.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"mssql": "bin/mssql"
|
||||
@@ -7625,9 +7905,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/nanostores": {
|
||||
"version": "0.11.4",
|
||||
"resolved": "https://registry.npmjs.org/nanostores/-/nanostores-0.11.4.tgz",
|
||||
"integrity": "sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nanostores/-/nanostores-1.0.1.tgz",
|
||||
"integrity": "sha512-kNZ9xnoJYKg/AfxjrVL4SS0fKX++4awQReGqWnwTRHxeHGZ1FJFVgTqr/eMrNQdp0Tz7M7tG/TDaX8QfHDwVCw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -7636,7 +7916,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
"node": "^20.0.0 || >=22.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/native-duplexpair": {
|
||||
@@ -7659,9 +7939,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz",
|
||||
"integrity": "sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==",
|
||||
"version": "7.0.9",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.9.tgz",
|
||||
"integrity": "sha512-9/Qm0qXIByEP8lEV2qOqcAW7bRpL8CR9jcTwk3NBnHJNmP9fIJ86g2fgmIXqHY+nj55ZEMwWqYAT2QTDpRUYiQ==",
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@@ -7704,6 +7984,20 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-check-updates": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.0.0.tgz",
|
||||
"integrity": "sha512-qcfjZEv6xB+WvW24S8wU1MKISPPiTREraBg62XDo/7zmOLXH3Zj7ti2v/LRfks0qITU8SDZLTWwgIitflvursw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"ncu": "build/cli.js",
|
||||
"npm-check-updates": "build/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0",
|
||||
"npm": ">=8.12.1"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-run-path": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
|
||||
@@ -8235,13 +8529,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "9.9.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz",
|
||||
"integrity": "sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==",
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-10.0.0.tgz",
|
||||
"integrity": "sha512-eI9pKwWEix40kfvSzqEP6ldqOoBIN7dwD/o91TY5z8vQI12sAffpR/pOqAD1IVVwIVHDpHjkq0joBPdJD0rafA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0",
|
||||
"fast-redact": "^3.1.1",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-std-serializers": "^7.0.0",
|
||||
@@ -8249,6 +8542,7 @@
|
||||
"quick-format-unescaped": "^4.0.3",
|
||||
"real-require": "^0.2.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"slow-redact": "^0.3.0",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"thread-stream": "^3.0.0"
|
||||
},
|
||||
@@ -8275,9 +8569,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.1.tgz",
|
||||
"integrity": "sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==",
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.2.tgz",
|
||||
"integrity": "sha512-3cN0tCakkT4f3zo9RXDIhy6GTvtYD6bK4CRBLN9j3E/ePqN1tugAXD5rGVfoChW6s0hiek+eyYlLNqc/BG7vBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.7",
|
||||
@@ -8807,12 +9101,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "2.7.1",
|
||||
"dev": true,
|
||||
@@ -9089,6 +9377,147 @@
|
||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/slow-redact": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/slow-redact/-/slow-redact-0.3.1.tgz",
|
||||
"integrity": "sha512-NvFvl1GuLZNW4U046Tfi8b26zXo8aBzgCAS2f7yVJR/fArN93mOqSA99cB9uITm92ajSz01bsu1K7SCVVjIMpQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
|
||||
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.4",
|
||||
"base64id": "~2.0.0",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io": "~6.6.0",
|
||||
"socket.io-adapter": "~2.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.4",
|
||||
"ws": "~8.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io/node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io/node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/sonic-boom": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
|
||||
@@ -9372,9 +9801,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tedious": {
|
||||
"version": "18.6.1",
|
||||
"resolved": "https://registry.npmjs.org/tedious/-/tedious-18.6.1.tgz",
|
||||
"integrity": "sha512-9AvErXXQTd6l7TDd5EmM+nxbOGyhnmdbp/8c3pw+tjaiSXW9usME90ET/CRG1LN1Y9tPMtz/p83z4Q97B4DDpw==",
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tedious/-/tedious-19.0.0.tgz",
|
||||
"integrity": "sha512-nmxNBAT72mMVCIYp0Ts0Zzd5+LBQjoXlqigCrIjSo2OERSi04vr3EHq3qJxv/zgrSkg7si03SoIIfekTAadA7w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@azure/core-auth": "^1.7.2",
|
||||
@@ -9389,7 +9818,7 @@
|
||||
"sprintf-js": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"node": ">=18.17"
|
||||
}
|
||||
},
|
||||
"node_modules/tedious/node_modules/bl": {
|
||||
@@ -9622,9 +10051,9 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.4",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz",
|
||||
"integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==",
|
||||
"version": "4.20.6",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz",
|
||||
"integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9674,7 +10103,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.2",
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -9705,7 +10136,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.10.0",
|
||||
"version": "7.14.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz",
|
||||
"integrity": "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
@@ -9918,6 +10351,27 @@
|
||||
"version": "1.0.2",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/wsl-utils": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
|
||||
@@ -10008,9 +10462,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.5.tgz",
|
||||
"integrity": "sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==",
|
||||
"version": "4.1.12",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz",
|
||||
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
|
||||
34
package.json
34
package.json
@@ -30,7 +30,8 @@
|
||||
"db:migrate": "npx drizzle-kit push",
|
||||
"db:generate": "npx drizzle-kit generate",
|
||||
"translateDocs": "cd scripts && node translateScript.js",
|
||||
"auth:generate": "npx @better-auth/cli generate --config ./app/src/pkg/auth/auth.ts"
|
||||
"auth:generate": "npx @better-auth/cli generate --config ./app/src/pkg/auth/auth.ts",
|
||||
"updates": "ncu -g"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -41,42 +42,45 @@
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@dotenvx/dotenvx": "^1.49.0",
|
||||
"@dotenvx/dotenvx": "^1.51.0",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@types/cors": "^2.8.19",
|
||||
"axios": "^1.12.2",
|
||||
"better-auth": "^1.3.9",
|
||||
"better-auth": "^1.3.27",
|
||||
"cors": "^2.8.5",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"drizzle-orm": "^0.44.5",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"drizzle-orm": "^0.44.6",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"express": "^5.1.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"morgan": "^1.10.1",
|
||||
"mssql": "^11.0.1",
|
||||
"nodemailer": "^7.0.6",
|
||||
"mssql": "^12.0.0",
|
||||
"nodemailer": "^7.0.9",
|
||||
"nodemailer-express-handlebars": "^7.0.0",
|
||||
"npm-check-updates": "^19.0.0",
|
||||
"pg": "^8.16.3",
|
||||
"pino": "^9.9.0",
|
||||
"pino-pretty": "^13.1.1",
|
||||
"pino": "^10.0.0",
|
||||
"pino-pretty": "^13.1.2",
|
||||
"postgres": "^3.4.7",
|
||||
"zod": "^4.1.5"
|
||||
"socket.io": "^4.8.1",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.2.6",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/morgan": "^1.9.10",
|
||||
"@types/mssql": "^9.1.7",
|
||||
"@types/node": "^24.3.0",
|
||||
"@types/nodemailer": "^7.0.1",
|
||||
"@types/mssql": "^9.1.8",
|
||||
"@types/node": "^24.7.1",
|
||||
"@types/nodemailer": "^7.0.2",
|
||||
"@types/nodemailer-express-handlebars": "^4.0.5",
|
||||
"concurrently": "^9.2.1",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"tsx": "^4.20.4",
|
||||
"typescript": "^5.9.2"
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
Reference in New Issue
Block a user