Compare commits

...

9 Commits

81 changed files with 11837 additions and 501 deletions

48
.vscode/settings copy.json.bak vendored Normal file
View 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
View File

@@ -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"]
}

View 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
}

View File

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

View File

@@ -1,7 +1,7 @@
meta {
name: GrantROle by ID
type: http
seq: 8
seq: 7
}
post {

View File

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

View File

@@ -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"

View File

@@ -14,6 +14,10 @@ params:path {
token: usbet1
}
headers {
Cookie: {{session_cookie}}
}
body:json {
{
"zipcode": 45245

View File

@@ -1,7 +1,7 @@
meta {
name: Get user Roles
type: http
seq: 9
seq: 7
}
get {

View File

@@ -1,7 +1,7 @@
meta {
name: Request Resetpassword
type: http
seq: 10
seq: 8
}
post {

View File

@@ -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
}

View File

@@ -1,7 +1,7 @@
vars {
url: http://localhost:4200
session_cookie:
urlv2: http://usksc1vms006:3000
urlv2: http://usiow1vms006:3001
jwtV2:
}
vars:secret [

View File

@@ -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", () =>

View 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);
};

View 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
);
};

View 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;

View File

@@ -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;

View File

@@ -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) => {

View 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>;

View File

@@ -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>;

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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: [],
});
}

View File

@@ -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}`

View 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
`;

View 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
];

View File

View 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);
});
});
};

View 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
View 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
View 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"
}
}
}

View File

@@ -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>
}

View File

@@ -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>
}

View File

@@ -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>
}

View File

@@ -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",

View File

@@ -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",

View 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 };

View 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>
);
}

View 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 };

View 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 }

View File

@@ -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;
}
}

View 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);
});

View File

@@ -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)

View 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>
);
}

View 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 = 180px 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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;
}

View 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,
};
}

View File

@@ -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>;
}

View File

@@ -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>
}

View File

@@ -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>
}

View 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>
);
}

View File

@@ -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>
}

View 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>
);
}

View File

@@ -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>

View File

@@ -5,7 +5,6 @@
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}

View File

@@ -36,6 +36,8 @@ export default defineConfig({
},
server: {
port: 5500,
host: true,
allowedHosts: true,
proxy: {
"/lst/api": {
target: `http://localhost:${Number(

View File

@@ -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"),
};
}),
};
}
};

View File

@@ -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;

View File

@@ -24,6 +24,6 @@
arguments=".\lstWrapper.dll"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess" />
hostingModel="outofprocess" />
</system.webServer>
</configuration>

View 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");

View File

@@ -0,0 +1,2 @@
ALTER TABLE "orderScheduler" ADD COLUMN "av" integer;--> statement-breakpoint
ALTER TABLE "orderScheduler" ADD COLUMN "decription" text;

View 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();

View File

@@ -0,0 +1 @@
ALTER TABLE "orderScheduler" RENAME COLUMN "decription" TO "description";

View File

@@ -0,0 +1,2 @@
ALTER TABLE "orderScheduler" ADD COLUMN "customer_address_id" integer;--> statement-breakpoint
ALTER TABLE "orderScheduler" ADD COLUMN "customer_description" text;

View File

@@ -0,0 +1 @@
ALTER TABLE "orderScheduler" ADD COLUMN "created_as_EDI" boolean;

View 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();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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
View File

@@ -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"

View File

@@ -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": {