Compare commits
35 Commits
v0.1.0-alp
...
f635415b75
| Author | SHA1 | Date | |
|---|---|---|---|
| f635415b75 | |||
| 40edbc3eae | |||
| 2558b2e5bb | |||
| 45a0dee9ca | |||
| bb7931d6c8 | |||
| 4f848bb649 | |||
| f8335f5217 | |||
| 2a35381fe4 | |||
| e6b92aeb10 | |||
| da87e2e1d3 | |||
| 3ef0f230dd | |||
| 78f7b8a179 | |||
| 1a3d8a7ddc | |||
| 2a648f6306 | |||
| 69a9a81a88 | |||
| 188fdd0eb7 | |||
| db28635c8c | |||
| bcdf9566bc | |||
| c15ee070e7 | |||
| 347edb7078 | |||
| fe0b1573f3 | |||
| 9c0ef1f5df | |||
| 8b076949a7 | |||
| 6d0fb8aee4 | |||
| 3a0c729b9a | |||
| 057a570e43 | |||
| 52974aa0b4 | |||
| ecfbda9036 | |||
| 389211186f | |||
| 3a27fd8542 | |||
| 1f6637c512 | |||
| 1840ac5e58 | |||
| 636daaed0a | |||
| 71c83062cb | |||
| cd67c4de80 |
@@ -9,4 +9,4 @@ builds
|
||||
testFiles
|
||||
nssm.exe
|
||||
postgresql-17.9-2-windows-x64.exe
|
||||
VSCodeUserSetup-x64-1.112.0.msi
|
||||
VSCodeSetup-x64-1.120.0.msi
|
||||
66
.gitea/ISSUE_TEMPLATE/bug_report_frontend.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: Bug Report - Frontend
|
||||
about: Report something that is broken or not working correctly
|
||||
title: "[BUG - Frontend] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- bug
|
||||
- frontend
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
Briefly explain the issue.
|
||||
|
||||
---
|
||||
|
||||
# Steps To Reproduce
|
||||
|
||||
1. Go to ...
|
||||
2. Click ...
|
||||
3. Scan ...
|
||||
4. Error occurs ...
|
||||
|
||||
---
|
||||
|
||||
# Expected Behavior
|
||||
|
||||
What should have happened?
|
||||
|
||||
---
|
||||
|
||||
# Actual Behavior
|
||||
|
||||
What actually happened instead?
|
||||
|
||||
---
|
||||
|
||||
# Severity
|
||||
|
||||
- [ ] Low
|
||||
- [ ] Medium
|
||||
- [ ] High
|
||||
- [ ] Critical
|
||||
|
||||
---
|
||||
|
||||
# Environment
|
||||
|
||||
Example:
|
||||
|
||||
- Production
|
||||
- Development
|
||||
- Zebra Scanner
|
||||
- Mobile Device
|
||||
- Windows Server
|
||||
- Docker
|
||||
|
||||
---
|
||||
|
||||
# Logs / Screenshots
|
||||
|
||||
Paste logs or upload screenshots here.
|
||||
|
||||
```txt
|
||||
Paste logs here
|
||||
66
.gitea/ISSUE_TEMPLATE/bug_report_mobile.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: Bug Report - Mobile
|
||||
about: Report something that is broken or not working correctly
|
||||
title: "[BUG - Mobile] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- bug
|
||||
- mobile
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
Briefly explain the issue.
|
||||
|
||||
---
|
||||
|
||||
# Steps To Reproduce
|
||||
|
||||
1. Go to ...
|
||||
2. Click ...
|
||||
3. Scan ...
|
||||
4. Error occurs ...
|
||||
|
||||
---
|
||||
|
||||
# Expected Behavior
|
||||
|
||||
What should have happened?
|
||||
|
||||
---
|
||||
|
||||
# Actual Behavior
|
||||
|
||||
What actually happened instead?
|
||||
|
||||
---
|
||||
|
||||
# Severity
|
||||
|
||||
- [ ] Low
|
||||
- [ ] Medium
|
||||
- [ ] High
|
||||
- [ ] Critical
|
||||
|
||||
---
|
||||
|
||||
# Environment
|
||||
|
||||
Example:
|
||||
|
||||
- Production
|
||||
- Development
|
||||
- Zebra Scanner
|
||||
- Mobile Device
|
||||
- Windows Server
|
||||
- Docker
|
||||
|
||||
---
|
||||
|
||||
# Logs / Screenshots
|
||||
|
||||
Paste logs or upload screenshots here.
|
||||
|
||||
```txt
|
||||
Paste logs here
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
name: Bug Report
|
||||
name: Bug Report - Server
|
||||
about: Report something that is broken or not working correctly
|
||||
title: "[BUG] "
|
||||
title: "[BUG - Server] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- bug
|
||||
- server
|
||||
|
||||
---
|
||||
|
||||
48
.gitea/ISSUE_TEMPLATE/enhancement_frontend.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: Enhancement - Frontend
|
||||
about: Improve or refine an existing feature
|
||||
title: "[ENHANCEMENT - Frontend] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- enhancement
|
||||
- frontend
|
||||
---
|
||||
|
||||
# Existing Feature
|
||||
|
||||
What current feature or workflow is being improved?
|
||||
|
||||
Example:
|
||||
|
||||
- Notifications
|
||||
- Scanner Login
|
||||
- Release Monitor
|
||||
- Printing
|
||||
- Auth
|
||||
|
||||
---
|
||||
|
||||
# Proposed Improvement
|
||||
|
||||
Describe the improvement.
|
||||
|
||||
---
|
||||
|
||||
# Expected Benefit
|
||||
|
||||
Why would this improvement help?
|
||||
|
||||
---
|
||||
|
||||
# Impact
|
||||
|
||||
- [ ] Small
|
||||
- [ ] Medium
|
||||
- [ ] Large
|
||||
|
||||
---
|
||||
|
||||
# Additional Notes
|
||||
|
||||
Anything else worth mentioning.
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
name: Enhancement
|
||||
name: Enhancement - Mobile
|
||||
about: Improve or refine an existing feature
|
||||
title: "[ENHANCEMENT] "
|
||||
title: "[ENHANCEMENT - Mobile] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- enhancement
|
||||
- mobile
|
||||
---
|
||||
|
||||
# Existing Feature
|
||||
48
.gitea/ISSUE_TEMPLATE/enhancement_server.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: Enhancement - Server
|
||||
about: Improve or refine an existing feature
|
||||
title: "[ENHANCEMENT - Server] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- enhancement
|
||||
- server
|
||||
---
|
||||
|
||||
# Existing Feature
|
||||
|
||||
What current feature or workflow is being improved?
|
||||
|
||||
Example:
|
||||
|
||||
- Notifications
|
||||
- Scanner Login
|
||||
- Release Monitor
|
||||
- Printing
|
||||
- Auth
|
||||
|
||||
---
|
||||
|
||||
# Proposed Improvement
|
||||
|
||||
Describe the improvement.
|
||||
|
||||
---
|
||||
|
||||
# Expected Benefit
|
||||
|
||||
Why would this improvement help?
|
||||
|
||||
---
|
||||
|
||||
# Impact
|
||||
|
||||
- [ ] Small
|
||||
- [ ] Medium
|
||||
- [ ] Large
|
||||
|
||||
---
|
||||
|
||||
# Additional Notes
|
||||
|
||||
Anything else worth mentioning.
|
||||
41
.gitea/ISSUE_TEMPLATE/feature_request_frontend.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Feature Request - Frontend
|
||||
about: Suggest a brand new feature or module
|
||||
title: "[FEATURE - Frontend] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- feature
|
||||
- frontend
|
||||
---
|
||||
|
||||
# Problem Statement
|
||||
|
||||
What problem are you trying to solve?
|
||||
|
||||
---
|
||||
|
||||
# Proposed Solution
|
||||
|
||||
Describe the feature you would like added.
|
||||
|
||||
---
|
||||
|
||||
# Alternatives Considered
|
||||
|
||||
Any other ideas, workarounds, or approaches?
|
||||
|
||||
---
|
||||
|
||||
# Priority
|
||||
|
||||
- [ ] Nice to Have
|
||||
- [ ] Medium Priority
|
||||
- [ ] High Priority
|
||||
- [ ] Critical
|
||||
|
||||
---
|
||||
|
||||
# Additional Context
|
||||
|
||||
Add mockups, screenshots, examples, or notes here.
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
name: Feature Request
|
||||
name: Feature Request - Mobile
|
||||
about: Suggest a brand new feature or module
|
||||
title: "[FEATURE] "
|
||||
title: "[FEATURE - Mobile] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- feature
|
||||
- mobile
|
||||
---
|
||||
|
||||
# Problem Statement
|
||||
41
.gitea/ISSUE_TEMPLATE/feature_request_server.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Feature Request - Server
|
||||
about: Suggest a brand new feature or module
|
||||
title: "[FEATURE - Server] "
|
||||
ref: "main"
|
||||
labels:
|
||||
|
||||
- feature
|
||||
- server
|
||||
---
|
||||
|
||||
# Problem Statement
|
||||
|
||||
What problem are you trying to solve?
|
||||
|
||||
---
|
||||
|
||||
# Proposed Solution
|
||||
|
||||
Describe the feature you would like added.
|
||||
|
||||
---
|
||||
|
||||
# Alternatives Considered
|
||||
|
||||
Any other ideas, workarounds, or approaches?
|
||||
|
||||
---
|
||||
|
||||
# Priority
|
||||
|
||||
- [ ] Nice to Have
|
||||
- [ ] Medium Priority
|
||||
- [ ] High Priority
|
||||
- [ ] Critical
|
||||
|
||||
---
|
||||
|
||||
# Additional Context
|
||||
|
||||
Add mockups, screenshots, examples, or notes here.
|
||||
36
CHANGELOG.md
@@ -1,5 +1,41 @@
|
||||
# All Changes to LST can be found below.
|
||||
|
||||
## [0.1.0-alpha.2](https://git.tuffraid.net/cowch/lst_v3/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) (2026-05-23)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **build:** gives a rabbit hole error
|
||||
|
||||
### 🌟 Enhancements
|
||||
|
||||
* **opendock:** added in new article link setup for fine tuning how od works ([3892111](https://git.tuffraid.net/cowch/lst_v3/commits/389211186f00cb8a6fdd5de092a944fa7e5898aa))
|
||||
* **opendock:** scheduing updates ([1840ac5](https://git.tuffraid.net/cowch/lst_v3/commits/1840ac5e580c726c452216480b6e14e7c52a0f35)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
* **build:** crashes when files changed :( ([1f6637c](https://git.tuffraid.net/cowch/lst_v3/commits/1f6637c512dcd465c5000f8d1baaa8e76766edc1)), closes [#24](https://git.tuffraid.net/cowch/lst_v3/issues/24)
|
||||
* **docs:** wrong location for images ([057a570](https://git.tuffraid.net/cowch/lst_v3/commits/057a570e43a8e1763652d98244c90999c3fccd42))
|
||||
* **mobile:** correction to axios helper ([ecfbda9](https://git.tuffraid.net/cowch/lst_v3/commits/ecfbda9036f3d68c93e9c1d81021efa8093f18e2))
|
||||
* **sql queries:** disable job would error so now we will check if it exists before trying to kill it ([636daae](https://git.tuffraid.net/cowch/lst_v3/commits/636daaed0adeda908e7e850a4f5bb20d7bbef861))
|
||||
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
* **mobile:** updated imgs to be a little smaller ([3a27fd8](https://git.tuffraid.net/cowch/lst_v3/commits/3a27fd8542c3fa4ad5520532c2f10c6e3eaa951c))
|
||||
|
||||
|
||||
### 🛠️ Code Refactor
|
||||
|
||||
* **mobile:** added missing error to the scanner ([52974aa](https://git.tuffraid.net/cowch/lst_v3/commits/52974aa0b4f21431777b773200a57f185b4babd2))
|
||||
* **opendock:** changes to how we do the intergration scheduling ([cd67c4d](https://git.tuffraid.net/cowch/lst_v3/commits/cd67c4de80b6f0244afc639a7360e9dc2ba97a21)), closes [#23](https://git.tuffraid.net/cowch/lst_v3/issues/23)
|
||||
|
||||
|
||||
### 📈 Project changes
|
||||
|
||||
* **docker:** changes to the ignore file ([71c8306](https://git.tuffraid.net/cowch/lst_v3/commits/71c83062cb644796ebbfd845084ac6c019206faa))
|
||||
|
||||
## [0.1.0-alpha.1](https://git.tuffraid.net/cowch/lst_v3/compare/v0.1.0-alpha.0...v0.1.0-alpha.1) (2026-05-19)
|
||||
|
||||
|
||||
|
||||
19
README.md
@@ -7,7 +7,7 @@
|
||||
Quick summary of current rewrite/migration goal.
|
||||
|
||||
- **Phase:** Backend rewrite
|
||||
- **Last updated:** 2026-04-06
|
||||
- **Last updated:** 2026-05-27
|
||||
|
||||
---
|
||||
|
||||
@@ -17,20 +17,20 @@ Quick summary of current rewrite/migration goal.
|
||||
|----------|--------------|--------|
|
||||
| User Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress |
|
||||
| User Profile | ~~Edit profile~~, upload avatar | 🟨 In Progress |
|
||||
| User Admin | Edit user, create user, remove user, alplaprod user integration | ⏳ Not Started |
|
||||
| User Admin | ~~Edit user~~, ~~create user~~, remove user, alplaprod user integration | ⏳ Not Started |
|
||||
| Notifications | ~~Subscribe~~, ~~Create~~, ~~Update~~, ~~~~Remove~~, Manual Trigger | 🟨 In Progress |
|
||||
| Datamart | ~~Create~~, ~~Update~~, ~~Run~~, Deactivate | 🟨 In Progress |
|
||||
| Frontend | Analytics and charts | ⏳ Not Started |
|
||||
| Docs | Instructions and trouble shooting | ⏳ Not Started |
|
||||
| One Click Print | Get printers, monitor printers, label process, material process, Special processes | ⏳ Not Started |
|
||||
| One Click Print | ~~Get printers~~, monitor printers, label process, material process, Special processes | 🟨 In Progress |
|
||||
| Silo Adjustments | Create, History, Comments | ⏳ Not Started |
|
||||
| Demand Management | Orders, Forecast, Special Mappings, Create trucks, Load Trucks (tablet scanning) | ⏳ Not Started |
|
||||
| Open Docks | Integrations | ⏳ Not Started |
|
||||
| Open Docks | Integrations | 🟨 In Progress |
|
||||
| Transport Insight | Integrations | ⏳ Not Started |
|
||||
| Quality Request Tool | Add Pallet, Monitor for moved, status changes, alerts | ⏳ Not Started |
|
||||
| Logistics | Consume material, return and print, label info, relocate | ⏳ Not Started |
|
||||
| EOM | Endpoints, Report Pull for finance | ⏳ Not Started |
|
||||
| OCME | Custom integration | ⏳ Not Started |
|
||||
| ~~OCME~~ | ~~Custom integration~~ | Canceled |
|
||||
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
||||
| System | Tests,Builds, Updates, Remote Logging, DB Backups, Alerting | ⏳ Not Started |
|
||||
|
||||
@@ -47,4 +47,13 @@ How to run the current version of the app.
|
||||
git clone https://git.tuffraid.net/cowch/lst_v3.git
|
||||
cd lst_v3
|
||||
npm install
|
||||
```
|
||||
|
||||
Rename the .env-example to .env
|
||||
|
||||
Update all the fields
|
||||
|
||||
```bash
|
||||
npm run dev:db:migrate
|
||||
npm run dev
|
||||
```
|
||||
@@ -46,7 +46,8 @@ const createApp = async () => {
|
||||
server: ${JSON.stringify(umamiConfig.server ?? "unknown")},
|
||||
appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")},
|
||||
umamiHost: ${JSON.stringify(umamiConfig.umamiHost ?? "")},
|
||||
umamiWebsiteId: ${JSON.stringify(umamiConfig.umamiWebsiteId ?? "")}
|
||||
umamiWebsiteId: ${JSON.stringify(umamiConfig.umamiWebsiteId ?? "")},
|
||||
timezone: ${JSON.stringify(process.env.TIMEZONE ?? "America/Chicago")}
|
||||
};
|
||||
`);
|
||||
});
|
||||
|
||||
0
backend/datamart/datamart.controller.test.ts
Normal file
68
backend/datamart/getDatamart.route.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import express from "express";
|
||||
import request from "supertest";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../db/db.controller.js", () => ({
|
||||
db: {},
|
||||
}));
|
||||
|
||||
vi.mock("../logger/logger.controller.js", () => ({
|
||||
createLogger: () => ({
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("./datamart.controller.js", () => ({
|
||||
runDatamartQuery: vi.fn(async ({ name, options }) => ({
|
||||
success: true,
|
||||
message: `Ran ${name}`,
|
||||
data: {
|
||||
name,
|
||||
options,
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
import { runDatamartQuery } from "./datamart.controller.js";
|
||||
import getDatamartRoute from "./getDatamart.route.js";
|
||||
|
||||
function createTestApp() {
|
||||
const app = express();
|
||||
|
||||
app.use(express.json());
|
||||
app.use("/datamart", getDatamartRoute);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
describe("GET /datamart/:name", () => {
|
||||
it("runs a datamart query by name and returns api response", async () => {
|
||||
const app = createTestApp();
|
||||
|
||||
const res = await request(app).get("/datamart/orders").query({
|
||||
value: "123",
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
expect(runDatamartQuery).toHaveBeenCalledWith({
|
||||
name: "orders",
|
||||
options: {
|
||||
value: "123",
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.body.success).toBe(true);
|
||||
expect(res.body.module).toBe("datamart");
|
||||
expect(res.body.subModule).toBe("query");
|
||||
expect(res.body.data).toEqual({
|
||||
name: "orders",
|
||||
options: {
|
||||
value: "123",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
|
||||
import * as opendockAVCheck from "./schema/opendock_articleSetup.js";
|
||||
import * as scanUserSchema from "./schema/scanUsers.js";
|
||||
import * as settingsSchema from "./schema/settings.schema.js";
|
||||
|
||||
const dbURL = `postgres://${process.env.DATABASE_USER}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}:${process.env.DATABASE_PORT}/${process.env.DATABASE_DB}`;
|
||||
|
||||
@@ -20,5 +21,7 @@ const queryClient = postgres(dbURL, {
|
||||
export const db = drizzle(queryClient, {
|
||||
schema: {
|
||||
...scanUserSchema,
|
||||
...settingsSchema,
|
||||
...opendockAVCheck,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,15 +17,15 @@ export const alplaPurchaseHistory = pgTable("alpla_purchase_history", {
|
||||
status: integer("status"),
|
||||
statusText: text("status_text"),
|
||||
journalNum: integer("journal_num"),
|
||||
add_date: timestamp("add_date").defaultNow(),
|
||||
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||
add_user: text("add_user"),
|
||||
upd_user: text("upd_user"),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
remark: text("remark"),
|
||||
approvedStatus: text("approved_status").default("new"),
|
||||
position: jsonb("position").default([]),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
updatedAt: timestamp("updated_at").defaultNow(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
export const alplaPurchaseHistorySchema =
|
||||
|
||||
@@ -3,7 +3,9 @@ import { integer, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
export const analytics = pgTable("analytics", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.defaultNow()
|
||||
.notNull(),
|
||||
|
||||
method: text("method").notNull(),
|
||||
routePattern: text("route_pattern").notNull(),
|
||||
|
||||
@@ -16,13 +16,13 @@ export const jobAuditLog = pgTable(
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
jobName: text("job_name"),
|
||||
startedAt: timestamp("start_at"),
|
||||
finishedAt: timestamp("finished_at"),
|
||||
finishedAt: timestamp("finished_at", { withTimezone: true }),
|
||||
durationMs: integer("duration_ms"),
|
||||
status: text("status"), //success | error
|
||||
errorMessage: text("error_message"),
|
||||
errorStack: text("error_stack"),
|
||||
metadata: jsonb("meta_data"),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
},
|
||||
(table) => {
|
||||
return {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const user = pgTable("user", {
|
||||
emailVerified: boolean("email_verified").default(false).notNull(),
|
||||
image: text("image"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at")
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||
.defaultNow()
|
||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||
.notNull(),
|
||||
|
||||
@@ -6,5 +6,5 @@ export const deploymentHistory = pgTable("deployment_history", {
|
||||
buildNumber: integer("build_number").notNull(),
|
||||
status: text("status").notNull(), // started, success, failed
|
||||
message: text("message"),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
@@ -28,11 +28,15 @@ export const analyticsDaily = pgTable(
|
||||
avgDurationMs: integer("avg_duration_ms").notNull(),
|
||||
maxDurationMs: integer("max_duration_ms").notNull(),
|
||||
|
||||
firstHitAt: timestamp("first_hit_at").notNull(),
|
||||
lastHitAt: timestamp("last_hit_at").notNull(),
|
||||
firstHitAt: timestamp("first_hit_at", { withTimezone: true }).notNull(),
|
||||
lastHitAt: timestamp("last_hit_at", { withTimezone: true }).notNull(),
|
||||
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.defaultNow()
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||
.defaultNow()
|
||||
.notNull(),
|
||||
},
|
||||
(table) => [
|
||||
unique("analytics_daily_business_route_unique").on(
|
||||
|
||||
@@ -18,9 +18,9 @@ export const datamart = pgTable("datamart", {
|
||||
active: boolean("active").default(true),
|
||||
options: text("options").default(""),
|
||||
public: boolean("public_access").default(false),
|
||||
add_date: timestamp("add_date").defaultNow(),
|
||||
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||
add_user: text("add_user").default("lst-system"),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
upd_user: text("upd_user").default("lst-system"),
|
||||
});
|
||||
|
||||
|
||||
22
backend/db/schema/dockdoor.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { boolean, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import type { z } from "zod";
|
||||
|
||||
export const dockDoorScanners = pgTable("dock_door_scanners", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
ip: text("ip").notNull(),
|
||||
name: text("name").unique(),
|
||||
dockId: text("dock_id"),
|
||||
active: boolean("active").default(true),
|
||||
currentLoadingOrder: text("current_loading_order").default(""),
|
||||
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||
add_user: text("add_user").default("lst-system"),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
upd_user: text("upd_user").default("lst-system"),
|
||||
});
|
||||
|
||||
export const dockDoorScannersSchema = createSelectSchema(dockDoorScanners);
|
||||
export const newDockDoorScannersSchema = createInsertSchema(dockDoorScanners);
|
||||
|
||||
export type DockDoorScanners = z.infer<typeof dockDoorScannersSchema>;
|
||||
export type NewDockDoorScanners = z.infer<typeof newDockDoorScannersSchema>;
|
||||
@@ -20,7 +20,7 @@ export const invHistoricalData = pgTable("inv_historical_data", {
|
||||
whseId: text("whse_id").default(""),
|
||||
whseName: text("whse_name").default("missing whseName"),
|
||||
upd_user: text("upd_user").default("lst-system"),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
export const invHistoricalDataSchema = createSelectSchema(invHistoricalData);
|
||||
|
||||
@@ -18,7 +18,7 @@ export const logs = pgTable("logs", {
|
||||
stack: jsonb("stack").default([]),
|
||||
checked: boolean("checked").default(false),
|
||||
hostname: text("hostname"),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
export const logSchema = createSelectSchema(logs);
|
||||
|
||||
@@ -14,14 +14,17 @@ export const opendockApt = pgTable(
|
||||
"opendock_apt",
|
||||
{
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
release: integer("release").notNull().unique(),
|
||||
release: integer("release").notNull().unique("opendock_apt_release_unique"),
|
||||
openDockAptId: text("open_dock_apt_id").notNull(),
|
||||
appointment: jsonb("appointment").notNull().default([]),
|
||||
upd_date: timestamp("upd_date").notNull().defaultNow(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
releaseIdx: index("opendock_apt_release_idx").on(table.release),
|
||||
openDockAptIdIdx: index("opendock_apt_opendock_id_idx").on(
|
||||
table.openDockAptId,
|
||||
),
|
||||
50
backend/db/schema/opendock_articleSetup.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import {
|
||||
integer,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
unique,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import type { z } from "zod";
|
||||
|
||||
export const loadTypeEnum = pgEnum("load_type", ["drop", "live"]);
|
||||
|
||||
export const opendockArticleSetup = pgTable(
|
||||
"opendock_article_setup",
|
||||
{
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
av: integer("av").notNull(),
|
||||
description: text("description").notNull(),
|
||||
customer: text("customer").notNull(), // customer should be a concat of the ID - Desc
|
||||
customerDescription: text("customer_description").notNull(),
|
||||
loadType: loadTypeEnum("load_type").notNull().default("drop"),
|
||||
dock: text("dock").notNull(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
upd_user: text("upd_user").notNull().default("lst-system"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
add_user: text("add_user").notNull().default("lst-system"),
|
||||
},
|
||||
(table) => ({
|
||||
uniqueAvCustomer: unique("uq_opendock_article_setup_av_customer").on(
|
||||
table.av,
|
||||
table.customer,
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
export const opendockArticleSetupSchema =
|
||||
createSelectSchema(opendockArticleSetup);
|
||||
export const newOpendockArticleSetupSchema =
|
||||
createInsertSchema(opendockArticleSetup);
|
||||
|
||||
export type OpendockArticleSetup = z.infer<typeof opendockArticleSetupSchema>;
|
||||
export type NewOpendockArticleSetup = z.infer<
|
||||
typeof newOpendockArticleSetupSchema
|
||||
>;
|
||||
25
backend/db/schema/opendock_docks.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import type { z } from "zod";
|
||||
|
||||
export const opendockDockSetup = pgTable("opendock_dock_setup", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
dockID: text("dock_id").notNull(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
upd_user: text("upd_user").notNull().default("lst-system"),
|
||||
createdAt: timestamp("created_at", { withTimezone: true })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
add_user: text("add_user").notNull().default("lst-system"),
|
||||
});
|
||||
|
||||
export const opendockDockSetupSchema = createSelectSchema(opendockDockSetup);
|
||||
export const newOpendockDockSetupSchema = createInsertSchema(opendockDockSetup);
|
||||
|
||||
export type OpendockArticleSetup = z.infer<typeof opendockDockSetupSchema>;
|
||||
export type NewOpendockArticleSetup = z.infer<
|
||||
typeof newOpendockDockSetupSchema
|
||||
>;
|
||||
@@ -7,5 +7,5 @@ export const printerLog = pgTable("printer_log", {
|
||||
printerSN: text("printer_sn"),
|
||||
condition: text("condition").notNull(),
|
||||
message: text("message"),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
@@ -28,8 +28,8 @@ export const printerData = pgTable(
|
||||
printDelay: integer("printDelay").default(90),
|
||||
processes: jsonb("processes").default([]),
|
||||
printDelayOverride: boolean("print_delay_override").default(false), // this will be more for if we have the lot time active but want to over ride this single line for some reason
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
//uniqueIndex("emailUniqueIndex").on(sql`lower(${table.email})`),
|
||||
|
||||
@@ -30,8 +30,8 @@ export const scanUser = pgTable(
|
||||
role: mobileRoleEnum("role").notNull().default("user"),
|
||||
active: boolean("active").default(true),
|
||||
lastScan: timestamp("last_scan").defaultNow(),
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
},
|
||||
(table) => ({
|
||||
userNotificationUnique: unique("scan_user_unique").on(
|
||||
|
||||
@@ -13,7 +13,7 @@ export const scanLog = pgTable("scan_log", {
|
||||
status: text("status"),
|
||||
scannerVersion: text("scanner_version").default("0"),
|
||||
lines: jsonb("lines").default([]),
|
||||
add_Date: timestamp("add_date").defaultNow(),
|
||||
add_Date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
export const scanLogSchema = createSelectSchema(scanLog);
|
||||
|
||||
@@ -22,7 +22,7 @@ export const serverData = pgTable(
|
||||
contactPhone: text("contact_phone"),
|
||||
active: boolean("active").default(true),
|
||||
serverLoc: text("server_loc"),
|
||||
lastUpdated: timestamp("last_updated").defaultNow(),
|
||||
lastUpdated: timestamp("last_updated", { withTimezone: true }).defaultNow(),
|
||||
buildNumber: integer("build_number"),
|
||||
isUpgrading: boolean("is_upgrading").default(false),
|
||||
},
|
||||
|
||||
@@ -32,13 +32,13 @@ export const settings = pgTable(
|
||||
settingType: settingType(),
|
||||
seedVersion: integer("seed_version").default(1), // this is intended for if we want to update the settings.
|
||||
add_User: text("add_User").default("LST_System").notNull(),
|
||||
add_Date: timestamp("add_Date").defaultNow(),
|
||||
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||
upd_user: text("upd_User").default("LST_System").notNull(),
|
||||
upd_date: timestamp("upd_date").defaultNow(),
|
||||
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||
},
|
||||
(table) => [
|
||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||
uniqueIndex("name").on(table.name),
|
||||
uniqueIndex("settings_name_unique").on(table.name),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ import type z from "zod";
|
||||
export const appStats = pgTable("app_stats", {
|
||||
id: text("id").primaryKey().default("primary"),
|
||||
currentBuild: integer("current_build").notNull().default(1),
|
||||
lastBuildAt: timestamp("last_build_at"),
|
||||
lastDeployAt: timestamp("last_deploy_at"),
|
||||
lastBuildAt: timestamp("last_build_at", { withTimezone: true }),
|
||||
lastDeployAt: timestamp("last_deploy_at", { withTimezone: true }),
|
||||
building: boolean("building").notNull().default(false),
|
||||
updating: boolean("updating").notNull().default(false),
|
||||
lastUpdated: timestamp("last_updated").defaultNow(),
|
||||
lastUpdated: timestamp("last_updated", { withTimezone: true }).defaultNow(),
|
||||
meta: jsonb("meta").$type<Record<string, unknown>>().default({}),
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { addDays, subDays } from "date-fns";
|
||||
import { format } from "date-fns-tz";
|
||||
import { Router } from "express";
|
||||
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
r.get("/", async (_, res) => {
|
||||
const orders = await runProdApi({
|
||||
method: "post",
|
||||
endpoint: "/public/v1.0/OutboundDeliveries/LoadingOrders/Search",
|
||||
data: [
|
||||
{
|
||||
loadingDateFrom: format(subDays(new Date(Date.now()), 3), "yyyy-MM-dd"),
|
||||
loadingDateTo: format(addDays(new Date(Date.now()), 3), "yyyy-MM-dd"),
|
||||
states: [
|
||||
1, // planned
|
||||
],
|
||||
//isCommissioned: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "current Active loading orders",
|
||||
message: `Current active loading loaders.`,
|
||||
data: orders?.data ?? [],
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
export default r;
|
||||
70
backend/dockdoorScanning/dockdoor.closeLoadingOrder.route.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { Router } from "express";
|
||||
import z from "zod";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
const endLoading = z.object({
|
||||
loadingOrder: z.string(),
|
||||
dockId: z.string(),
|
||||
});
|
||||
|
||||
r.post("/", async (req, res) => {
|
||||
// close the loading order
|
||||
|
||||
// clear the loading order off the dock
|
||||
|
||||
try {
|
||||
const validated = endLoading.parse(req.body);
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.update(dockDoorScanners)
|
||||
.set({
|
||||
currentLoadingOrder: "",
|
||||
upd_date: sql`NOW()`,
|
||||
upd_user: req.user?.username,
|
||||
})
|
||||
.where(eq(dockDoorScanners.dockId, validated.dockId))
|
||||
.returning(),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrder",
|
||||
message: `Failed to updating the dock.`,
|
||||
data: (error as any) ?? [],
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrder",
|
||||
message: `Loading order ${validated.loadingOrder} was just closed.`,
|
||||
data: data ?? [],
|
||||
status: 200,
|
||||
});
|
||||
} catch (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrder",
|
||||
message: `Failed to start loading order.`,
|
||||
data: (error as any) ?? [],
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default r;
|
||||
25
backend/dockdoorScanning/dockdoor.loadUnits.route.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Router } from "express";
|
||||
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
import loadUnit from "./dockdoor.loadUnits.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
r.post("/", async (req, res) => {
|
||||
const unit = await loadUnit({
|
||||
dockId: req.body.dockId,
|
||||
runningNo: req.body.runningNo,
|
||||
});
|
||||
|
||||
return apiReturn(res, {
|
||||
success: unit.success,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingUnit",
|
||||
message: unit.message,
|
||||
data: unit?.data ?? [],
|
||||
status: unit.success ? 200 : 400,
|
||||
});
|
||||
});
|
||||
|
||||
export default r;
|
||||
143
backend/dockdoorScanning/dockdoor.loadUnits.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
// sends the units from the dock door scanner here.
|
||||
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||
import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
|
||||
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||
|
||||
// validate we are active
|
||||
|
||||
type Data = {
|
||||
dockId?: string;
|
||||
sscc?: string;
|
||||
runningNo?: string;
|
||||
};
|
||||
|
||||
const loadUnit = async (data: Data) => {
|
||||
// are we even active at this time?
|
||||
const dockDoorActive = await db.query.settings.findFirst({
|
||||
where: (u, { eq }) => eq(u.name, "dockDoorScanning"),
|
||||
});
|
||||
|
||||
if (!dockDoorActive?.active) {
|
||||
return returnFunc({
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadunit",
|
||||
message: "Dock door scanning feature is not active.",
|
||||
data: [],
|
||||
notify: false,
|
||||
room: "",
|
||||
});
|
||||
}
|
||||
// check if its a valids an sscc
|
||||
|
||||
if (data.sscc === "noread") {
|
||||
return returnFunc({
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadUnit",
|
||||
message:
|
||||
"Failed to load the unit to the truck, there was no pallet read.",
|
||||
data: [],
|
||||
notify: false,
|
||||
room: `dockDoorLoading:${data.dockId}`,
|
||||
});
|
||||
}
|
||||
|
||||
// check if we currently have a loading order attached to the dock door.
|
||||
const dock = await db
|
||||
.select()
|
||||
.from(dockDoorScanners)
|
||||
.where(eq(dockDoorScanners.dockId, data.dockId as string));
|
||||
|
||||
if (dock[0]?.currentLoadingOrder === "") {
|
||||
return returnFunc({
|
||||
success: true,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrders",
|
||||
message:
|
||||
"There are know current active loading orders please start one and try again.",
|
||||
data: [],
|
||||
notify: false,
|
||||
room: `dockDoorLoading:${data.dockId}`,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: pallet validation, check if we are on hold, then check if we have been in the staging warehouse for more than x time.
|
||||
|
||||
// if on hold stop the scan and send a bad read with the reason its on hold and what its on hold for, including coa.
|
||||
|
||||
// if precheck is active then check if we have a warehouse, then check if the pallet was in the warehouse for greater than the define min, all fails send a warning and still do the scan
|
||||
|
||||
// add the loading units
|
||||
try {
|
||||
const unitToScan = data.sscc
|
||||
? { sscc: data.sscc?.slice(2) }
|
||||
: { runningNo: Number(data.runningNo) };
|
||||
|
||||
const prod = (await runProdApi({
|
||||
method: "post",
|
||||
endpoint: `/public/v1.0/OutboundDeliveries/LoadingOrders/${dock[0]?.currentLoadingOrder}/LoadUnit`,
|
||||
data: [unitToScan],
|
||||
})) as any;
|
||||
|
||||
//emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data ?? []);
|
||||
|
||||
if (!prod?.success) {
|
||||
emitToRoom(`dockDoorLoading:${data.dockId}`, prod?.data.errors[0]);
|
||||
return returnFunc({
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadUnit",
|
||||
message: `Unit encountered an error while loading`,
|
||||
data: prod?.data.errors[0] as any,
|
||||
notify: false,
|
||||
//room: `dockDoorLoading:${data.dockId}`,
|
||||
});
|
||||
} else {
|
||||
const emitData = {
|
||||
message: `The unit ${prod.data.message.messageParams.runningNo} was loaded.`,
|
||||
data: prod.data,
|
||||
code: 0,
|
||||
};
|
||||
emitToRoom(`dockDoorLoading:${data.dockId}`, emitData as any);
|
||||
return returnFunc({
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "loadUnit",
|
||||
message: `Unit added to loading order`,
|
||||
data: [
|
||||
{
|
||||
message: `The unit ${prod.data.message.messageParams.runningNo} was loaded.`,
|
||||
data: prod.data,
|
||||
code: 0,
|
||||
},
|
||||
] as any,
|
||||
notify: false,
|
||||
//room: `dockDoorLoading:${data.dockId}`,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return returnFunc({
|
||||
success: true,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadUnit",
|
||||
message: `Failed to load unit`,
|
||||
data: error as any,
|
||||
notify: false,
|
||||
room: `dockDoorLoading:${data.dockId}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default loadUnit;
|
||||
48
backend/dockdoorScanning/dockdoor.routes.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Express } from "express";
|
||||
import { featureCheck } from "../middleware/featureActive.middleware.js";
|
||||
import activeLoadingOrders from "./dockdoor.activeLoadingOrders.route.js";
|
||||
import closeLoadingOrder from "./dockdoor.closeLoadingOrder.route.js";
|
||||
import load from "./dockdoor.loadUnits.route.js";
|
||||
import startLoad from "./dockdoor.startLoad.route.js";
|
||||
import prodDocks from "./dockdoors.docks.route.js";
|
||||
import docks from "./dockdoors.route.js";
|
||||
|
||||
export const setupDockDoorRoutes = (baseUrl: string, app: Express) => {
|
||||
//stats will be like this as we dont need to change this
|
||||
|
||||
app.use(
|
||||
`${baseUrl}/api/dockDoor/scanners`,
|
||||
featureCheck("dockDoorScanning"),
|
||||
|
||||
docks,
|
||||
);
|
||||
|
||||
app.use(
|
||||
`${baseUrl}/api/dockDoor/finishOrder`,
|
||||
featureCheck("dockDoorScanning"),
|
||||
closeLoadingOrder,
|
||||
);
|
||||
app.use(
|
||||
`${baseUrl}/api/dockDoor/activeLoadingOrders`,
|
||||
featureCheck("dockDoorScanning"),
|
||||
activeLoadingOrders,
|
||||
);
|
||||
app.use(
|
||||
`${baseUrl}/api/dockDoor/startLoad`,
|
||||
featureCheck("dockDoorScanning"),
|
||||
startLoad,
|
||||
);
|
||||
app.use(
|
||||
`${baseUrl}/api/dockDoor/docks`,
|
||||
featureCheck("dockDoorScanning"),
|
||||
prodDocks,
|
||||
);
|
||||
|
||||
app.use(
|
||||
`${baseUrl}/api/dockDoor/loadUnit`,
|
||||
featureCheck("dockDoorScanning"),
|
||||
load,
|
||||
);
|
||||
|
||||
// all other system should be under /api/system/*
|
||||
};
|
||||
66
backend/dockdoorScanning/dockdoor.startLoad.route.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { Router } from "express";
|
||||
import z from "zod";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
const startLoading = z.object({
|
||||
loadingOrder: z.string(),
|
||||
dockId: z.string(),
|
||||
});
|
||||
|
||||
r.post("/", async (req, res) => {
|
||||
try {
|
||||
const validated = startLoading.parse(req.body);
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
db
|
||||
.update(dockDoorScanners)
|
||||
.set({
|
||||
currentLoadingOrder: validated.loadingOrder,
|
||||
upd_date: sql`NOW()`,
|
||||
upd_user: req.user?.username,
|
||||
})
|
||||
.where(eq(dockDoorScanners.dockId, validated.dockId))
|
||||
.returning(),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrder",
|
||||
message: `Failed to updating the dock.`,
|
||||
data: (error as any) ?? [],
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrder",
|
||||
message: `Loading order ${validated.loadingOrder} was just added to dockId ${validated.dockId}.`,
|
||||
data: data ?? [],
|
||||
status: 200,
|
||||
});
|
||||
} catch (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "loadingOrder",
|
||||
message: `Failed to start loading order.`,
|
||||
data: (error as any) ?? [],
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default r;
|
||||
54
backend/dockdoorScanning/dockdoors.docks.route.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Router } from "express";
|
||||
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||
import {
|
||||
type SqlQuery,
|
||||
sqlQuerySelector,
|
||||
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
r.get("/", async (_, res) => {
|
||||
const activeDocks = sqlQuerySelector(`outbound.docks`) as SqlQuery;
|
||||
|
||||
if (!activeDocks.success) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "docks",
|
||||
message: `There was an error getting the docks query.`,
|
||||
data: [],
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const { data, error } = await tryCatch(
|
||||
prodQuery(activeDocks.query, "Current Active Docks"),
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "newDock",
|
||||
message: `There was an error getting the docks.`,
|
||||
data: (error as any) ?? ([] as any),
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "docks",
|
||||
message: `Current active docks.`,
|
||||
data: (data.data as any) ?? ([] as any),
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
export default r;
|
||||
76
backend/dockdoorScanning/dockdoors.route.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Router } from "express";
|
||||
import z from "zod";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
const newDockScanner = z.object({
|
||||
ip: z.string(),
|
||||
name: z.string(),
|
||||
dockId: z.string(),
|
||||
});
|
||||
|
||||
r.get("/", async (_, res) => {
|
||||
try {
|
||||
const docks = await db
|
||||
.select()
|
||||
.from(dockDoorScanners)
|
||||
.orderBy(dockDoorScanners.name);
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "lane check",
|
||||
message: `All dock Doors.`,
|
||||
data: docks ?? [],
|
||||
status: 200,
|
||||
});
|
||||
} catch (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "newDock",
|
||||
message: `There was an error adding in the new dock.`,
|
||||
data: error ?? ([] as any),
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
r.post("/", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const validated = newDockScanner.parse(req.body);
|
||||
|
||||
const newDock = await db
|
||||
.insert(dockDoorScanners)
|
||||
.values(validated)
|
||||
.returning();
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "dockdoor",
|
||||
subModule: "lane check",
|
||||
message: `${validated.name} was just added.`,
|
||||
data: newDock ?? [],
|
||||
status: 200,
|
||||
});
|
||||
} catch (error) {
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error",
|
||||
module: "dockdoor",
|
||||
subModule: "newDock",
|
||||
message: `There was an error adding in the new dock.`,
|
||||
data: error ?? ([] as any),
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default r;
|
||||
@@ -37,7 +37,7 @@ const dbStream = new Writable({
|
||||
subModule: obj?.subModule?.toLowerCase(),
|
||||
hostname: obj?.hostname?.toLowerCase(),
|
||||
message: obj.msg,
|
||||
stack: obj?.stack,
|
||||
stack: obj?.stack || obj?.error, // this will add in the error or stack depending on how we pass it.
|
||||
})
|
||||
.returning(),
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ const historicalInvImport = async () => {
|
||||
});
|
||||
}
|
||||
|
||||
if (data?.length === 0) {
|
||||
if (data.length === 0) {
|
||||
const avSQLQuery = sqlQuerySelector(`datamart.activeArticles`) as SqlQuery;
|
||||
|
||||
if (!avSQLQuery.success) {
|
||||
@@ -139,7 +139,7 @@ const historicalInvImport = async () => {
|
||||
subModule: "inv",
|
||||
message: `Error adding historical data to lst db`,
|
||||
data: errorImport as any,
|
||||
notify: true,
|
||||
notify: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,27 @@ router.get("/ehs/xml", (_, res) => {
|
||||
return res.sendFile(xmlPath);
|
||||
});
|
||||
|
||||
router.get("/upgrade/android/13", (_, res) => {
|
||||
router.get("/android/upgrade/11", (_, res) => {
|
||||
const apkPath = path.join(
|
||||
downloadDir,
|
||||
"HE_FULL_UPDATE_11-70-20.00-RG-U00-STD-HEL-04.zip",
|
||||
);
|
||||
|
||||
if (!fs.existsSync(apkPath)) {
|
||||
return res.status(404).json({ success: false, message: "APK not found" });
|
||||
}
|
||||
|
||||
//res.setHeader("Content-Type", "application/vnd.android.package-archive");
|
||||
res.setHeader("Content-Type", "application/zip");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename="HE_FULL_UPDATE_11.zip"`,
|
||||
);
|
||||
|
||||
return res.sendFile(apkPath);
|
||||
});
|
||||
|
||||
router.get("/android/upgrade/13", (_, res) => {
|
||||
const apkPath = path.join(
|
||||
downloadDir,
|
||||
"HE_FULL_UPDATE_13-51-16.00-TG-U00-STD-HEL-04.zip",
|
||||
@@ -82,7 +102,7 @@ router.get("/upgrade/android/13", (_, res) => {
|
||||
return res.sendFile(apkPath);
|
||||
});
|
||||
|
||||
router.get("/upgrade/android/14", (_, res) => {
|
||||
router.get("/android/upgrade/14", (_, res) => {
|
||||
const apkPath = path.join(
|
||||
downloadDir,
|
||||
"HE_FULL_UPDATE_14-38-04.00-UG-U15-STD-HEL-04.zip",
|
||||
|
||||
@@ -13,7 +13,7 @@ router.post("/", async (req, res) => {
|
||||
await db
|
||||
.update(scanUser)
|
||||
.set({ lastScan: sql`NOW()` })
|
||||
.where(eq(scanUser.name, body.name));
|
||||
.where(eq(scanUser.name, body.user));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { addHours } from "date-fns";
|
||||
import { formatInTimeZone } from "date-fns-tz";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { opendockApt } from "../db/schema/opendock.schema.js";
|
||||
import { opendockApt } from "../db/schema/opendock_apt.schema.js";
|
||||
import { settings } from "../db/schema/settings.schema.js";
|
||||
import { createLogger } from "../logger/logger.controller.js";
|
||||
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||
@@ -27,7 +27,18 @@ type Releases = {
|
||||
Quantity: number;
|
||||
LineItemArticleWeight: number;
|
||||
CustomerReleaseNumber: string;
|
||||
DeliveryAddressDescription: string;
|
||||
DeliveryAddressHumanReadableId: string;
|
||||
AdditionalInformation1: string;
|
||||
};
|
||||
|
||||
// TODO: add these docs into the db
|
||||
const actaulDocks = [
|
||||
{ name: "cermac", dockId: "bcb17fae-0b1a-47a7-9fbf-594c5ebccce9" },
|
||||
{ name: "matrix", dockId: "3e32cbfc-49f4-4138-b491-9d5df9c94754" },
|
||||
{ name: "gerber", dockId: "9109e789-6c15-4cd9-87cb-de1b18627b6d" },
|
||||
{ name: "rb", dockId: "6be02526-6183-4789-a73f-e0aa155e6d1e" },
|
||||
];
|
||||
const timeZone = process.env.TIMEZONE as string;
|
||||
const TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000;
|
||||
const log = createLogger({ module: "opendock", subModule: "releaseMonitor" });
|
||||
@@ -61,6 +72,7 @@ let lastCheck = formatInTimeZone(
|
||||
// };
|
||||
|
||||
const postRelease = async (release: Releases) => {
|
||||
log.debug({}, `Release: ${release.ReleaseNumber} is about to be validated`);
|
||||
if (!odToken.odToken) {
|
||||
log.info({}, "Getting Auth Token");
|
||||
await getToken();
|
||||
@@ -73,6 +85,62 @@ const postRelease = async (release: Releases) => {
|
||||
log.info({}, "Refreshing Auth Token");
|
||||
await getToken();
|
||||
}
|
||||
|
||||
// load validation checks
|
||||
const defaultDock = await db.query.settings.findFirst({
|
||||
where: (u, { eq }) => eq(u.name, "defaultLoadType"),
|
||||
});
|
||||
|
||||
// check if the release has the loadtype in it
|
||||
const releaseLoadtypeCheck = (release.AdditionalInformation1 ?? "")
|
||||
.toLowerCase()
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.includes("drop");
|
||||
|
||||
// allowed to schedule now, as long as we see od in here somewhere
|
||||
const releaseOkToSchedule = (release.AdditionalInformation1 ?? "")
|
||||
.toLowerCase()
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.includes("od");
|
||||
|
||||
// dock was sent over
|
||||
const releaseDockInfo = actaulDocks.some((dock) =>
|
||||
(release.AdditionalInformation1 ?? "")
|
||||
.toLowerCase()
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.includes(dock.name.toLowerCase()),
|
||||
);
|
||||
|
||||
const opendDockArticleCheck = await db.query.opendockArticleSetup.findFirst({
|
||||
where: (table, { and, eq }) =>
|
||||
and(
|
||||
eq(table.av, release.LineItemHumanReadableId),
|
||||
eq(table.customer, release.DeliveryAddressHumanReadableId),
|
||||
),
|
||||
});
|
||||
|
||||
// selected dock
|
||||
const releaseDocks = (release.AdditionalInformation1 ?? "")
|
||||
.toLowerCase()
|
||||
.split(",")
|
||||
.map((x) => x.trim());
|
||||
|
||||
const matchedDock = actaulDocks.find((dock) =>
|
||||
releaseDocks.includes(dock.name.toLowerCase()),
|
||||
);
|
||||
|
||||
const setDock =
|
||||
// validate we dont have the dock in the release
|
||||
releaseDockInfo
|
||||
? matchedDock?.dockId
|
||||
: // validate we dont have the dock in the aritcle check
|
||||
(actaulDocks.find((d) => d.name === opendDockArticleCheck?.dock)
|
||||
?.dockId ?? process.env.DEFAULT_DOCK);
|
||||
|
||||
// TODO: add in docks from lst db here to make it more universal for the team
|
||||
/**
|
||||
* ReleaseState
|
||||
* 0 = open
|
||||
@@ -101,7 +169,8 @@ const postRelease = async (release: Releases) => {
|
||||
: release.DeliveryState === 4 && "Completed",
|
||||
userId: process.env.DEFAULT_CARRIER, // this should be the carrierid
|
||||
loadTypeId: process.env.DEFAULT_LOAD_TYPE, // well get this and make it a default one
|
||||
dockId: process.env.DEFAULT_DOCK, // this the warehouse we want it in to start out
|
||||
// TODO: look in the remarks in the release and if its says
|
||||
dockId: setDock, // this the warehouse we want it in to start out
|
||||
refNumbers: [release.ReleaseNumber],
|
||||
//refNumber: release.ReleaseNumber,
|
||||
start: release.DeliveryDate,
|
||||
@@ -115,6 +184,19 @@ const postRelease = async (release: Releases) => {
|
||||
},
|
||||
units: null,
|
||||
customFields: [
|
||||
{
|
||||
name: "strCustomer",
|
||||
type: "str",
|
||||
label: "Customer",
|
||||
value: `${release.DeliveryAddressDescription}`,
|
||||
description: "Who is the customer ",
|
||||
placeholder: "",
|
||||
dropDownValues: [],
|
||||
minLengthOrValue: 1,
|
||||
hiddenFromCarrier: false,
|
||||
requiredForCarrier: false,
|
||||
requiredForWarehouse: false,
|
||||
},
|
||||
{
|
||||
name: "strArticle",
|
||||
type: "str",
|
||||
@@ -190,60 +272,186 @@ const postRelease = async (release: Releases) => {
|
||||
|
||||
if (existing) {
|
||||
const id = existing.openDockAptId;
|
||||
try {
|
||||
const response = await axios.patch(
|
||||
`${process.env.OPENDOCK_URL}/appointment/${id}`,
|
||||
newDockApt,
|
||||
{
|
||||
headers: {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
Authorization: `Bearer ${odToken.odToken}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status === 400) {
|
||||
log.error({}, response.data.data.message);
|
||||
if (
|
||||
(releaseLoadtypeCheck ||
|
||||
opendDockArticleCheck?.loadType === "drop" ||
|
||||
defaultDock?.value === "drop") &&
|
||||
(release.DeliveryState === 0 || release.DeliveryState === 1)
|
||||
) {
|
||||
const setArrival = { ...newDockApt, status: "Arrived" };
|
||||
|
||||
// set to arrived
|
||||
try {
|
||||
const response = await axios.patch(
|
||||
`${process.env.OPENDOCK_URL}/appointment/${id}`,
|
||||
setArrival,
|
||||
{
|
||||
headers: {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
Authorization: `Bearer ${odToken.odToken}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status === 400) {
|
||||
log.error({}, response.data.data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the release in the db leaving as insert just incase something weird happened
|
||||
try {
|
||||
await db
|
||||
.insert(opendockApt)
|
||||
.values({
|
||||
release: release.ReleaseNumber,
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: opendockApt.release,
|
||||
set: {
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
upd_date: sql`NOW()`,
|
||||
},
|
||||
})
|
||||
.returning();
|
||||
|
||||
log.info({}, `${release.ReleaseNumber} was updated`);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
{ stack: e },
|
||||
`Error updating the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to many possibilities
|
||||
} catch (e: any) {
|
||||
//console.info(newDockApt);
|
||||
log.error(
|
||||
{ stack: e.response.data },
|
||||
`An error has occurred during patching of the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// update the release in the db leaving as insert just incase something weird happened
|
||||
// set to inprogress
|
||||
await delay(1500);
|
||||
try {
|
||||
await db
|
||||
.insert(opendockApt)
|
||||
.values({
|
||||
release: release.ReleaseNumber,
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: opendockApt.release,
|
||||
set: {
|
||||
const response = await axios.patch(
|
||||
`${process.env.OPENDOCK_URL}/appointment/${id}`,
|
||||
newDockApt,
|
||||
{
|
||||
headers: {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
Authorization: `Bearer ${odToken.odToken}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status === 400) {
|
||||
log.error({}, response.data.data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the release in the db leaving as insert just incase something weird happened
|
||||
try {
|
||||
await db
|
||||
.insert(opendockApt)
|
||||
.values({
|
||||
release: release.ReleaseNumber,
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
upd_date: sql`NOW()`,
|
||||
},
|
||||
})
|
||||
.returning();
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: opendockApt.release,
|
||||
set: {
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
upd_date: sql`NOW()`,
|
||||
},
|
||||
})
|
||||
.returning();
|
||||
|
||||
log.info({}, `${release.ReleaseNumber} was updated`);
|
||||
} catch (e) {
|
||||
log.info({}, `${release.ReleaseNumber} was updated`);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
{ stack: e },
|
||||
`Error updating the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to many possibilities
|
||||
} catch (e: any) {
|
||||
//console.info(newDockApt);
|
||||
log.error(
|
||||
{ error: e },
|
||||
`Error updating the release: ${release.ReleaseNumber}`,
|
||||
{ stack: e.response.data },
|
||||
`An error has occurred during patching of the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to many possibilities
|
||||
} catch (e: any) {
|
||||
//console.info(newDockApt);
|
||||
log.error(
|
||||
{ error: e.response.data },
|
||||
`An error has occurred during patching of the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const response = await axios.patch(
|
||||
`${process.env.OPENDOCK_URL}/appointment/${id}`,
|
||||
newDockApt,
|
||||
{
|
||||
headers: {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
Authorization: `Bearer ${odToken.odToken}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (response.status === 400) {
|
||||
log.error({}, response.data.data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// update the release in the db leaving as insert just incase something weird happened
|
||||
try {
|
||||
await db
|
||||
.insert(opendockApt)
|
||||
.values({
|
||||
release: release.ReleaseNumber,
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: opendockApt.release,
|
||||
set: {
|
||||
openDockAptId: response.data.data.id,
|
||||
appointment: response.data.data,
|
||||
upd_date: sql`NOW()`,
|
||||
},
|
||||
})
|
||||
.returning();
|
||||
|
||||
log.info({}, `${release.ReleaseNumber} was updated`);
|
||||
} catch (e) {
|
||||
log.error(
|
||||
{ stack: e },
|
||||
`Error updating the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to many possibilities
|
||||
} catch (e: any) {
|
||||
//console.info(newDockApt);
|
||||
log.error(
|
||||
{ stack: e.response.data },
|
||||
`An error has occurred during patching of the release: ${release.ReleaseNumber}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (
|
||||
(releaseLoadtypeCheck ||
|
||||
opendDockArticleCheck?.loadType === "drop" ||
|
||||
defaultDock?.value === "drop") &&
|
||||
releaseOkToSchedule
|
||||
) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${process.env.OPENDOCK_URL}/appointment`,
|
||||
@@ -287,17 +495,87 @@ const postRelease = async (release: Releases) => {
|
||||
|
||||
log.info({}, `${release.ReleaseNumber} was created`);
|
||||
} catch (e) {
|
||||
log.error({ error: e }, "Error creating new release");
|
||||
log.error({ stack: e }, "Error creating new release");
|
||||
}
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to many possibilities
|
||||
} catch (e: any) {
|
||||
log.error(
|
||||
{ error: e?.response?.data },
|
||||
"Error posting new release to opendock",
|
||||
{ stack: e?.response?.data },
|
||||
`Error posting new release to opendock, ${release.ReleaseNumber}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// try {
|
||||
// const response = await axios.post(
|
||||
// `${process.env.OPENDOCK_URL}/appointment`,
|
||||
// newDockApt,
|
||||
// {
|
||||
// headers: {
|
||||
// "content-type": "application/json; charset=utf-8",
|
||||
// Authorization: `Bearer ${odToken.odToken}`,
|
||||
// },
|
||||
// },
|
||||
// );
|
||||
|
||||
// // we need the id,release#,status from this response, store it in lst, check if we have a release so we can just update it.
|
||||
// // this will be utilized when we are listening for the changes to the apts. that way we can update the state to arrived. we will run our own checks on this guy during the incoming messages.
|
||||
|
||||
// if (response.status === 400) {
|
||||
// log.error({}, response.data.data.message);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // the response to make it simple we want response.data.id, response.data.relNumber, status will be defaulted to Scheduled if we created it here.
|
||||
// // TODO: add this release data to our db. but save it in json format and well parse it out. that way we future proof it and have everything in here vs just a few things
|
||||
// //console.info(response.data.data, "Was Created");
|
||||
// try {
|
||||
// await db
|
||||
// .insert(opendockApt)
|
||||
// .values({
|
||||
// release: release.ReleaseNumber,
|
||||
// openDockAptId: response.data.data.id,
|
||||
// appointment: response.data.data,
|
||||
// })
|
||||
// .onConflictDoUpdate({
|
||||
// target: opendockApt.release,
|
||||
// set: {
|
||||
// openDockAptId: response.data.data.id,
|
||||
// appointment: response.data.data,
|
||||
// upd_date: sql`NOW()`,
|
||||
// },
|
||||
// })
|
||||
// .returning();
|
||||
|
||||
// log.info({}, `${release.ReleaseNumber} was created`);
|
||||
// } catch (e) {
|
||||
// log.error({ stack: e }, "Error creating new release");
|
||||
// }
|
||||
// // biome-ignore lint/suspicious/noExplicitAny: to many possibilities
|
||||
// } catch (e: any) {
|
||||
// log.error(
|
||||
// { stack: e?.response?.data },
|
||||
// `Error posting new release to opendock, ${release.ReleaseNumber}`,
|
||||
// );
|
||||
|
||||
// return;
|
||||
// }
|
||||
|
||||
log.info(
|
||||
{
|
||||
stack: {
|
||||
release: release.ReleaseNumber,
|
||||
releaseLoadtypeCheck,
|
||||
articleLoadType: opendDockArticleCheck?.loadType,
|
||||
defaultLoadType: defaultDock?.value,
|
||||
releaseOkToSchedule,
|
||||
},
|
||||
},
|
||||
`Skipping OpenDock post - release: ${release.ReleaseNumber} is not allowed to schedule`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await delay(750); // rate limit protection
|
||||
|
||||
277
backend/opendock/opendock.articleCheck.route.ts
Normal file
@@ -0,0 +1,277 @@
|
||||
import { desc, eq, sql } from "drizzle-orm";
|
||||
import { Router } from "express";
|
||||
import z from "zod";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import {
|
||||
type NewOpendockArticleSetup,
|
||||
opendockArticleSetup,
|
||||
} from "../db/schema/opendock_articleSetup.js";
|
||||
import { opendockDockSetup } from "../db/schema/opendock_docks.js";
|
||||
import { prodQuery } from "../prodSql/prodSqlQuery.controller.js";
|
||||
import {
|
||||
type SqlQuery,
|
||||
sqlQuerySelector,
|
||||
} from "../prodSql/prodSqlQuerySelector.utils.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||
|
||||
const r = Router();
|
||||
|
||||
const newArticleLink = z.object({
|
||||
av: z.number().int(),
|
||||
description: z.string(),
|
||||
customer: z.string().min(1).max(32),
|
||||
customerDescription: z.string().min(2).max(100),
|
||||
loadType: z
|
||||
.enum(["drop", "live"])
|
||||
.optional()
|
||||
.describe("What roles are available to use."),
|
||||
dock: z
|
||||
//.record(z.string(), z.unknown())
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"This allows us to add extra fields to the data to parse against",
|
||||
),
|
||||
});
|
||||
|
||||
const newDockLink = z.object({
|
||||
name: z.string(),
|
||||
dockID: z.string(),
|
||||
});
|
||||
|
||||
r.post("/", async (req, res) => {
|
||||
try {
|
||||
const validated = newArticleLink.parse(req.body) as NewOpendockArticleSetup;
|
||||
|
||||
const newLink = await db
|
||||
.insert(opendockArticleSetup)
|
||||
.values({
|
||||
av: validated.av,
|
||||
description: validated.description,
|
||||
customer: validated.customer,
|
||||
customerDescription: validated.customerDescription,
|
||||
loadType: validated.loadType,
|
||||
dock: validated.dock,
|
||||
add_user: req.user?.username ?? "lst_user",
|
||||
})
|
||||
.returning();
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: `${validated.av} was just added `,
|
||||
data: newLink as any,
|
||||
status: 200,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof z.ZodError) {
|
||||
const flattened = z.flattenError(err);
|
||||
// return res.status(400).json({
|
||||
// error: "Validation failed",
|
||||
// details: flattened,
|
||||
// });
|
||||
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error", //connect.success ? "info" : "error",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: "Validation failed",
|
||||
data: [flattened.fieldErrors],
|
||||
status: 400, //connect.success ? 200 : 400,
|
||||
});
|
||||
}
|
||||
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error", //connect.success ? "info" : "error",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: "Internal Server Error adding article link",
|
||||
data: [err],
|
||||
status: 400, //connect.success ? 200 : 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
r.patch("/:id", async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const updates: Record<string, unknown | null> = {};
|
||||
|
||||
if (req.body?.loadType !== undefined) {
|
||||
updates.loadType = req.body.loadType;
|
||||
}
|
||||
|
||||
if (req.body?.dock !== undefined) {
|
||||
updates.dock = req.body.dock;
|
||||
}
|
||||
|
||||
updates.upd_user = req.user?.username || "lst_user";
|
||||
updates.upd_date = sql`NOW()`;
|
||||
|
||||
const updatedSetting = await db
|
||||
.update(opendockArticleSetup)
|
||||
.set(updates)
|
||||
.where(eq(opendockArticleSetup.id, id))
|
||||
.returning();
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: `${updatedSetting[0]?.av} was just updated. `,
|
||||
data: updatedSetting,
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
r.delete("/:id", async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
const removeLink = await db
|
||||
.delete(opendockArticleSetup)
|
||||
.where(eq(opendockArticleSetup.id, id))
|
||||
.returning();
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "info", //connect.success ? "info" : "error",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: "Article link was deleted",
|
||||
data: removeLink,
|
||||
status: 200, //connect.success ? 200 : 400,
|
||||
});
|
||||
});
|
||||
|
||||
r.get("/", async (_, res) => {
|
||||
const { data } = await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(opendockArticleSetup)
|
||||
.orderBy(desc(opendockArticleSetup.customer))
|
||||
.limit(1500),
|
||||
);
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: `All links`,
|
||||
data: data ?? [],
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
r.get("/customers/:av", async (req, res) => {
|
||||
const { av } = req.params;
|
||||
|
||||
const avSQLQuery = sqlQuerySelector(`opendock.addressLink`) as SqlQuery;
|
||||
|
||||
if (!avSQLQuery.success) {
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "error",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: avSQLQuery.message,
|
||||
data: [],
|
||||
status: 200,
|
||||
});
|
||||
}
|
||||
const { data } = await tryCatch(
|
||||
prodQuery(
|
||||
avSQLQuery.query.replace("[articleCheck]", av),
|
||||
"openDock addressLink",
|
||||
),
|
||||
);
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: `All customers linked to av: ${av}`,
|
||||
data: data?.data ?? ([] as any),
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
r.post("/dock", async (req, res) => {
|
||||
try {
|
||||
const validated = newDockLink.parse(req.body) as any;
|
||||
|
||||
const newLink = await db
|
||||
.insert(opendockDockSetup)
|
||||
.values({
|
||||
name: validated.name,
|
||||
dockID: validated.dockID,
|
||||
add_user: req.user?.username ?? "lst_user",
|
||||
})
|
||||
.returning();
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: `${validated.name} was just added `,
|
||||
data: newLink as any,
|
||||
status: 200,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof z.ZodError) {
|
||||
const flattened = z.flattenError(err);
|
||||
// return res.status(400).json({
|
||||
// error: "Validation failed",
|
||||
// details: flattened,
|
||||
// });
|
||||
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error", //connect.success ? "info" : "error",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: "Validation failed",
|
||||
data: [flattened.fieldErrors],
|
||||
status: 400, //connect.success ? 200 : 400,
|
||||
});
|
||||
}
|
||||
|
||||
return apiReturn(res, {
|
||||
success: false,
|
||||
level: "error", //connect.success ? "info" : "error",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: "Internal Server Error adding dock link",
|
||||
data: [err],
|
||||
status: 400, //connect.success ? 200 : 400,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
r.get("/dock", async (_, res) => {
|
||||
const { data } = await tryCatch(
|
||||
db
|
||||
.select()
|
||||
.from(opendockDockSetup)
|
||||
.orderBy(desc(opendockDockSetup.name))
|
||||
.limit(1500),
|
||||
);
|
||||
|
||||
return apiReturn(res, {
|
||||
success: true,
|
||||
level: "info",
|
||||
module: "opendock",
|
||||
subModule: "articleCheck",
|
||||
message: `All dock links`,
|
||||
data: data ?? [],
|
||||
status: 200,
|
||||
});
|
||||
});
|
||||
|
||||
export default r;
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Express } from "express";
|
||||
import { requireAuth } from "../middleware/auth.middleware.js";
|
||||
import { featureCheck } from "../middleware/featureActive.middleware.js";
|
||||
import articleCheck from "./opendock.articleCheck.route.js";
|
||||
|
||||
import getApt from "./opendockGetRelease.route.js";
|
||||
|
||||
@@ -13,4 +14,11 @@ export const setupOpendockRoutes = (baseUrl: string, app: Express) => {
|
||||
requireAuth,
|
||||
getApt,
|
||||
);
|
||||
|
||||
app.use(
|
||||
`${baseUrl}/api/opendock/articleCheck`,
|
||||
featureCheck("opendock_sync"),
|
||||
requireAuth,
|
||||
articleCheck,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { desc, gte, sql } from "drizzle-orm";
|
||||
import { Router } from "express";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { opendockApt } from "../db/schema/opendock.schema.js";
|
||||
import { opendockApt } from "../db/schema/opendock_apt.schema.js";
|
||||
import { apiReturn } from "../utils/returnHelper.utils.js";
|
||||
import { tryCatch } from "../utils/trycatch.utils.js";
|
||||
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
/*
|
||||
disables sql jobs.
|
||||
*/
|
||||
EXEC msdb.dbo.sp_update_job @job_name = N'[jobName]', @enabled = 0;
|
||||
--EXEC msdb.dbo.sp_update_job @job_name = N'[jobName]', @enabled = 0;
|
||||
-- DECLARE @JobName varchar(max) = '[jobName]'
|
||||
-- UPDATE msdb.dbo.sysjobs
|
||||
-- SET enabled = 0
|
||||
-- WHERE name = @JobName;
|
||||
|
||||
DECLARE @JobName NVARCHAR(128) = N'[jobName]';
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM msdb.dbo.sysjobs
|
||||
WHERE name = @JobName
|
||||
)
|
||||
BEGIN
|
||||
EXEC msdb.dbo.sp_update_job
|
||||
@job_name = @JobName,
|
||||
@enabled = 0;
|
||||
END
|
||||
@@ -1,11 +1,80 @@
|
||||
SELECT count(*) as activated
|
||||
FROM [test1_AlplaPROD2.0_Read].[support].[FeatureActivation]
|
||||
|
||||
where feature in (108,7)
|
||||
where feature in (7)
|
||||
|
||||
|
||||
/*
|
||||
as more features get activated and need to have this checked to include the new endpoints add here so we can check this.
|
||||
108 = waste
|
||||
7 = warehousing
|
||||
[DefaultTranslation("Blocking")]
|
||||
Blocking = 1,
|
||||
|
||||
[DefaultTranslation("Users")]
|
||||
UserManagement = 2,
|
||||
|
||||
[DefaultTranslation("Complaint Handling")]
|
||||
ComplaintHandling = 3,
|
||||
|
||||
[DefaultTranslation("Demand Management")]
|
||||
DemandManagement = 4,
|
||||
|
||||
[DefaultTranslation("Issue Material")]
|
||||
IssueMaterial = 5,
|
||||
|
||||
[DefaultTranslation("Production Controlling")]
|
||||
ProductionControlling = 6,
|
||||
|
||||
[DefaultTranslation("Warehousing")]
|
||||
Warehousing = 7,
|
||||
|
||||
[DefaultTranslation("Outbound Deliveries")]
|
||||
OutboundDeliveries = 8,
|
||||
|
||||
[DefaultTranslation("Production Scheduling")]
|
||||
ProductionScheduling = 9,
|
||||
|
||||
[DefaultTranslation("Advanced Scheduling")]
|
||||
AdvancedScheduling = 10,
|
||||
|
||||
[DefaultTranslation("Material Requirements Planning")]
|
||||
MaterialRequirementsPlanning = 11,
|
||||
|
||||
[DefaultTranslation("Production Labelling")]
|
||||
ProductionLabelling = 12,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Accounting")]
|
||||
Accounting = 100,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Irradiation")]
|
||||
Irradiation = 101,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Central Moulds")]
|
||||
CentralMoulds = 102,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Maintenance")]
|
||||
Maintenance = 103,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Disable Manual Bookings")]
|
||||
DisableManualBookings = 104,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Purchasing")]
|
||||
Purchasing = 105,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("Tracing")]
|
||||
Tracing = 106,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("AlplaERP (D365)")]
|
||||
AlplaErp = 107,
|
||||
|
||||
[SpecialProcess]
|
||||
[DefaultTranslation("AI chatbot")]
|
||||
AiChatBot = 108
|
||||
*/
|
||||
|
||||
34
backend/prodSql/queries/opendock.addressLink.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
This will return all address with a sales price.
|
||||
*/
|
||||
WITH ranked AS (
|
||||
SELECT
|
||||
av.id,
|
||||
av.humanReadableId as av,
|
||||
av.Alias as description,
|
||||
-- CONCAT(ad.HumanReadableId, ' - ',ad.Name) as customer ,
|
||||
ad.HumanReadableId as customer,
|
||||
ad.Name as customerDescription,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY AddressId, sp.articleId
|
||||
ORDER BY ValidAfter DESC
|
||||
) AS rn
|
||||
FROM [test1_AlplaPROD2.0_Read].[masterData].[SalesPrice] as sp (nolock)
|
||||
|
||||
/* av */
|
||||
left join
|
||||
[test1_AlplaPROD2.0_Read].[masterData].[Article] as av (nolock) on
|
||||
av.id = sp.articleId
|
||||
|
||||
/* address */
|
||||
left join
|
||||
[test1_AlplaPROD2.0_Read].[masterData].[Address] as ad (nolock) on
|
||||
ad.id = AddressId
|
||||
|
||||
)
|
||||
SELECT *
|
||||
FROM ranked
|
||||
WHERE rn = 1
|
||||
and ranked.av = '[articleCheck]'
|
||||
|
||||
order by customerDescription
|
||||
6
backend/prodSql/queries/outbound.docks.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
USE [test1_AlplaPROD2.0_Read]
|
||||
|
||||
SELECT *
|
||||
FROM [masterData].[Dock] (nolock)
|
||||
where active = 1
|
||||
order by Description desc
|
||||
@@ -21,7 +21,7 @@ SELECT
|
||||
,[MainMaterialId]
|
||||
,[MainMaterialHumanReadableId]
|
||||
,[MainMaterialDescription]
|
||||
,[AdditionalInformation1]
|
||||
,[AdditionalInformation1] -- we will use this to reference as the first check
|
||||
,[AdditionalInformation2]
|
||||
,[D365SupplierLot]
|
||||
,[TradeUnits]
|
||||
@@ -49,7 +49,7 @@ SELECT
|
||||
,[PaymentTermsDescription]
|
||||
,[Remark]
|
||||
,[DeliveryAddressId]
|
||||
,[DeliveryAddressHumanReadableId]
|
||||
,[DeliveryAddressHumanReadableId] --use this to validate with the new drop or live check
|
||||
,[DeliveryAddressDescription]
|
||||
,[DeliveryStreetName]
|
||||
,[DeliveryAddressZip]
|
||||
|
||||
@@ -4,6 +4,7 @@ import { setupAuthRoutes } from "./auth/auth.routes.js";
|
||||
// import the routes and route setups
|
||||
import { setupApiDocsRoutes } from "./configs/scaler.config.js";
|
||||
import { setupDatamartRoutes } from "./datamart/datamart.routes.js";
|
||||
import { setupDockDoorRoutes } from "./dockdoorScanning/dockdoor.routes.js";
|
||||
import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js";
|
||||
import { setupMobileRoutes } from "./mobile/mobile.routes.js";
|
||||
import { setupNotificationRoutes } from "./notification/notification.routes.js";
|
||||
@@ -29,4 +30,5 @@ export const setupRoutes = (baseUrl: string, app: Express) => {
|
||||
setupNotificationRoutes(baseUrl, app);
|
||||
setupOCPRoutes(baseUrl, app);
|
||||
setupTCPRoutes(baseUrl, app);
|
||||
setupDockDoorRoutes(baseUrl, app);
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from "./utils/analyticRouteHits.utils.js";
|
||||
import { createCronJob } from "./utils/croner.utils.js";
|
||||
import { sendEmail } from "./utils/sendEmail.utils.js";
|
||||
import { ppooMonitoring } from "./warehousing/warehousing.ppooMonitor.js";
|
||||
|
||||
const port = Number(process.env.PORT) || 3000;
|
||||
export let systemSettings: Setting[] = [];
|
||||
@@ -78,6 +79,10 @@ const start = async () => {
|
||||
runRouteHitAnalyticsCron(),
|
||||
);
|
||||
|
||||
createCronJob("ppooMonitor", "*/45 * * * * *", async () =>
|
||||
ppooMonitoring(),
|
||||
);
|
||||
|
||||
createCronJob("cleanHitsUp", "0 0 7 * * *", () => cleanupOldRouteHits());
|
||||
// one shots only needed to run on server startups
|
||||
createNotifications();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RoomId } from "./types.socket.js";
|
||||
import type { RoomId } from "./roomDefinitions.socket.js";
|
||||
|
||||
export const MAX_HISTORY = 50;
|
||||
export const FLUSH_INTERVAL = 100; // 50ms change higher if needed
|
||||
|
||||
@@ -1,17 +1,57 @@
|
||||
import { desc } from "drizzle-orm";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { logs } from "../db/schema/logs.schema.js";
|
||||
import type { RoomId } from "./types.socket.js";
|
||||
import { ppoRun } from "../warehousing/warehousing.ppooMonitor.js";
|
||||
|
||||
type RoomDefinition<T = unknown> = {
|
||||
seed: (limit: number) => Promise<T[]>;
|
||||
};
|
||||
|
||||
export const protectedRooms: any = {
|
||||
export type StaticRoomId =
|
||||
| "logs"
|
||||
| "labels"
|
||||
| "admin"
|
||||
| "admin:build"
|
||||
| "ppoo"
|
||||
| "dockDoorLoading:2";
|
||||
export type DynamicRoomId = `dockDoorLoading:${string}`;
|
||||
export type RoomId = StaticRoomId | DynamicRoomId;
|
||||
|
||||
export type RoomConfig = {
|
||||
requiresAuth?: boolean;
|
||||
role?: string[];
|
||||
seed?: (limit: number, roomId: RoomId) => Promise<unknown[]>;
|
||||
};
|
||||
|
||||
export const protectedRooms: Record<StaticRoomId, RoomConfig> = {
|
||||
logs: { requiresAuth: true, role: ["admin", "systemAdmin"] },
|
||||
//admin: { requiresAuth: false, role: ["admin", "systemAdmin"] },
|
||||
labels: {},
|
||||
admin: {},
|
||||
"admin:build": {},
|
||||
ppoo: {},
|
||||
"dockDoorLoading:2": {},
|
||||
};
|
||||
|
||||
export function getRoomConfig(roomId: string): RoomConfig | null {
|
||||
if (roomId in protectedRooms) {
|
||||
return protectedRooms[roomId as StaticRoomId];
|
||||
}
|
||||
|
||||
if (roomId.startsWith("dockDoorLoading:")) {
|
||||
const dockId = roomId.split(":")[1];
|
||||
|
||||
if (!dockId) return null;
|
||||
|
||||
return {
|
||||
requiresAuth: true,
|
||||
role: ["admin", "systemAdmin", "dockDoor"],
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export const roomDefinition: Record<RoomId, RoomDefinition> = {
|
||||
logs: {
|
||||
seed: async (limit) => {
|
||||
@@ -48,4 +88,22 @@ export const roomDefinition: Record<RoomId, RoomDefinition> = {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
ppoo: {
|
||||
seed: async (limit) => {
|
||||
console.log(limit);
|
||||
return {
|
||||
type: "snapshot",
|
||||
items: await ppoRun(),
|
||||
createdAt: new Date().toISOString(),
|
||||
} as any;
|
||||
},
|
||||
},
|
||||
|
||||
// TODO: add in dynamic room seeding
|
||||
"dockDoorLoading:2": {
|
||||
seed: async (limit) => {
|
||||
console.log(limit);
|
||||
return [];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// the emitter setup
|
||||
|
||||
import type { RoomId } from "./types.socket.js";
|
||||
import type { RoomId } from "./roomDefinitions.socket.js";
|
||||
|
||||
let addDataToRoom: ((roomId: RoomId, payload: unknown[]) => void) | null = null;
|
||||
|
||||
|
||||
@@ -7,18 +7,33 @@ import {
|
||||
roomFlushTimers,
|
||||
roomHistory,
|
||||
} from "./roomCache.socket.js";
|
||||
import { roomDefinition } from "./roomDefinitions.socket.js";
|
||||
import type { RoomId } from "./types.socket.js";
|
||||
import { type RoomId, roomDefinition } from "./roomDefinitions.socket.js";
|
||||
|
||||
// get the db data if not exiting already
|
||||
const log = createLogger({ module: "socket.io", subModule: "roomService" });
|
||||
let ioRef: Server | null = null;
|
||||
|
||||
export const registerRoomService = (io: Server) => {
|
||||
ioRef = io;
|
||||
};
|
||||
|
||||
export const hasRoomMembers = (roomId: string): boolean => {
|
||||
if (!ioRef) return false;
|
||||
|
||||
return (ioRef.sockets.adapter.rooms.get(roomId)?.size ?? 0) > 0;
|
||||
};
|
||||
|
||||
export const getRoomMemberCount = (roomId: string): number => {
|
||||
if (!ioRef) return 0;
|
||||
|
||||
return ioRef.sockets.adapter.rooms.get(roomId)?.size ?? 0;
|
||||
};
|
||||
export const preseedRoom = async (roomId: RoomId) => {
|
||||
if (roomHistory.has(roomId)) {
|
||||
return roomHistory.get(roomId);
|
||||
}
|
||||
|
||||
const roomDef = roomDefinition[roomId];
|
||||
const roomDef = roomDefinition[roomId] as any;
|
||||
|
||||
if (!roomDef) {
|
||||
log.error({}, `Room ${roomId} is not defined`);
|
||||
@@ -32,7 +47,7 @@ export const preseedRoom = async (roomId: RoomId) => {
|
||||
};
|
||||
|
||||
export const createRoomEmitter = (io: Server) => {
|
||||
const addDataToRoom = <T>(roomId: RoomId, payload: T) => {
|
||||
const addDataToRoom = <T>(roomId: RoomId, payload: T[]) => {
|
||||
if (!roomHistory.has(roomId)) {
|
||||
roomHistory.set(roomId, []);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,11 @@ import { Server } from "socket.io";
|
||||
import { createLogger } from "../logger/logger.controller.js";
|
||||
import { allowedOrigins } from "../utils/cors.utils.js";
|
||||
import { registerEmitter } from "./roomEmitter.socket.js";
|
||||
import { createRoomEmitter, preseedRoom } from "./roomService.socket.js";
|
||||
import {
|
||||
createRoomEmitter,
|
||||
preseedRoom,
|
||||
registerRoomService,
|
||||
} from "./roomService.socket.js";
|
||||
|
||||
//const __filename = fileURLToPath(import.meta.url);
|
||||
//const __dirname = dirname(__filename);
|
||||
@@ -15,7 +19,7 @@ const log = createLogger({ module: "socket.io", subModule: "setup" });
|
||||
|
||||
import { auth } from "../utils/auth.utils.js";
|
||||
//import type { Session, User } from "better-auth"; // adjust if needed
|
||||
import { protectedRooms } from "./roomDefinitions.socket.js";
|
||||
import { getRoomConfig } from "./roomDefinitions.socket.js";
|
||||
|
||||
// declare module "socket.io" {
|
||||
// interface Socket {
|
||||
@@ -33,6 +37,9 @@ export const setupSocketIORoutes = (baseUrl: string, server: HttpServer) => {
|
||||
},
|
||||
});
|
||||
|
||||
// manage members of the rooms.
|
||||
registerRoomService(io);
|
||||
|
||||
// ✅ Create emitter instance
|
||||
const { addDataToRoom } = createRoomEmitter(io);
|
||||
registerEmitter(addDataToRoom);
|
||||
@@ -78,38 +85,76 @@ export const setupSocketIORoutes = (baseUrl: string, server: HttpServer) => {
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
s.on("join-room", async (rn) => {
|
||||
const config = protectedRooms[rn];
|
||||
// s.on("join-room", async (rn) => {
|
||||
// const config = protectedRooms[rn];
|
||||
|
||||
if (config?.requiresAuth && !s.user) {
|
||||
// if (config?.requiresAuth && !s.user) {
|
||||
// return s.emit("room-error", {
|
||||
// room: rn,
|
||||
// message: "Authentication required",
|
||||
// });
|
||||
// }
|
||||
|
||||
// const roles = Array.isArray(config?.role) ? config?.role : [config?.role];
|
||||
|
||||
// //if (config?.role && s.user?.role !== config.role) {
|
||||
// if (config?.role && !roles.includes(s.user?.role)) {
|
||||
// return s.emit("room-error", {
|
||||
// roomId: rn,
|
||||
// message: `Not authorized to be in room: ${rn}`,
|
||||
// });
|
||||
// }
|
||||
// s.join(rn);
|
||||
|
||||
// // get room seeded
|
||||
// const history = await preseedRoom(rn);
|
||||
// log.info({}, `User joined ${rn}: ${s.id}`);
|
||||
// // send the intial data
|
||||
// s.emit("room-update", {
|
||||
// roomId: rn,
|
||||
// payloads: history,
|
||||
// initial: true,
|
||||
// });
|
||||
// });
|
||||
|
||||
s.on("join-room", async (rn: string) => {
|
||||
const config = getRoomConfig(rn);
|
||||
|
||||
if (!config) {
|
||||
return s.emit("room-error", {
|
||||
room: rn,
|
||||
roomId: rn,
|
||||
message: `Unknown room: ${rn}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (config.requiresAuth && !s.user) {
|
||||
return s.emit("room-error", {
|
||||
roomId: rn,
|
||||
message: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
const roles = Array.isArray(config?.role) ? config?.role : [config?.role];
|
||||
const roles = Array.isArray(config.role) ? config.role : [];
|
||||
|
||||
//if (config?.role && s.user?.role !== config.role) {
|
||||
if (config?.role && !roles.includes(s.user?.role)) {
|
||||
if (roles.length > 0 && !roles.includes(s.user?.role)) {
|
||||
return s.emit("room-error", {
|
||||
roomId: rn,
|
||||
message: `Not authorized to be in room: ${rn}`,
|
||||
});
|
||||
}
|
||||
|
||||
s.join(rn);
|
||||
|
||||
// get room seeded
|
||||
const history = await preseedRoom(rn);
|
||||
const history = await preseedRoom(rn as any);
|
||||
|
||||
log.info({}, `User joined ${rn}: ${s.id}`);
|
||||
// send the intial data
|
||||
|
||||
s.emit("room-update", {
|
||||
roomId: rn,
|
||||
payloads: history,
|
||||
initial: true,
|
||||
});
|
||||
});
|
||||
|
||||
s.on("leave-room", (room) => {
|
||||
s.leave(room);
|
||||
log.info({}, `${s.id} left room: ${room}`);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type RoomId = "logs" | "labels" | "admin" | "admin:build"; //| "alerts" | "metrics";
|
||||
@@ -162,6 +162,17 @@ const servers: NewServerData[] = [
|
||||
serverLoc: "D$\\LST_V3",
|
||||
buildNumber: 1,
|
||||
},
|
||||
{
|
||||
name: "Salt Lake City",
|
||||
server: "USSLC1VMS006",
|
||||
plantToken: "usslc1",
|
||||
idAddress: "10.202.0.26",
|
||||
greatPlainsPlantCode: "70",
|
||||
contactEmail: "",
|
||||
contactPhone: "",
|
||||
serverLoc: "D$\\LST_V3",
|
||||
buildNumber: 1,
|
||||
},
|
||||
];
|
||||
|
||||
// notes here for when it comes time to pull in all the server address info [test1_AlplaPROD2.0_Read].[masterData].[Plant] has everything from every server :D
|
||||
|
||||
@@ -86,8 +86,40 @@ const newSettings: NewSetting[] = [
|
||||
roles: ["admin"],
|
||||
seedVersion: 1,
|
||||
},
|
||||
{
|
||||
name: "dockDoorScanning",
|
||||
value: "0",
|
||||
active: false,
|
||||
description: "dock door scanning",
|
||||
moduleName: "dockDoorScanning",
|
||||
settingType: "feature",
|
||||
roles: ["admin"],
|
||||
seedVersion: 1,
|
||||
},
|
||||
|
||||
// standard settings
|
||||
{
|
||||
name: "stagingWarehouse",
|
||||
value: "30218",
|
||||
active: true,
|
||||
description:
|
||||
"The warehouse we will use for staging, validation that we did our prechecks if required",
|
||||
moduleName: "dockDoorScanning",
|
||||
settingType: "standard",
|
||||
roles: ["admin"],
|
||||
seedVersion: 1,
|
||||
},
|
||||
{
|
||||
name: "precheck",
|
||||
value: "5",
|
||||
active: false,
|
||||
description:
|
||||
"Precheck is required, the value is in minute, 5 min should be 5",
|
||||
moduleName: "dockDoorScanning",
|
||||
settingType: "standard",
|
||||
roles: ["admin"],
|
||||
seedVersion: 1,
|
||||
},
|
||||
{
|
||||
name: "prolinkCheck",
|
||||
value: "1",
|
||||
@@ -357,6 +389,17 @@ const newSettings: NewSetting[] = [
|
||||
roles: ["admin"],
|
||||
seedVersion: 1,
|
||||
},
|
||||
{
|
||||
name: "defaultLoadType",
|
||||
value: "drop",
|
||||
active: false,
|
||||
description:
|
||||
"What is the default load type we will use for creating new apt: drop or live are the current options.",
|
||||
moduleName: "opendock",
|
||||
settingType: "standard",
|
||||
roles: ["admin"],
|
||||
seedVersion: 1,
|
||||
},
|
||||
];
|
||||
|
||||
export const baseSettingValidationCheck = async () => {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import net from "node:net";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||
import { printerData } from "../db/schema/printers.schema.js";
|
||||
import loadUnit from "../dockdoorScanning/dockdoor.loadUnits.js";
|
||||
import { createLogger } from "../logger/logger.controller.js";
|
||||
import { delay } from "../utils/delay.utils.js";
|
||||
import { returnFunc } from "../utils/returnHelper.utils.js";
|
||||
@@ -14,6 +16,7 @@ export let isServerRunning = false;
|
||||
|
||||
const port = parseInt(process.env.TCP_PORT ?? "2222", 10);
|
||||
|
||||
// This is the parser for zebra scanners
|
||||
const parseTcpAlert = (input: string) => {
|
||||
// guard
|
||||
const colonIndex = input.indexOf(":");
|
||||
@@ -74,6 +77,23 @@ export const startTCPServer = async () => {
|
||||
|
||||
printerListen(printerData as PrinterData);
|
||||
}
|
||||
|
||||
// check if its a dock door scanner
|
||||
const dockdoorScanners = await db.select().from(dockDoorScanners);
|
||||
|
||||
if (dockdoorScanners.some((s) => s.ip === ip.replace("::ffff:", ""))) {
|
||||
console.log("dock door logic");
|
||||
|
||||
const currentDock = dockdoorScanners.filter(
|
||||
(s) => s.ip === ip.replace("::ffff:", ""),
|
||||
);
|
||||
|
||||
// send the data + dock scan over
|
||||
loadUnit({
|
||||
dockId: currentDock[0]?.dockId ?? "0",
|
||||
sscc: data.toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("end", () => {
|
||||
|
||||
@@ -3,11 +3,12 @@ import { adminAc, defaultStatements } from "better-auth/plugins/admin/access";
|
||||
|
||||
export const statement = {
|
||||
...defaultStatements,
|
||||
app: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
quality: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
logistics: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
mobile: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
notifications: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
app: ["read", "create", "update", "delete", "readAll"],
|
||||
quality: ["read", "create", "update", "delete", "readAll"],
|
||||
logistics: ["read", "create", "update", "delete", "readAll"],
|
||||
mobile: ["read", "create", "update", "delete", "readAll"],
|
||||
openDock: ["read", "create", "update", "delete"],
|
||||
notifications: ["read", "create", "update", "delete", "readAll"],
|
||||
} as const;
|
||||
|
||||
export const ac = createAccessControl(statement);
|
||||
@@ -15,24 +16,50 @@ export const ac = createAccessControl(statement);
|
||||
export const user = ac.newRole({
|
||||
app: ["read", "create"],
|
||||
notifications: ["read", "create"],
|
||||
openDock: ["read"],
|
||||
});
|
||||
|
||||
export const manager = ac.newRole({
|
||||
app: ["read", "create", "update"],
|
||||
mobile: ["read", "create", "update"],
|
||||
openDock: ["read", "create", "update"],
|
||||
});
|
||||
|
||||
export const transport = ac.newRole({
|
||||
app: ["read", "create", "update"],
|
||||
openDock: ["read", "create", "update"],
|
||||
});
|
||||
|
||||
export const admin = ac.newRole({
|
||||
app: ["read", "create", "update"],
|
||||
mobile: ["read", "create", "update"],
|
||||
user: ["create", "update"],
|
||||
user: ["create", "update", "ban"],
|
||||
openDock: ["read", "create", "update"],
|
||||
});
|
||||
|
||||
export const systemAdmin = ac.newRole({
|
||||
...adminAc.statements,
|
||||
app: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
quality: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
mobile: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
logistics: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
notifications: ["read", "create", "share", "update", "delete", "readAll"],
|
||||
app: ["read", "create", "update", "delete", "readAll"],
|
||||
quality: ["read", "create", "update", "delete", "readAll"],
|
||||
mobile: ["read", "create", "update", "delete", "readAll"],
|
||||
logistics: ["read", "create", "update", "delete", "readAll"],
|
||||
notifications: ["read", "create", "update", "delete", "readAll"],
|
||||
openDock: ["read", "create", "update", "delete"],
|
||||
});
|
||||
|
||||
/* example usage
|
||||
const canCreateProject = await authClient.admin.hasPermission({
|
||||
permissions: {
|
||||
project: ["create"],
|
||||
},
|
||||
});
|
||||
// You can also check multiple resource permissions at the same time
|
||||
const canCreateProjectAndCreateSale = await authClient.admin.hasPermission({
|
||||
permissions: {
|
||||
project: ["create"],
|
||||
sale: ["create"]
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,14 @@ import {
|
||||
//import { eq } from "drizzle-orm";
|
||||
import { db } from "../db/db.controller.js";
|
||||
import * as rawSchema from "../db/schema/auth.schema.js";
|
||||
import { ac, admin, manager, systemAdmin, user } from "./auth.permissions.js";
|
||||
import {
|
||||
ac,
|
||||
admin,
|
||||
manager,
|
||||
systemAdmin,
|
||||
transport,
|
||||
user,
|
||||
} from "./auth.permissions.js";
|
||||
import { allowedOrigins } from "./cors.utils.js";
|
||||
import { sendEmail } from "./sendEmail.utils.js";
|
||||
|
||||
@@ -164,6 +171,7 @@ export const auth = betterAuth({
|
||||
admin,
|
||||
user,
|
||||
manager,
|
||||
transport,
|
||||
systemAdmin,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -17,6 +17,7 @@ export const allowedOrigins = [
|
||||
`http://${process.env.PROD_SERVER}:3100`, // temp
|
||||
`http://usmcd1olp082:3000`,
|
||||
`${process.env.EXTERNAL_URL}`, // internal docker
|
||||
"chrome-extension://mddoackclclnbkmofficmmepfnadolfa",
|
||||
];
|
||||
export const lstCors = () => {
|
||||
return cors({
|
||||
|
||||
@@ -16,7 +16,8 @@ export interface ReturnHelper<T = unknown[]> {
|
||||
| "tcp"
|
||||
| "logistics"
|
||||
| "admin"
|
||||
| "mobile";
|
||||
| "mobile"
|
||||
| "dockdoor";
|
||||
subModule: string;
|
||||
|
||||
level: "info" | "error" | "debug" | "fatal" | "warn";
|
||||
|
||||
29
backend/warehousing/warehousing.ppooMonitor.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { emitToRoom } from "../socket.io/roomEmitter.socket.js";
|
||||
import { hasRoomMembers } from "../socket.io/roomService.socket.js";
|
||||
import { runProdApi } from "../utils/prodEndpoint.utils.js";
|
||||
|
||||
export const ppoRun = async () => {
|
||||
const laneData = await runProdApi({
|
||||
method: "post",
|
||||
endpoint: "/public/v1.1/Warehousing/GetWarehouseUnits",
|
||||
data: [
|
||||
{
|
||||
laneIds: ["0"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return laneData?.data ?? [];
|
||||
};
|
||||
|
||||
export const ppooMonitoring = async () => {
|
||||
if (!hasRoomMembers(`ppoo`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitToRoom("ppoo", {
|
||||
type: "snapshot",
|
||||
items: await ppoRun(),
|
||||
createdAt: new Date().toISOString(),
|
||||
} as any);
|
||||
};
|
||||
699
frontend/package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.5.0",
|
||||
"@fontsource-variable/inter": "^5.2.8",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"@tanstack/react-form": "^1.28.5",
|
||||
@@ -34,6 +35,7 @@
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vite-imagetools": "^10.0.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -439,6 +441,15 @@
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
@@ -484,6 +495,66 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@base-ui/react": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.5.0.tgz",
|
||||
"integrity": "sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.29.2",
|
||||
"@base-ui/utils": "0.2.9",
|
||||
"@floating-ui/react-dom": "^2.1.8",
|
||||
"@floating-ui/utils": "^0.2.11",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/mui-org"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@date-fns/tz": "^1.2.0",
|
||||
"@types/react": "^17 || ^18 || ^19",
|
||||
"date-fns": "^4.0.0",
|
||||
"react": "^17 || ^18 || ^19",
|
||||
"react-dom": "^17 || ^18 || ^19"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@date-fns/tz": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"date-fns": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@base-ui/utils": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.9.tgz",
|
||||
"integrity": "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.29.2",
|
||||
"@floating-ui/utils": "^0.2.11",
|
||||
"reselect": "^5.1.1",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^17 || ^18 || ^19",
|
||||
"react": "^17 || ^18 || ^19",
|
||||
"react-dom": "^17 || ^18 || ^19"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@better-auth/utils": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.1.tgz",
|
||||
@@ -649,6 +720,16 @@
|
||||
"node": "^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
|
||||
@@ -1333,6 +1414,519 @@
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
||||
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
||||
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
||||
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
||||
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
||||
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
||||
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
||||
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
||||
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
||||
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
||||
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
||||
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/ansi": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",
|
||||
@@ -3163,6 +3757,28 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
|
||||
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
|
||||
@@ -6672,6 +7288,12 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esutils": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
@@ -7511,6 +8133,15 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/imagetools-core": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-9.1.0.tgz",
|
||||
"integrity": "sha512-xQjs+2vrxLnAjCq+omuNkd5UQTld9/bP8+YT0LyYTlKfuSQtgUBvqhUwGugzSAh6sCdN+LnROMuLswn5hZ9Fhg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
@@ -10460,6 +11091,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz",
|
||||
"integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
@@ -10638,7 +11275,6 @@
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -10804,6 +11440,50 @@
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.5",
|
||||
"@img/sharp-darwin-x64": "0.34.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
||||
"@img/sharp-linux-arm": "0.34.5",
|
||||
"@img/sharp-linux-arm64": "0.34.5",
|
||||
"@img/sharp-linux-ppc64": "0.34.5",
|
||||
"@img/sharp-linux-riscv64": "0.34.5",
|
||||
"@img/sharp-linux-s390x": "0.34.5",
|
||||
"@img/sharp-linux-x64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.5",
|
||||
"@img/sharp-wasm32": "0.34.5",
|
||||
"@img/sharp-win32-arm64": "0.34.5",
|
||||
"@img/sharp-win32-ia32": "0.34.5",
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -11837,6 +12517,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-imagetools": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-imagetools/-/vite-imagetools-10.0.0.tgz",
|
||||
"integrity": "sha512-+83L32YPU/2BOHWhudO2+9T5HBvb3+0qHoUNN7fb0+XcAoXilx7aE25cDPWU5kBi5Yc750zYCvHxgfyR+tAuMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.0.5",
|
||||
"imagetools-core": "^9.1.0",
|
||||
"sharp": "^0.34.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.5.0",
|
||||
"@fontsource-variable/inter": "^5.2.8",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"@tanstack/react-form": "^1.28.5",
|
||||
@@ -21,6 +22,8 @@
|
||||
"better-auth": "^1.5.5",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"lucide-react": "^0.577.0",
|
||||
"next-themes": "^0.4.6",
|
||||
"radix-ui": "^1.4.3",
|
||||
@@ -34,9 +37,8 @@
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"zod": "^4.3.6",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0"
|
||||
"vite-imagetools": "^10.0.0",
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.36.0",
|
||||
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 17 KiB |
BIN
frontend/public/imgs/docs/mobile/usbow1-1.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
frontend/public/imgs/docs/mobile/usbow1-2.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
frontend/public/imgs/docs/mobile/usbow1-3.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6.9 KiB |
BIN
frontend/public/imgs/docs/mobile/usmar1-1.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
frontend/public/imgs/docs/mobile/usmar1-2.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
frontend/public/imgs/docs/mobile/usmar1-3.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
frontend/public/imgs/docs/mobile/usstp1-1.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
frontend/public/imgs/docs/mobile/usstp1-2.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
frontend/public/imgs/docs/mobile/usstp1-3.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 6.9 KiB |
BIN
frontend/public/stage-now/usbow1-stageNow.pdf
Normal file
BIN
frontend/public/stage-now/usmar1-stageNow.pdf
Normal file
BIN
frontend/public/stage-now/usstp1-stageNow.pdf
Normal file
94
frontend/src/components/Sidebar/TransportationBar.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { ChevronRight, Link as link } from "lucide-react";
|
||||
import { permissionQuery } from "../../lib/queries/permsCheck";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "../ui/collapsible";
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
useSidebar,
|
||||
} from "../ui/sidebar";
|
||||
|
||||
export default function TransportationBar() {
|
||||
const { data: canCreate = false } = useQuery(
|
||||
permissionQuery({
|
||||
openDock: ["create"],
|
||||
}),
|
||||
);
|
||||
|
||||
const { setOpen } = useSidebar();
|
||||
const items = [
|
||||
{
|
||||
title: "Open Dock",
|
||||
url: "/transportation",
|
||||
//icon,
|
||||
isActive: canCreate,
|
||||
items: [
|
||||
{
|
||||
title: "ArticleLink",
|
||||
icon: link,
|
||||
url: "/transportation/opendock",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Transportation</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<div key={item.title}>
|
||||
{item.isActive && (
|
||||
<Collapsible
|
||||
asChild
|
||||
//defaultOpen={isNotifications}
|
||||
className="group/collapsible"
|
||||
>
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuButton tooltip={item.title}>
|
||||
{item.title}
|
||||
|
||||
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub>
|
||||
{item.items?.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
<SidebarMenuSubButton asChild>
|
||||
<Link
|
||||
to={subItem.url}
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<subItem.icon />
|
||||
<span>{subItem.title}</span>
|
||||
</Link>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
);
|
||||
}
|
||||