Compare commits
24 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 |
66
.gitea/ISSUE_TEMPLATE/bug_report_frontend.md
Normal file
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
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
|
about: Report something that is broken or not working correctly
|
||||||
title: "[BUG] "
|
title: "[BUG - Server] "
|
||||||
ref: "main"
|
ref: "main"
|
||||||
labels:
|
labels:
|
||||||
|
|
||||||
- bug
|
- bug
|
||||||
|
- server
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
48
.gitea/ISSUE_TEMPLATE/enhancement_frontend.md
Normal file
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
|
about: Improve or refine an existing feature
|
||||||
title: "[ENHANCEMENT] "
|
title: "[ENHANCEMENT - Mobile] "
|
||||||
ref: "main"
|
ref: "main"
|
||||||
labels:
|
labels:
|
||||||
|
|
||||||
- enhancement
|
- enhancement
|
||||||
|
- mobile
|
||||||
---
|
---
|
||||||
|
|
||||||
# Existing Feature
|
# Existing Feature
|
||||||
48
.gitea/ISSUE_TEMPLATE/enhancement_server.md
Normal file
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
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
|
about: Suggest a brand new feature or module
|
||||||
title: "[FEATURE] "
|
title: "[FEATURE - Mobile] "
|
||||||
ref: "main"
|
ref: "main"
|
||||||
labels:
|
labels:
|
||||||
|
|
||||||
- feature
|
- feature
|
||||||
|
- mobile
|
||||||
---
|
---
|
||||||
|
|
||||||
# Problem Statement
|
# Problem Statement
|
||||||
41
.gitea/ISSUE_TEMPLATE/feature_request_server.md
Normal file
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.
|
||||||
19
README.md
19
README.md
@@ -7,7 +7,7 @@
|
|||||||
Quick summary of current rewrite/migration goal.
|
Quick summary of current rewrite/migration goal.
|
||||||
|
|
||||||
- **Phase:** Backend rewrite
|
- **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 Authentication | ~~Login~~, ~~Signup~~, API Key | 🟨 In Progress |
|
||||||
| User Profile | ~~Edit profile~~, upload avatar | 🟨 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 |
|
| Notifications | ~~Subscribe~~, ~~Create~~, ~~Update~~, ~~~~Remove~~, Manual Trigger | 🟨 In Progress |
|
||||||
| Datamart | ~~Create~~, ~~Update~~, ~~Run~~, Deactivate | 🟨 In Progress |
|
| Datamart | ~~Create~~, ~~Update~~, ~~Run~~, Deactivate | 🟨 In Progress |
|
||||||
| Frontend | Analytics and charts | ⏳ Not Started |
|
| Frontend | Analytics and charts | ⏳ Not Started |
|
||||||
| Docs | Instructions and trouble shooting | ⏳ 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 |
|
| Silo Adjustments | Create, History, Comments | ⏳ Not Started |
|
||||||
| Demand Management | Orders, Forecast, Special Mappings, Create trucks, Load Trucks (tablet scanning) | ⏳ 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 |
|
| Transport Insight | Integrations | ⏳ Not Started |
|
||||||
| Quality Request Tool | Add Pallet, Monitor for moved, status changes, alerts | ⏳ 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 |
|
| Logistics | Consume material, return and print, label info, relocate | ⏳ Not Started |
|
||||||
| EOM | Endpoints, Report Pull for finance | ⏳ 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 |
|
| API Migration | Moving to new REST endpoints | 🔧 In Progress |
|
||||||
| System | Tests,Builds, Updates, Remote Logging, DB Backups, Alerting | ⏳ Not Started |
|
| 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
|
git clone https://git.tuffraid.net/cowch/lst_v3.git
|
||||||
cd lst_v3
|
cd lst_v3
|
||||||
npm install
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Rename the .env-example to .env
|
||||||
|
|
||||||
|
Update all the fields
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev:db:migrate
|
||||||
npm run dev
|
npm run dev
|
||||||
|
```
|
||||||
@@ -46,7 +46,8 @@ const createApp = async () => {
|
|||||||
server: ${JSON.stringify(umamiConfig.server ?? "unknown")},
|
server: ${JSON.stringify(umamiConfig.server ?? "unknown")},
|
||||||
appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")},
|
appVersion: ${JSON.stringify(umamiConfig.appVersion ?? "dev")},
|
||||||
umamiHost: ${JSON.stringify(umamiConfig.umamiHost ?? "")},
|
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
0
backend/datamart/datamart.controller.test.ts
Normal file
68
backend/datamart/getDatamart.route.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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -17,15 +17,15 @@ export const alplaPurchaseHistory = pgTable("alpla_purchase_history", {
|
|||||||
status: integer("status"),
|
status: integer("status"),
|
||||||
statusText: text("status_text"),
|
statusText: text("status_text"),
|
||||||
journalNum: integer("journal_num"),
|
journalNum: integer("journal_num"),
|
||||||
add_date: timestamp("add_date").defaultNow(),
|
add_date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
add_user: text("add_user"),
|
add_user: text("add_user"),
|
||||||
upd_user: text("upd_user"),
|
upd_user: text("upd_user"),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
remark: text("remark"),
|
remark: text("remark"),
|
||||||
approvedStatus: text("approved_status").default("new"),
|
approvedStatus: text("approved_status").default("new"),
|
||||||
position: jsonb("position").default([]),
|
position: jsonb("position").default([]),
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
updatedAt: timestamp("updated_at").defaultNow(),
|
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const alplaPurchaseHistorySchema =
|
export const alplaPurchaseHistorySchema =
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { integer, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|||||||
export const analytics = pgTable("analytics", {
|
export const analytics = pgTable("analytics", {
|
||||||
id: uuid("id").defaultRandom().primaryKey(),
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
|
||||||
method: text("method").notNull(),
|
method: text("method").notNull(),
|
||||||
routePattern: text("route_pattern").notNull(),
|
routePattern: text("route_pattern").notNull(),
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ export const jobAuditLog = pgTable(
|
|||||||
id: uuid("id").defaultRandom().primaryKey(),
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
jobName: text("job_name"),
|
jobName: text("job_name"),
|
||||||
startedAt: timestamp("start_at"),
|
startedAt: timestamp("start_at"),
|
||||||
finishedAt: timestamp("finished_at"),
|
finishedAt: timestamp("finished_at", { withTimezone: true }),
|
||||||
durationMs: integer("duration_ms"),
|
durationMs: integer("duration_ms"),
|
||||||
status: text("status"), //success | error
|
status: text("status"), //success | error
|
||||||
errorMessage: text("error_message"),
|
errorMessage: text("error_message"),
|
||||||
errorStack: text("error_stack"),
|
errorStack: text("error_stack"),
|
||||||
metadata: jsonb("meta_data"),
|
metadata: jsonb("meta_data"),
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => {
|
(table) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const user = pgTable("user", {
|
|||||||
emailVerified: boolean("email_verified").default(false).notNull(),
|
emailVerified: boolean("email_verified").default(false).notNull(),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||||
updatedAt: timestamp("updated_at")
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
.defaultNow()
|
.defaultNow()
|
||||||
.$onUpdate(() => /* @__PURE__ */ new Date())
|
.$onUpdate(() => /* @__PURE__ */ new Date())
|
||||||
.notNull(),
|
.notNull(),
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ export const deploymentHistory = pgTable("deployment_history", {
|
|||||||
buildNumber: integer("build_number").notNull(),
|
buildNumber: integer("build_number").notNull(),
|
||||||
status: text("status").notNull(), // started, success, failed
|
status: text("status").notNull(), // started, success, failed
|
||||||
message: text("message"),
|
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(),
|
avgDurationMs: integer("avg_duration_ms").notNull(),
|
||||||
maxDurationMs: integer("max_duration_ms").notNull(),
|
maxDurationMs: integer("max_duration_ms").notNull(),
|
||||||
|
|
||||||
firstHitAt: timestamp("first_hit_at").notNull(),
|
firstHitAt: timestamp("first_hit_at", { withTimezone: true }).notNull(),
|
||||||
lastHitAt: timestamp("last_hit_at").notNull(),
|
lastHitAt: timestamp("last_hit_at", { withTimezone: true }).notNull(),
|
||||||
|
|
||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
||||||
|
.defaultNow()
|
||||||
|
.notNull(),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
unique("analytics_daily_business_route_unique").on(
|
unique("analytics_daily_business_route_unique").on(
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ export const datamart = pgTable("datamart", {
|
|||||||
active: boolean("active").default(true),
|
active: boolean("active").default(true),
|
||||||
options: text("options").default(""),
|
options: text("options").default(""),
|
||||||
public: boolean("public_access").default(false),
|
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"),
|
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"),
|
upd_user: text("upd_user").default("lst-system"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
22
backend/db/schema/dockdoor.schema.ts
Normal file
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(""),
|
whseId: text("whse_id").default(""),
|
||||||
whseName: text("whse_name").default("missing whseName"),
|
whseName: text("whse_name").default("missing whseName"),
|
||||||
upd_user: text("upd_user").default("lst-system"),
|
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);
|
export const invHistoricalDataSchema = createSelectSchema(invHistoricalData);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const logs = pgTable("logs", {
|
|||||||
stack: jsonb("stack").default([]),
|
stack: jsonb("stack").default([]),
|
||||||
checked: boolean("checked").default(false),
|
checked: boolean("checked").default(false),
|
||||||
hostname: text("hostname"),
|
hostname: text("hostname"),
|
||||||
createdAt: timestamp("created_at").defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const logSchema = createSelectSchema(logs);
|
export const logSchema = createSelectSchema(logs);
|
||||||
|
|||||||
@@ -17,8 +17,12 @@ export const opendockApt = pgTable(
|
|||||||
release: integer("release").notNull().unique("opendock_apt_release_unique"),
|
release: integer("release").notNull().unique("opendock_apt_release_unique"),
|
||||||
openDockAptId: text("open_dock_apt_id").notNull(),
|
openDockAptId: text("open_dock_apt_id").notNull(),
|
||||||
appointment: jsonb("appointment").notNull().default([]),
|
appointment: jsonb("appointment").notNull().default([]),
|
||||||
upd_date: timestamp("upd_date").notNull().defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => ({
|
(table) => ({
|
||||||
openDockAptIdIdx: index("opendock_apt_opendock_id_idx").on(
|
openDockAptIdIdx: index("opendock_apt_opendock_id_idx").on(
|
||||||
|
|||||||
@@ -22,9 +22,13 @@ export const opendockArticleSetup = pgTable(
|
|||||||
customerDescription: text("customer_description").notNull(),
|
customerDescription: text("customer_description").notNull(),
|
||||||
loadType: loadTypeEnum("load_type").notNull().default("drop"),
|
loadType: loadTypeEnum("load_type").notNull().default("drop"),
|
||||||
dock: text("dock").notNull(),
|
dock: text("dock").notNull(),
|
||||||
upd_date: timestamp("upd_date").notNull().defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
upd_user: text("upd_user").notNull().default("lst-system"),
|
upd_user: text("upd_user").notNull().default("lst-system"),
|
||||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
add_user: text("add_user").notNull().default("lst-system"),
|
add_user: text("add_user").notNull().default("lst-system"),
|
||||||
},
|
},
|
||||||
(table) => ({
|
(table) => ({
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ export const opendockDockSetup = pgTable("opendock_dock_setup", {
|
|||||||
id: uuid("id").defaultRandom().primaryKey(),
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
dockID: text("dock_id").notNull(),
|
dockID: text("dock_id").notNull(),
|
||||||
upd_date: timestamp("upd_date").notNull().defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
upd_user: text("upd_user").notNull().default("lst-system"),
|
upd_user: text("upd_user").notNull().default("lst-system"),
|
||||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
createdAt: timestamp("created_at", { withTimezone: true })
|
||||||
|
.notNull()
|
||||||
|
.defaultNow(),
|
||||||
add_user: text("add_user").notNull().default("lst-system"),
|
add_user: text("add_user").notNull().default("lst-system"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ export const printerLog = pgTable("printer_log", {
|
|||||||
printerSN: text("printer_sn"),
|
printerSN: text("printer_sn"),
|
||||||
condition: text("condition").notNull(),
|
condition: text("condition").notNull(),
|
||||||
message: text("message"),
|
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),
|
printDelay: integer("printDelay").default(90),
|
||||||
processes: jsonb("processes").default([]),
|
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
|
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(),
|
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
//uniqueIndex("emailUniqueIndex").on(sql`lower(${table.email})`),
|
//uniqueIndex("emailUniqueIndex").on(sql`lower(${table.email})`),
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ export const scanUser = pgTable(
|
|||||||
role: mobileRoleEnum("role").notNull().default("user"),
|
role: mobileRoleEnum("role").notNull().default("user"),
|
||||||
active: boolean("active").default(true),
|
active: boolean("active").default(true),
|
||||||
lastScan: timestamp("last_scan").defaultNow(),
|
lastScan: timestamp("last_scan").defaultNow(),
|
||||||
add_Date: timestamp("add_Date").defaultNow(),
|
add_Date: timestamp("add_Date", { withTimezone: true }).defaultNow(),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => ({
|
(table) => ({
|
||||||
userNotificationUnique: unique("scan_user_unique").on(
|
userNotificationUnique: unique("scan_user_unique").on(
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const scanLog = pgTable("scan_log", {
|
|||||||
status: text("status"),
|
status: text("status"),
|
||||||
scannerVersion: text("scanner_version").default("0"),
|
scannerVersion: text("scanner_version").default("0"),
|
||||||
lines: jsonb("lines").default([]),
|
lines: jsonb("lines").default([]),
|
||||||
add_Date: timestamp("add_date").defaultNow(),
|
add_Date: timestamp("add_date", { withTimezone: true }).defaultNow(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const scanLogSchema = createSelectSchema(scanLog);
|
export const scanLogSchema = createSelectSchema(scanLog);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const serverData = pgTable(
|
|||||||
contactPhone: text("contact_phone"),
|
contactPhone: text("contact_phone"),
|
||||||
active: boolean("active").default(true),
|
active: boolean("active").default(true),
|
||||||
serverLoc: text("server_loc"),
|
serverLoc: text("server_loc"),
|
||||||
lastUpdated: timestamp("last_updated").defaultNow(),
|
lastUpdated: timestamp("last_updated", { withTimezone: true }).defaultNow(),
|
||||||
buildNumber: integer("build_number"),
|
buildNumber: integer("build_number"),
|
||||||
isUpgrading: boolean("is_upgrading").default(false),
|
isUpgrading: boolean("is_upgrading").default(false),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ export const settings = pgTable(
|
|||||||
settingType: settingType(),
|
settingType: settingType(),
|
||||||
seedVersion: integer("seed_version").default(1), // this is intended for if we want to update the settings.
|
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_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_user: text("upd_User").default("LST_System").notNull(),
|
||||||
upd_date: timestamp("upd_date").defaultNow(),
|
upd_date: timestamp("upd_date", { withTimezone: true }).defaultNow(),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import type z from "zod";
|
|||||||
export const appStats = pgTable("app_stats", {
|
export const appStats = pgTable("app_stats", {
|
||||||
id: text("id").primaryKey().default("primary"),
|
id: text("id").primaryKey().default("primary"),
|
||||||
currentBuild: integer("current_build").notNull().default(1),
|
currentBuild: integer("current_build").notNull().default(1),
|
||||||
lastBuildAt: timestamp("last_build_at"),
|
lastBuildAt: timestamp("last_build_at", { withTimezone: true }),
|
||||||
lastDeployAt: timestamp("last_deploy_at"),
|
lastDeployAt: timestamp("last_deploy_at", { withTimezone: true }),
|
||||||
building: boolean("building").notNull().default(false),
|
building: boolean("building").notNull().default(false),
|
||||||
updating: boolean("updating").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({}),
|
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
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
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
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
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
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
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
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(),
|
subModule: obj?.subModule?.toLowerCase(),
|
||||||
hostname: obj?.hostname?.toLowerCase(),
|
hostname: obj?.hostname?.toLowerCase(),
|
||||||
message: obj.msg,
|
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(),
|
.returning(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const historicalInvImport = async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data?.length === 0) {
|
if (data.length === 0) {
|
||||||
const avSQLQuery = sqlQuerySelector(`datamart.activeArticles`) as SqlQuery;
|
const avSQLQuery = sqlQuerySelector(`datamart.activeArticles`) as SqlQuery;
|
||||||
|
|
||||||
if (!avSQLQuery.success) {
|
if (!avSQLQuery.success) {
|
||||||
@@ -139,7 +139,7 @@ const historicalInvImport = async () => {
|
|||||||
subModule: "inv",
|
subModule: "inv",
|
||||||
message: `Error adding historical data to lst db`,
|
message: `Error adding historical data to lst db`,
|
||||||
data: errorImport as any,
|
data: errorImport as any,
|
||||||
notify: true,
|
notify: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ router.post("/", async (req, res) => {
|
|||||||
await db
|
await db
|
||||||
.update(scanUser)
|
.update(scanUser)
|
||||||
.set({ lastScan: sql`NOW()` })
|
.set({ lastScan: sql`NOW()` })
|
||||||
.where(eq(scanUser.name, body.name));
|
.where(eq(scanUser.name, body.user));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ type Releases = {
|
|||||||
DeliveryAddressHumanReadableId: string;
|
DeliveryAddressHumanReadableId: string;
|
||||||
AdditionalInformation1: 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 timeZone = process.env.TIMEZONE as string;
|
||||||
const TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000;
|
const TWENTY_FOUR_HOURS = 24 * 60 * 60 * 1000;
|
||||||
const log = createLogger({ module: "opendock", subModule: "releaseMonitor" });
|
const log = createLogger({ module: "opendock", subModule: "releaseMonitor" });
|
||||||
@@ -64,6 +72,7 @@ let lastCheck = formatInTimeZone(
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
const postRelease = async (release: Releases) => {
|
const postRelease = async (release: Releases) => {
|
||||||
|
log.debug({}, `Release: ${release.ReleaseNumber} is about to be validated`);
|
||||||
if (!odToken.odToken) {
|
if (!odToken.odToken) {
|
||||||
log.info({}, "Getting Auth Token");
|
log.info({}, "Getting Auth Token");
|
||||||
await getToken();
|
await getToken();
|
||||||
@@ -82,21 +91,55 @@ const postRelease = async (release: Releases) => {
|
|||||||
where: (u, { eq }) => eq(u.name, "defaultLoadType"),
|
where: (u, { eq }) => eq(u.name, "defaultLoadType"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// check if the release has the data in it
|
// check if the release has the loadtype in it
|
||||||
const releaseLoadtypeCheck = (release.AdditionalInformation1 ?? "")
|
const releaseLoadtypeCheck = (release.AdditionalInformation1 ?? "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.split(",")
|
.split(",")
|
||||||
.map((x) => x.trim())
|
.map((x) => x.trim())
|
||||||
.includes("drop");
|
.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({
|
const opendDockArticleCheck = await db.query.opendockArticleSetup.findFirst({
|
||||||
where: (table, { and, eq }) =>
|
where: (table, { and, eq }) =>
|
||||||
and(
|
and(
|
||||||
eq(table.av, release.LineItemArticleWeight),
|
eq(table.av, release.LineItemHumanReadableId),
|
||||||
eq(table.customer, release.DeliveryAddressHumanReadableId),
|
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
|
// TODO: add in docks from lst db here to make it more universal for the team
|
||||||
/**
|
/**
|
||||||
* ReleaseState
|
* ReleaseState
|
||||||
@@ -127,7 +170,7 @@ const postRelease = async (release: Releases) => {
|
|||||||
userId: process.env.DEFAULT_CARRIER, // this should be the carrierid
|
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
|
loadTypeId: process.env.DEFAULT_LOAD_TYPE, // well get this and make it a default one
|
||||||
// TODO: look in the remarks in the release and if its says
|
// TODO: look in the remarks in the release and if its says
|
||||||
dockId: process.env.DEFAULT_DOCK, // this the warehouse we want it in to start out
|
dockId: setDock, // this the warehouse we want it in to start out
|
||||||
refNumbers: [release.ReleaseNumber],
|
refNumbers: [release.ReleaseNumber],
|
||||||
//refNumber: release.ReleaseNumber,
|
//refNumber: release.ReleaseNumber,
|
||||||
start: release.DeliveryDate,
|
start: release.DeliveryDate,
|
||||||
@@ -403,7 +446,12 @@ const postRelease = async (release: Releases) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (
|
||||||
|
(releaseLoadtypeCheck ||
|
||||||
|
opendDockArticleCheck?.loadType === "drop" ||
|
||||||
|
defaultDock?.value === "drop") &&
|
||||||
|
releaseOkToSchedule
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${process.env.OPENDOCK_URL}/appointment`,
|
`${process.env.OPENDOCK_URL}/appointment`,
|
||||||
@@ -458,6 +506,76 @@ const postRelease = async (release: Releases) => {
|
|||||||
|
|
||||||
return;
|
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
|
await delay(750); // rate limit protection
|
||||||
|
|||||||
@@ -1,11 +1,80 @@
|
|||||||
SELECT count(*) as activated
|
SELECT count(*) as activated
|
||||||
FROM [test1_AlplaPROD2.0_Read].[support].[FeatureActivation]
|
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.
|
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
|
[DefaultTranslation("Blocking")]
|
||||||
7 = warehousing
|
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
|
||||||
*/
|
*/
|
||||||
|
|||||||
6
backend/prodSql/queries/outbound.docks.sql
Normal file
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
|
||||||
@@ -4,6 +4,7 @@ import { setupAuthRoutes } from "./auth/auth.routes.js";
|
|||||||
// import the routes and route setups
|
// import the routes and route setups
|
||||||
import { setupApiDocsRoutes } from "./configs/scaler.config.js";
|
import { setupApiDocsRoutes } from "./configs/scaler.config.js";
|
||||||
import { setupDatamartRoutes } from "./datamart/datamart.routes.js";
|
import { setupDatamartRoutes } from "./datamart/datamart.routes.js";
|
||||||
|
import { setupDockDoorRoutes } from "./dockdoorScanning/dockdoor.routes.js";
|
||||||
import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js";
|
import { setupGPSqlRoutes } from "./gpSql/gpSql.routes.js";
|
||||||
import { setupMobileRoutes } from "./mobile/mobile.routes.js";
|
import { setupMobileRoutes } from "./mobile/mobile.routes.js";
|
||||||
import { setupNotificationRoutes } from "./notification/notification.routes.js";
|
import { setupNotificationRoutes } from "./notification/notification.routes.js";
|
||||||
@@ -29,4 +30,5 @@ export const setupRoutes = (baseUrl: string, app: Express) => {
|
|||||||
setupNotificationRoutes(baseUrl, app);
|
setupNotificationRoutes(baseUrl, app);
|
||||||
setupOCPRoutes(baseUrl, app);
|
setupOCPRoutes(baseUrl, app);
|
||||||
setupTCPRoutes(baseUrl, app);
|
setupTCPRoutes(baseUrl, app);
|
||||||
|
setupDockDoorRoutes(baseUrl, app);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
} from "./utils/analyticRouteHits.utils.js";
|
} from "./utils/analyticRouteHits.utils.js";
|
||||||
import { createCronJob } from "./utils/croner.utils.js";
|
import { createCronJob } from "./utils/croner.utils.js";
|
||||||
import { sendEmail } from "./utils/sendEmail.utils.js";
|
import { sendEmail } from "./utils/sendEmail.utils.js";
|
||||||
|
import { ppooMonitoring } from "./warehousing/warehousing.ppooMonitor.js";
|
||||||
|
|
||||||
const port = Number(process.env.PORT) || 3000;
|
const port = Number(process.env.PORT) || 3000;
|
||||||
export let systemSettings: Setting[] = [];
|
export let systemSettings: Setting[] = [];
|
||||||
@@ -78,6 +79,10 @@ const start = async () => {
|
|||||||
runRouteHitAnalyticsCron(),
|
runRouteHitAnalyticsCron(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
createCronJob("ppooMonitor", "*/45 * * * * *", async () =>
|
||||||
|
ppooMonitoring(),
|
||||||
|
);
|
||||||
|
|
||||||
createCronJob("cleanHitsUp", "0 0 7 * * *", () => cleanupOldRouteHits());
|
createCronJob("cleanHitsUp", "0 0 7 * * *", () => cleanupOldRouteHits());
|
||||||
// one shots only needed to run on server startups
|
// one shots only needed to run on server startups
|
||||||
createNotifications();
|
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 MAX_HISTORY = 50;
|
||||||
export const FLUSH_INTERVAL = 100; // 50ms change higher if needed
|
export const FLUSH_INTERVAL = 100; // 50ms change higher if needed
|
||||||
|
|||||||
@@ -1,17 +1,57 @@
|
|||||||
import { desc } from "drizzle-orm";
|
import { desc } from "drizzle-orm";
|
||||||
import { db } from "../db/db.controller.js";
|
import { db } from "../db/db.controller.js";
|
||||||
import { logs } from "../db/schema/logs.schema.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> = {
|
type RoomDefinition<T = unknown> = {
|
||||||
seed: (limit: number) => Promise<T[]>;
|
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"] },
|
logs: { requiresAuth: true, role: ["admin", "systemAdmin"] },
|
||||||
//admin: { requiresAuth: false, 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> = {
|
export const roomDefinition: Record<RoomId, RoomDefinition> = {
|
||||||
logs: {
|
logs: {
|
||||||
seed: async (limit) => {
|
seed: async (limit) => {
|
||||||
@@ -48,4 +88,22 @@ export const roomDefinition: Record<RoomId, RoomDefinition> = {
|
|||||||
return [];
|
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
|
// 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;
|
let addDataToRoom: ((roomId: RoomId, payload: unknown[]) => void) | null = null;
|
||||||
|
|
||||||
|
|||||||
@@ -7,18 +7,33 @@ import {
|
|||||||
roomFlushTimers,
|
roomFlushTimers,
|
||||||
roomHistory,
|
roomHistory,
|
||||||
} from "./roomCache.socket.js";
|
} from "./roomCache.socket.js";
|
||||||
import { roomDefinition } from "./roomDefinitions.socket.js";
|
import { type RoomId, roomDefinition } from "./roomDefinitions.socket.js";
|
||||||
import type { RoomId } from "./types.socket.js";
|
|
||||||
|
|
||||||
// get the db data if not exiting already
|
// get the db data if not exiting already
|
||||||
const log = createLogger({ module: "socket.io", subModule: "roomService" });
|
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) => {
|
export const preseedRoom = async (roomId: RoomId) => {
|
||||||
if (roomHistory.has(roomId)) {
|
if (roomHistory.has(roomId)) {
|
||||||
return roomHistory.get(roomId);
|
return roomHistory.get(roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roomDef = roomDefinition[roomId];
|
const roomDef = roomDefinition[roomId] as any;
|
||||||
|
|
||||||
if (!roomDef) {
|
if (!roomDef) {
|
||||||
log.error({}, `Room ${roomId} is not defined`);
|
log.error({}, `Room ${roomId} is not defined`);
|
||||||
@@ -32,7 +47,7 @@ export const preseedRoom = async (roomId: RoomId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createRoomEmitter = (io: Server) => {
|
export const createRoomEmitter = (io: Server) => {
|
||||||
const addDataToRoom = <T>(roomId: RoomId, payload: T) => {
|
const addDataToRoom = <T>(roomId: RoomId, payload: T[]) => {
|
||||||
if (!roomHistory.has(roomId)) {
|
if (!roomHistory.has(roomId)) {
|
||||||
roomHistory.set(roomId, []);
|
roomHistory.set(roomId, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import { Server } from "socket.io";
|
|||||||
import { createLogger } from "../logger/logger.controller.js";
|
import { createLogger } from "../logger/logger.controller.js";
|
||||||
import { allowedOrigins } from "../utils/cors.utils.js";
|
import { allowedOrigins } from "../utils/cors.utils.js";
|
||||||
import { registerEmitter } from "./roomEmitter.socket.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 __filename = fileURLToPath(import.meta.url);
|
||||||
//const __dirname = dirname(__filename);
|
//const __dirname = dirname(__filename);
|
||||||
@@ -15,7 +19,7 @@ const log = createLogger({ module: "socket.io", subModule: "setup" });
|
|||||||
|
|
||||||
import { auth } from "../utils/auth.utils.js";
|
import { auth } from "../utils/auth.utils.js";
|
||||||
//import type { Session, User } from "better-auth"; // adjust if needed
|
//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" {
|
// declare module "socket.io" {
|
||||||
// interface Socket {
|
// interface Socket {
|
||||||
@@ -33,6 +37,9 @@ export const setupSocketIORoutes = (baseUrl: string, server: HttpServer) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// manage members of the rooms.
|
||||||
|
registerRoomService(io);
|
||||||
|
|
||||||
// ✅ Create emitter instance
|
// ✅ Create emitter instance
|
||||||
const { addDataToRoom } = createRoomEmitter(io);
|
const { addDataToRoom } = createRoomEmitter(io);
|
||||||
registerEmitter(addDataToRoom);
|
registerEmitter(addDataToRoom);
|
||||||
@@ -78,38 +85,76 @@ export const setupSocketIORoutes = (baseUrl: string, server: HttpServer) => {
|
|||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
s.on("join-room", async (rn) => {
|
// s.on("join-room", async (rn) => {
|
||||||
const config = protectedRooms[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", {
|
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",
|
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 (roles.length > 0 && !roles.includes(s.user?.role)) {
|
||||||
if (config?.role && !roles.includes(s.user?.role)) {
|
|
||||||
return s.emit("room-error", {
|
return s.emit("room-error", {
|
||||||
roomId: rn,
|
roomId: rn,
|
||||||
message: `Not authorized to be in room: ${rn}`,
|
message: `Not authorized to be in room: ${rn}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
s.join(rn);
|
s.join(rn);
|
||||||
|
|
||||||
// get room seeded
|
const history = await preseedRoom(rn as any);
|
||||||
const history = await preseedRoom(rn);
|
|
||||||
log.info({}, `User joined ${rn}: ${s.id}`);
|
log.info({}, `User joined ${rn}: ${s.id}`);
|
||||||
// send the intial data
|
|
||||||
s.emit("room-update", {
|
s.emit("room-update", {
|
||||||
roomId: rn,
|
roomId: rn,
|
||||||
payloads: history,
|
payloads: history,
|
||||||
initial: true,
|
initial: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
s.on("leave-room", (room) => {
|
s.on("leave-room", (room) => {
|
||||||
s.leave(room);
|
s.leave(room);
|
||||||
log.info({}, `${s.id} left room: ${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",
|
serverLoc: "D$\\LST_V3",
|
||||||
buildNumber: 1,
|
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
|
// 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"],
|
roles: ["admin"],
|
||||||
seedVersion: 1,
|
seedVersion: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "dockDoorScanning",
|
||||||
|
value: "0",
|
||||||
|
active: false,
|
||||||
|
description: "dock door scanning",
|
||||||
|
moduleName: "dockDoorScanning",
|
||||||
|
settingType: "feature",
|
||||||
|
roles: ["admin"],
|
||||||
|
seedVersion: 1,
|
||||||
|
},
|
||||||
|
|
||||||
// standard settings
|
// 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",
|
name: "prolinkCheck",
|
||||||
value: "1",
|
value: "1",
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { db } from "../db/db.controller.js";
|
import { db } from "../db/db.controller.js";
|
||||||
|
import { dockDoorScanners } from "../db/schema/dockdoor.schema.js";
|
||||||
import { printerData } from "../db/schema/printers.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 { createLogger } from "../logger/logger.controller.js";
|
||||||
import { delay } from "../utils/delay.utils.js";
|
import { delay } from "../utils/delay.utils.js";
|
||||||
import { returnFunc } from "../utils/returnHelper.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);
|
const port = parseInt(process.env.TCP_PORT ?? "2222", 10);
|
||||||
|
|
||||||
|
// This is the parser for zebra scanners
|
||||||
const parseTcpAlert = (input: string) => {
|
const parseTcpAlert = (input: string) => {
|
||||||
// guard
|
// guard
|
||||||
const colonIndex = input.indexOf(":");
|
const colonIndex = input.indexOf(":");
|
||||||
@@ -74,6 +77,23 @@ export const startTCPServer = async () => {
|
|||||||
|
|
||||||
printerListen(printerData as PrinterData);
|
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", () => {
|
socket.on("end", () => {
|
||||||
|
|||||||
@@ -13,7 +13,14 @@ import {
|
|||||||
//import { eq } from "drizzle-orm";
|
//import { eq } from "drizzle-orm";
|
||||||
import { db } from "../db/db.controller.js";
|
import { db } from "../db/db.controller.js";
|
||||||
import * as rawSchema from "../db/schema/auth.schema.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 { allowedOrigins } from "./cors.utils.js";
|
||||||
import { sendEmail } from "./sendEmail.utils.js";
|
import { sendEmail } from "./sendEmail.utils.js";
|
||||||
|
|
||||||
@@ -164,6 +171,7 @@ export const auth = betterAuth({
|
|||||||
admin,
|
admin,
|
||||||
user,
|
user,
|
||||||
manager,
|
manager,
|
||||||
|
transport,
|
||||||
systemAdmin,
|
systemAdmin,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export interface ReturnHelper<T = unknown[]> {
|
|||||||
| "tcp"
|
| "tcp"
|
||||||
| "logistics"
|
| "logistics"
|
||||||
| "admin"
|
| "admin"
|
||||||
| "mobile";
|
| "mobile"
|
||||||
|
| "dockdoor";
|
||||||
subModule: string;
|
subModule: string;
|
||||||
|
|
||||||
level: "info" | "error" | "debug" | "fatal" | "warn";
|
level: "info" | "error" | "debug" | "fatal" | "warn";
|
||||||
|
|||||||
29
backend/warehousing/warehousing.ppooMonitor.ts
Normal file
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);
|
||||||
|
};
|
||||||
49
frontend/src/components/ui/badge.tsx
Normal file
49
frontend/src/components/ui/badge.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
import { Slot } from "radix-ui"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
||||||
|
outline:
|
||||||
|
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
||||||
|
ghost:
|
||||||
|
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Badge({
|
||||||
|
className,
|
||||||
|
variant = "default",
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"span"> &
|
||||||
|
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||||
|
const Comp = asChild ? Slot.Root : "span"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="badge"
|
||||||
|
data-variant={variant}
|
||||||
|
className={cn(badgeVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants }
|
||||||
@@ -11,9 +11,12 @@ type RoomErrorPayload = {
|
|||||||
message?: string;
|
message?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UpdateMode = "append" | "replace";
|
||||||
|
|
||||||
export function useSocketRoom<T>(
|
export function useSocketRoom<T>(
|
||||||
roomId: string,
|
roomId: string,
|
||||||
getKey?: (item: T) => string | number,
|
getKey?: (item: T) => string | number,
|
||||||
|
updateMode: UpdateMode = "append",
|
||||||
) {
|
) {
|
||||||
const [data, setData] = useState<T[]>([]);
|
const [data, setData] = useState<T[]>([]);
|
||||||
const [info, setInfo] = useState(
|
const [info, setInfo] = useState(
|
||||||
@@ -44,7 +47,13 @@ export function useSocketRoom<T>(
|
|||||||
// protects against other room updates hitting this hook
|
// protects against other room updates hitting this hook
|
||||||
if (payload.roomId !== roomId) return;
|
if (payload.roomId !== roomId) return;
|
||||||
|
|
||||||
|
// resetting room data for rooms that just need updated data.
|
||||||
|
if (updateMode === "replace") {
|
||||||
|
setData(payload.payloads);
|
||||||
|
} else {
|
||||||
setData((prev) => [...payload.payloads, ...prev]);
|
setData((prev) => [...payload.payloads, ...prev]);
|
||||||
|
}
|
||||||
|
|
||||||
setInfo("");
|
setInfo("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,14 +62,14 @@ export function useSocketRoom<T>(
|
|||||||
setInfo(err.message ?? "Room error");
|
setInfo(err.message ?? "Room error");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!socket.connected) {
|
|
||||||
socket.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.on("connect", handleConnect);
|
socket.on("connect", handleConnect);
|
||||||
socket.on("room-update", handleUpdate);
|
socket.on("room-update", handleUpdate);
|
||||||
socket.on("room-error", handleError);
|
socket.on("room-error", handleError);
|
||||||
|
|
||||||
|
if (!socket.connected && socket.disconnected) {
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
|
||||||
// If already connected, join immediately
|
// If already connected, join immediately
|
||||||
if (socket.connected) {
|
if (socket.connected) {
|
||||||
socket.emit("join-room", roomId);
|
socket.emit("join-room", roomId);
|
||||||
@@ -74,7 +83,18 @@ export function useSocketRoom<T>(
|
|||||||
socket.off("room-update", handleUpdate);
|
socket.off("room-update", handleUpdate);
|
||||||
socket.off("room-error", handleError);
|
socket.off("room-error", handleError);
|
||||||
};
|
};
|
||||||
}, [roomId]);
|
}, [roomId, updateMode]);
|
||||||
|
|
||||||
return { data, info, clearRoom };
|
return { data, info, clearRoom };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
const isDockDoorPage = location.pathname.startsWith("/dockdoor");
|
||||||
|
|
||||||
|
useSocketRoom(
|
||||||
|
dockId ? `dockdoor:${dockId}` : null,
|
||||||
|
isDockDoorPage,
|
||||||
|
);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ type RuntimeConfig = {
|
|||||||
appVersion: string;
|
appVersion: string;
|
||||||
umamiHost: string;
|
umamiHost: string;
|
||||||
umamiWebsiteId: string;
|
umamiWebsiteId: string;
|
||||||
|
timezone: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -23,6 +24,7 @@ export const runtimeConfig: RuntimeConfig = {
|
|||||||
appVersion: window.LST_CONFIG?.appVersion ?? "dev",
|
appVersion: window.LST_CONFIG?.appVersion ?? "dev",
|
||||||
umamiHost: window.LST_CONFIG?.umamiHost ?? "",
|
umamiHost: window.LST_CONFIG?.umamiHost ?? "",
|
||||||
umamiWebsiteId: window.LST_CONFIG?.umamiWebsiteId ?? "",
|
umamiWebsiteId: window.LST_CONFIG?.umamiWebsiteId ?? "",
|
||||||
|
timezone: window.LST_CONFIG?.timezone ?? "America/Chicago",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function loadUmami() {
|
export function loadUmami() {
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
|
import { createColumnHelper } from "@tanstack/react-table";
|
||||||
|
import { formatInTimeZone } from "date-fns-tz";
|
||||||
import { useSocketRoom } from "@/hooks/socket.io.hook";
|
import { useSocketRoom } from "@/hooks/socket.io.hook";
|
||||||
import { authClient } from "@/lib/auth-client";
|
import { authClient } from "@/lib/auth-client";
|
||||||
|
import { Badge } from "../../components/ui/badge";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "../../components/ui/tooltip";
|
||||||
|
import LstTable from "../../lib/tableStuff/LstTable";
|
||||||
|
import SearchableHeader from "../../lib/tableStuff/SearchableHeader";
|
||||||
|
|
||||||
export const Route = createFileRoute("/admin/logs")({
|
export const Route = createFileRoute("/admin/logs")({
|
||||||
beforeLoad: async ({ location }) => {
|
beforeLoad: async ({ location }) => {
|
||||||
@@ -37,117 +47,168 @@ interface LogEntry {
|
|||||||
[key: string]: any; // catch any extra fields
|
[key: string]: any; // catch any extra fields
|
||||||
}
|
}
|
||||||
|
|
||||||
function LevelBadge({ level }: { level: number }) {
|
function LevelBadge({ level }: { level: string }) {
|
||||||
const config: Record<number, { label: string; className: string }> = {
|
const config: Record<string, { label: string; className: string }> = {
|
||||||
10: { label: "TRACE", className: "bg-gray-100 text-gray-600" },
|
trace: { label: "TRACE", className: "bg-gray-100 text-gray-600" },
|
||||||
20: { label: "DEBUG", className: "bg-blue-100 text-blue-700" },
|
debug: { label: "DEBUG", className: "bg-blue-50 text-blue-700" },
|
||||||
30: { label: "INFO", className: "bg-green-100 text-green-700" },
|
info: { label: "INFO", className: "bg-green-50 text-green-700" },
|
||||||
40: { label: "WARN", className: "bg-yellow-100 text-yellow-700" },
|
warn: { label: "WARN", className: "bg-yellow-100 text-yellow-700" },
|
||||||
50: { label: "ERROR", className: "bg-red-100 text-red-700" },
|
error: { label: "ERROR", className: "bg-red-50 text-red-700" },
|
||||||
60: { label: "FATAL", className: "bg-purple-100 text-purple-700" },
|
fatal: { label: "FATAL", className: "bg-purple-100 text-purple-700" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const { label, className } = config[level] ?? {
|
const badge = config[level];
|
||||||
label: String(level),
|
|
||||||
className: "bg-gray-100",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={`px-2 py-0.5 rounded text-xs font-medium ${className}`}>
|
<Badge
|
||||||
{label}
|
className={`rounded px-2 py-0.5 text-xs font-medium ${
|
||||||
</span>
|
badge?.className ?? "bg-gray-100 text-gray-600"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{badge?.label ?? level}
|
||||||
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { data: logs, info: logsInfo } = useSocketRoom<LogEntry>("logs");
|
const { data: logs } = useSocketRoom<LogEntry>("logs");
|
||||||
//const { user } = Route.useRouteContext();
|
const columnHelper = createColumnHelper<any>();
|
||||||
//const router = useRouter();
|
|
||||||
// const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
||||||
// const [logsInfo, setLogInfo] = useState(
|
|
||||||
// "No logs yet — join the room to start receiving",
|
|
||||||
// );
|
|
||||||
|
|
||||||
// useEffect(() => {
|
const column = [
|
||||||
// // Connect if not already connected
|
columnHelper.accessor("createdAt", {
|
||||||
// if (!socket.connected) {
|
header: ({ column }) => <SearchableHeader column={column} title="Time" />,
|
||||||
// socket.connect();
|
filterFn: "includesString",
|
||||||
// }
|
cell: (i) =>
|
||||||
|
formatInTimeZone(
|
||||||
// socket.on("connect", () => {
|
i.getValue(),
|
||||||
// socket.emit("join-room", "logs");
|
`${window.LST_CONFIG?.timezone}`,
|
||||||
// });
|
"MM/dd/yyyy HH:mm:ss",
|
||||||
|
),
|
||||||
// socket.emit("join-room", "logs");
|
}),
|
||||||
// socket.on(
|
columnHelper.accessor("level", {
|
||||||
// "room-update",
|
header: ({ column }) => (
|
||||||
// (data: { payloads: LogEntry[]; roomId: string }) => {
|
<SearchableHeader column={column} title="Level" searchable={true} />
|
||||||
// setLogs((prev) => [...data.payloads, ...prev]);
|
),
|
||||||
// },
|
filterFn: "includesString",
|
||||||
// );
|
cell: (i) => <LevelBadge level={i.getValue()} />,
|
||||||
|
}),
|
||||||
// socket.on("room-error", (data) => {
|
columnHelper.accessor("module", {
|
||||||
// setLogInfo(data.message);
|
header: ({ column }) => (
|
||||||
// });
|
<SearchableHeader column={column} title="Module" />
|
||||||
|
),
|
||||||
// // socket.on("logs", (data) => {
|
filterFn: "includesString",
|
||||||
// // console.log(data);
|
cell: (i) => i.getValue(),
|
||||||
// // setLogs((prev) => [...data.payloads, ...prev]);
|
}),
|
||||||
// // });
|
columnHelper.accessor("subModule", {
|
||||||
|
header: ({ column }) => (
|
||||||
// // Cleanup listeners on unmount
|
<SearchableHeader column={column} title="Submodule" />
|
||||||
// return () => {
|
),
|
||||||
// socket.emit("leave-room", "logs");
|
filterFn: "includesString",
|
||||||
// socket.off("room-update");
|
cell: (i) => i.getValue(),
|
||||||
// socket.off("room-error");
|
}),
|
||||||
// socket.off("logs");
|
columnHelper.accessor("hostname", {
|
||||||
// };
|
header: ({ column }) => (
|
||||||
// }, []);
|
<SearchableHeader column={column} title="Client" />
|
||||||
return (
|
),
|
||||||
<div>
|
filterFn: "includesString",
|
||||||
{/* Log Table */}
|
cell: (i) => i.getValue(),
|
||||||
<div className="rounded border overflow-auto max-h-[600px]">
|
}),
|
||||||
<table className="w-full text-sm">
|
columnHelper.accessor("message", {
|
||||||
<thead className="bg-muted sticky top-0">
|
header: ({ column }) => (
|
||||||
<tr>
|
<SearchableHeader column={column} title="Message" />
|
||||||
<th className="text-left px-3 py-2">Time</th>
|
),
|
||||||
<th className="text-left px-3 py-2">Level</th>
|
filterFn: "includesString",
|
||||||
<th className="text-left px-3 py-2">Module</th>
|
cell: (i) => {
|
||||||
<th className="text-left px-3 py-2">Host</th>
|
return i.getValue().length > 50 ? (
|
||||||
<th className="text-left px-3 py-2">Message</th>
|
<Tooltip>
|
||||||
</tr>
|
<TooltipTrigger>
|
||||||
</thead>
|
<p>{i.getValue().slice(0, 50)}...</p>
|
||||||
<tbody>
|
</TooltipTrigger>
|
||||||
{logs.length === 0 ? (
|
<TooltipContent>
|
||||||
<tr>
|
<p className="text-wrap max-w-48">{i.getValue()}</p>
|
||||||
<td
|
</TooltipContent>
|
||||||
colSpan={6}
|
</Tooltip>
|
||||||
className="text-center py-6 text-muted-foreground"
|
|
||||||
>
|
|
||||||
{logsInfo}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
) : (
|
) : (
|
||||||
logs.map((log, i) => (
|
i.getValue()
|
||||||
<tr
|
);
|
||||||
key={`${log.id}-${i}`}
|
},
|
||||||
className="border-t hover:bg-muted/50"
|
}),
|
||||||
|
columnHelper.accessor("stack", {
|
||||||
|
header: ({ column }) => (
|
||||||
|
<SearchableHeader column={column} title="Stack" />
|
||||||
|
),
|
||||||
|
filterFn: "includesString",
|
||||||
|
cell: (i) => {
|
||||||
|
const stack = i.row.original.stack;
|
||||||
|
if (!stack) return <span className="text-muted-foreground">—</span>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<p className="max-w-[350px] truncate text-left font-mono text-xs text-muted-foreground hover:text-foreground">
|
||||||
|
{stack.length !== 0 ? "Hover to see stack error" : "-"}
|
||||||
|
</p>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent
|
||||||
|
side="left"
|
||||||
|
align="start"
|
||||||
|
className="max-w-[900px] bg-zinc-950 text-zinc-50"
|
||||||
>
|
>
|
||||||
<td className="px-3 py-2 whitespace-nowrap">
|
<pre className="max-h-[500px] overflow-auto whitespace-pre-wrap rounded-md p-3 text-xs leading-relaxed">
|
||||||
{new Date(log.createdAt).toLocaleTimeString()}
|
<code>{JSON.stringify(stack, null, 2)}</code>
|
||||||
</td>
|
</pre>
|
||||||
<td className="px-3 py-2">
|
</TooltipContent>
|
||||||
<LevelBadge level={log.level} />
|
</Tooltip>
|
||||||
</td>
|
);
|
||||||
<td className="px-3 py-2">{log.module}</td>
|
},
|
||||||
<td className="px-3 py-2">{log.hostname}</td>
|
}),
|
||||||
<td className="px-3 py-2 max-w-sm truncate">{log.message}</td>
|
];
|
||||||
</tr>
|
return (
|
||||||
))
|
// <div>
|
||||||
)}
|
// {/* Log Table */}
|
||||||
</tbody>
|
// <div className="rounded border overflow-auto max-h-[600px]">
|
||||||
</table>
|
// <table className="w-full text-sm">
|
||||||
</div>
|
// <thead className="bg-muted sticky top-0">
|
||||||
</div>
|
// <tr>
|
||||||
|
// <th className="text-left px-3 py-2">Time</th>
|
||||||
|
// <th className="text-left px-3 py-2">Level</th>
|
||||||
|
// <th className="text-left px-3 py-2">Module</th>
|
||||||
|
// <th className="text-left px-3 py-2">Host</th>
|
||||||
|
// <th className="text-left px-3 py-2">Message</th>
|
||||||
|
// </tr>
|
||||||
|
// </thead>
|
||||||
|
// <tbody>
|
||||||
|
// {logs.length === 0 ? (
|
||||||
|
// <tr>
|
||||||
|
// <td
|
||||||
|
// colSpan={6}
|
||||||
|
// className="text-center py-6 text-muted-foreground"
|
||||||
|
// >
|
||||||
|
// {logsInfo}
|
||||||
|
// </td>
|
||||||
|
// </tr>
|
||||||
|
// ) : (
|
||||||
|
// logs.map((log, i) => (
|
||||||
|
// <tr
|
||||||
|
// key={`${log.id}-${i}`}
|
||||||
|
// className="border-t hover:bg-muted/50"
|
||||||
|
// >
|
||||||
|
// <td className="px-3 py-2 whitespace-nowrap">
|
||||||
|
// {new Date(log.createdAt).toLocaleTimeString()}
|
||||||
|
// </td>
|
||||||
|
// <td className="px-3 py-2">
|
||||||
|
// <LevelBadge level={log.level} />
|
||||||
|
// </td>
|
||||||
|
// <td className="px-3 py-2">{log.module}</td>
|
||||||
|
// <td className="px-3 py-2">{log.hostname}</td>
|
||||||
|
// <td className="px-3 py-2 max-w-sm truncate">{log.message}</td>
|
||||||
|
// </tr>
|
||||||
|
// ))
|
||||||
|
// )}
|
||||||
|
// </tbody>
|
||||||
|
// </table>
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
<LstTable data={logs} columns={column} pageSize={50} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useMutation, useSuspenseQuery } from "@tanstack/react-query";
|
import { useMutation, useSuspenseQuery } from "@tanstack/react-query";
|
||||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||||
import { createColumnHelper } from "@tanstack/react-table";
|
import { createColumnHelper } from "@tanstack/react-table";
|
||||||
import axios from "axios";
|
|
||||||
import { format } from "date-fns-tz";
|
import { format } from "date-fns-tz";
|
||||||
import { Trash } from "lucide-react";
|
import { Trash } from "lucide-react";
|
||||||
import { Suspense, useState } from "react";
|
import { Suspense, useState } from "react";
|
||||||
@@ -56,7 +55,7 @@ const updateSettings = async (
|
|||||||
) => {
|
) => {
|
||||||
//console.log(id, data);
|
//console.log(id, data);
|
||||||
try {
|
try {
|
||||||
const res = await axios.patch(`/mobile/auth/user/${id}`, data, {
|
const res = await api.patch(`/mobile/auth/user/${id}`, data, {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
validateStatus: () => true,
|
validateStatus: () => true,
|
||||||
@@ -121,7 +120,7 @@ const ScanUserTable = () => {
|
|||||||
<EditableCellInput
|
<EditableCellInput
|
||||||
value={getValue()}
|
value={getValue()}
|
||||||
id={row.original.id}
|
id={row.original.id}
|
||||||
field="value"
|
field="pinNumber"
|
||||||
onSubmit={({ id, field, value }) => {
|
onSubmit={({ id, field, value }) => {
|
||||||
updateSetting.mutate({ id, field, value });
|
updateSetting.mutate({ id, field, value });
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { useSession } from "../lib/auth-client";
|
import { useSession } from "../lib/auth-client";
|
||||||
import { trackLstEvent } from "../lib/umami.utils";
|
import { trackLstEvent } from "../lib/umami.utils";
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,10 @@ export default function NewArticleLink({ refetch }: { refetch: any }) {
|
|||||||
label: "Matrix",
|
label: "Matrix",
|
||||||
value: "matrix",
|
value: "matrix",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "RB",
|
||||||
|
value: "rb",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"name": "LST mobile",
|
"name": "LST mobile",
|
||||||
"slug": "lst-mobile",
|
"slug": "lst-mobile",
|
||||||
"version": "0.11.1-alpha",
|
"version": "0.11.1-alpha",
|
||||||
"orientation": "portrait",
|
"orientation": "default",
|
||||||
"icon": "./assets/icon_white.png",
|
"icon": "./assets/icon_white.png",
|
||||||
"scheme": "lstmobile",
|
"scheme": "lstmobile",
|
||||||
"userInterfaceStyle": "automatic",
|
"userInterfaceStyle": "automatic",
|
||||||
@@ -15,10 +15,14 @@
|
|||||||
"foregroundImage": "./assets/adaptive-icon-white.png",
|
"foregroundImage": "./assets/adaptive-icon-white.png",
|
||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
},
|
},
|
||||||
"versionCode": 37,
|
"versionCode": 42,
|
||||||
"minSupportedVersionCode": 33,
|
"minSupportedVersionCode": 33,
|
||||||
"predictiveBackGestureEnabled": false,
|
"predictiveBackGestureEnabled": false,
|
||||||
"package": "net.alpla.lst.mobile"
|
"package": "net.alpla.lst.mobile",
|
||||||
|
"permissions": [
|
||||||
|
"android.permission.WRITE_EXTERNAL_STORAGE",
|
||||||
|
"android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"output": "static",
|
"output": "static",
|
||||||
|
|||||||
104
lstMobile/package-lock.json
generated
104
lstMobile/package-lock.json
generated
@@ -16,7 +16,7 @@
|
|||||||
"@rn-primitives/portal": "^1.4.0",
|
"@rn-primitives/portal": "^1.4.0",
|
||||||
"@rn-primitives/separator": "^1.4.0",
|
"@rn-primitives/separator": "^1.4.0",
|
||||||
"@rn-primitives/slot": "^1.4.0",
|
"@rn-primitives/slot": "^1.4.0",
|
||||||
"@tanstack/react-query": "^5.99.0",
|
"@tanstack/react-query": "^5.100.14",
|
||||||
"axios": "^1.15.0",
|
"axios": "^1.15.0",
|
||||||
"babel-preset-expo": "^55.0.18",
|
"babel-preset-expo": "^55.0.18",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
"expo-image": "~55.0.8",
|
"expo-image": "~55.0.8",
|
||||||
"expo-linking": "~55.0.13",
|
"expo-linking": "~55.0.13",
|
||||||
"expo-router": "~55.0.12",
|
"expo-router": "~55.0.12",
|
||||||
|
"expo-screen-orientation": "~55.0.16",
|
||||||
"expo-splash-screen": "~55.0.18",
|
"expo-splash-screen": "~55.0.18",
|
||||||
"expo-status-bar": "~55.0.5",
|
"expo-status-bar": "~55.0.5",
|
||||||
"expo-symbols": "~55.0.7",
|
"expo-symbols": "~55.0.7",
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-native": "0.83.4",
|
"react-native": "0.83.4",
|
||||||
|
"react-native-blob-util": "^0.24.9",
|
||||||
"react-native-gesture-handler": "~2.30.0",
|
"react-native-gesture-handler": "~2.30.0",
|
||||||
"react-native-reanimated": "4.2.1",
|
"react-native-reanimated": "4.2.1",
|
||||||
"react-native-safe-area-context": "~5.6.2",
|
"react-native-safe-area-context": "~5.6.2",
|
||||||
@@ -5316,9 +5318,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/query-core": {
|
"node_modules/@tanstack/query-core": {
|
||||||
"version": "5.100.9",
|
"version": "5.100.14",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.14.tgz",
|
||||||
"integrity": "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==",
|
"integrity": "sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -5326,12 +5328,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/react-query": {
|
"node_modules/@tanstack/react-query": {
|
||||||
"version": "5.100.9",
|
"version": "5.100.14",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.9.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.14.tgz",
|
||||||
"integrity": "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==",
|
"integrity": "sha512-oOr6aRdSFEwWhzxEkD/9ZcItM3+LjBSkeVmadWKwUssAHTsqd/7bOjWrX4AbvEkoEhgAxzN0Xk6H/aYzXiYBAw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/query-core": "5.100.9"
|
"@tanstack/query-core": "5.100.14"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -6132,6 +6134,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/base-64": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -8171,6 +8178,16 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-screen-orientation": {
|
||||||
|
"version": "55.0.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-screen-orientation/-/expo-screen-orientation-55.0.16.tgz",
|
||||||
|
"integrity": "sha512-I9NIqb2zAkHsK/CxdmMdmgSFP7E1v++8z/Mj2X9j1AuK6l55yOma/JHo905KU3x2zPm9/l1BTzmMA320tiBebg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-server": {
|
"node_modules/expo-server": {
|
||||||
"version": "55.0.9",
|
"version": "55.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/expo-server/-/expo-server-55.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/expo-server/-/expo-server-55.0.9.tgz",
|
||||||
@@ -12821,6 +12838,77 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-blob-util": {
|
||||||
|
"version": "0.24.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-blob-util/-/react-native-blob-util-0.24.9.tgz",
|
||||||
|
"integrity": "sha512-tG3+m0WhVdBGifvxSFxZDVqtr85D0fGBJU6E4UxmK3tU+RabJZTumXEn8k7jn5/NFe8OhQhPjtBEZ11ZJ6L7Vw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base-64": "0.1.0",
|
||||||
|
"glob": "13.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ronradtke"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-blob-util/node_modules/balanced-match": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-blob-util/node_modules/brace-expansion": {
|
||||||
|
"version": "5.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||||
|
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-blob-util/node_modules/glob": {
|
||||||
|
"version": "13.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.1.tgz",
|
||||||
|
"integrity": "sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w==",
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^10.1.2",
|
||||||
|
"minipass": "^7.1.2",
|
||||||
|
"path-scurry": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-blob-util/node_modules/minimatch": {
|
||||||
|
"version": "10.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||||
|
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||||
|
"license": "BlueOak-1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^5.0.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "18 || 20 || >=22"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-css-interop": {
|
"node_modules/react-native-css-interop": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.3.tgz",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"@rn-primitives/portal": "^1.4.0",
|
"@rn-primitives/portal": "^1.4.0",
|
||||||
"@rn-primitives/separator": "^1.4.0",
|
"@rn-primitives/separator": "^1.4.0",
|
||||||
"@rn-primitives/slot": "^1.4.0",
|
"@rn-primitives/slot": "^1.4.0",
|
||||||
"@tanstack/react-query": "^5.99.0",
|
"@tanstack/react-query": "^5.100.14",
|
||||||
"axios": "^1.15.0",
|
"axios": "^1.15.0",
|
||||||
"babel-preset-expo": "^55.0.18",
|
"babel-preset-expo": "^55.0.18",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
"expo-image": "~55.0.8",
|
"expo-image": "~55.0.8",
|
||||||
"expo-linking": "~55.0.13",
|
"expo-linking": "~55.0.13",
|
||||||
"expo-router": "~55.0.12",
|
"expo-router": "~55.0.12",
|
||||||
|
"expo-screen-orientation": "~55.0.16",
|
||||||
"expo-splash-screen": "~55.0.18",
|
"expo-splash-screen": "~55.0.18",
|
||||||
"expo-status-bar": "~55.0.5",
|
"expo-status-bar": "~55.0.5",
|
||||||
"expo-symbols": "~55.0.7",
|
"expo-symbols": "~55.0.7",
|
||||||
@@ -56,6 +57,7 @@
|
|||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.2.0",
|
"react-dom": "19.2.0",
|
||||||
"react-native": "0.83.4",
|
"react-native": "0.83.4",
|
||||||
|
"react-native-blob-util": "^0.24.9",
|
||||||
"react-native-gesture-handler": "~2.30.0",
|
"react-native-gesture-handler": "~2.30.0",
|
||||||
"react-native-reanimated": "4.2.1",
|
"react-native-reanimated": "4.2.1",
|
||||||
"react-native-safe-area-context": "~5.6.2",
|
"react-native-safe-area-context": "~5.6.2",
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { Redirect, Tabs } from "expo-router";
|
import { Redirect, Tabs, useRouter } from "expo-router";
|
||||||
import {
|
import {
|
||||||
Boxes,
|
Boxes,
|
||||||
Container,
|
Container,
|
||||||
Home,
|
Home,
|
||||||
|
LogOut,
|
||||||
Logs,
|
Logs,
|
||||||
Rows4,
|
Rows4,
|
||||||
Settings,
|
Settings,
|
||||||
} from "lucide-react-native";
|
} from "lucide-react-native";
|
||||||
|
import { Alert } from "react-native";
|
||||||
import { useAppStore } from "../../hooks/useAppStore";
|
import { useAppStore } from "../../hooks/useAppStore";
|
||||||
import { useMobileAuthStore } from "../../hooks/useMobileAuth";
|
import { useMobileAuthStore } from "../../hooks/useMobileAuth";
|
||||||
|
|
||||||
@@ -20,6 +22,8 @@ export default function TabsLayout() {
|
|||||||
const serverPort = useAppStore((s) => s.serverPort);
|
const serverPort = useAppStore((s) => s.serverPort);
|
||||||
const user = useMobileAuthStore((s) => s.user);
|
const user = useMobileAuthStore((s) => s.user);
|
||||||
const isUnlocked = useMobileAuthStore((s) => s.isUnlocked);
|
const isUnlocked = useMobileAuthStore((s) => s.isUnlocked);
|
||||||
|
const logoutScanner = useMobileAuthStore((s) => s.logout);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const port = parseInt(serverPort || "0", 10) >= 50000;
|
const port = parseInt(serverPort || "0", 10) >= 50000;
|
||||||
|
|
||||||
@@ -36,6 +40,32 @@ export default function TabsLayout() {
|
|||||||
return role ? allowed.includes(role) : false;
|
return role ? allowed.includes(role) : false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logout = async () => {
|
||||||
|
try {
|
||||||
|
// optional confirm
|
||||||
|
Alert.alert("Logout", "Are you sure?", [
|
||||||
|
{ text: "Cancel", style: "cancel" },
|
||||||
|
{
|
||||||
|
text: "Logout",
|
||||||
|
style: "destructive",
|
||||||
|
onPress: async () => {
|
||||||
|
// clear auth/session
|
||||||
|
logoutScanner();
|
||||||
|
router.replace("/");
|
||||||
|
|
||||||
|
// clear zustand/session stuff
|
||||||
|
//useAuthStore.getState().reset();
|
||||||
|
|
||||||
|
// maybe clear async storage too
|
||||||
|
// await AsyncStorage.clear();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
@@ -62,10 +92,10 @@ export default function TabsLayout() {
|
|||||||
name="ppoo"
|
name="ppoo"
|
||||||
options={{
|
options={{
|
||||||
title: "PPOO",
|
title: "PPOO",
|
||||||
href:
|
// href:
|
||||||
isNormalScanner || !hasRole(["admin", "manager"])
|
// isNormalScanner || !hasRole(["admin", "manager"])
|
||||||
? null
|
// ? null
|
||||||
: "/(tabs)/ppoo",
|
// : "/(tabs)/ppoo",
|
||||||
tabBarIcon: ({ color, size }) => <Boxes size={size} color={color} />,
|
tabBarIcon: ({ color, size }) => <Boxes size={size} color={color} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -73,10 +103,10 @@ export default function TabsLayout() {
|
|||||||
name="laneCheck"
|
name="laneCheck"
|
||||||
options={{
|
options={{
|
||||||
title: "Lane Check",
|
title: "Lane Check",
|
||||||
href:
|
// href:
|
||||||
isNormalScanner || !hasRole(["admin", "manager"])
|
// isNormalScanner || !hasRole(["admin", "manager"])
|
||||||
? null
|
// ? null
|
||||||
: "/(tabs)/laneCheck",
|
// : "/(tabs)/laneCheck",
|
||||||
tabBarIcon: ({ color, size }) => <Rows4 size={size} color={color} />,
|
tabBarIcon: ({ color, size }) => <Rows4 size={size} color={color} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -112,6 +142,7 @@ export default function TabsLayout() {
|
|||||||
parseInt(serverPort || "0", 10) >= 50000 ? null : "/(tabs)/logs",
|
parseInt(serverPort || "0", 10) >= 50000 ? null : "/(tabs)/logs",
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
|
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="config"
|
name="config"
|
||||||
options={{
|
options={{
|
||||||
@@ -121,6 +152,22 @@ export default function TabsLayout() {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Tabs.Screen
|
||||||
|
name="logout"
|
||||||
|
options={{
|
||||||
|
title: "Logout",
|
||||||
|
tabBarIcon: ({ color, size }) => <LogOut color={color} size={size} />,
|
||||||
|
}}
|
||||||
|
listeners={{
|
||||||
|
tabPress: (e) => {
|
||||||
|
// stop navigation
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// run logout logic
|
||||||
|
logout();
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,134 @@
|
|||||||
import React from "react";
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||||
import { Text, View } from "react-native";
|
import * as Device from "expo-device";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Link } from "expo-router";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
ScrollView,
|
||||||
|
Text,
|
||||||
|
useWindowDimensions,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function LaneCheck() {
|
import { Card, CardContent } from "../../components/ui/card";
|
||||||
const getInfo = async () => {
|
import { getActiveLoadingOrders } from "../../lib/queryStuff/getActiveLoadingOrders";
|
||||||
const info = "ho";
|
import { getDocks } from "../../lib/queryStuff/getDocks";
|
||||||
|
|
||||||
console.log(info);
|
export default function DockScan() {
|
||||||
|
const { data } = useSuspenseQuery(getDocks());
|
||||||
|
const {
|
||||||
|
data: loadingOrders,
|
||||||
|
refetch,
|
||||||
|
isLoading,
|
||||||
|
} = useSuspenseQuery(getActiveLoadingOrders());
|
||||||
|
const { width } = useWindowDimensions();
|
||||||
|
const isTablet =
|
||||||
|
Device.modelName?.toLowerCase().includes("et40") ||
|
||||||
|
Device.modelName?.toLowerCase().includes("et45");
|
||||||
|
|
||||||
|
const columns = isTablet ? 3 : 1;
|
||||||
|
|
||||||
|
const gap = 8;
|
||||||
|
const cardWidth =
|
||||||
|
columns === 1 ? width - 16 : (width - gap * (columns + 1)) / columns;
|
||||||
|
|
||||||
|
const updateLoadingOrders = () => {
|
||||||
|
refetch();
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: `Refreshing Loading Orders`,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isLoading)
|
||||||
return (
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<View className="flex">
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
// flex: 1,
|
||||||
//justifyContent: "center",
|
//justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
marginTop: 50,
|
marginTop: 50,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text>Dock Scanning</Text>
|
<Text className="text-2xl text-bold">Dock Scanning</Text>
|
||||||
<Button onPress={getInfo}>
|
<Button title="Update Loading Orders" onPress={updateLoadingOrders} />
|
||||||
<Text>Check info</Text>
|
</View>
|
||||||
</Button>
|
<View>
|
||||||
|
<SafeAreaView className="flex">
|
||||||
|
<ScrollView className="w-full">
|
||||||
|
<View className="w-full flex-row flex-wrap gap-2 m-2">
|
||||||
|
{data.map((i: any) => {
|
||||||
|
const loadingPlan =
|
||||||
|
i.currentLoadingOrder !== ""
|
||||||
|
? loadingOrders.filter(
|
||||||
|
(x: any) => x.id === Number(i.currentLoadingOrder),
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={i.id}>
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: "/dock/[id]",
|
||||||
|
params: {
|
||||||
|
id: i.dockId.toString(),
|
||||||
|
currentLoading: i.currentLoadingOrder,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
borderWidth: 4,
|
||||||
|
width: cardWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Text>{i.name}</Text>
|
||||||
|
{i.currentLoadingOrder === "" ? (
|
||||||
|
<Text>Tap to active new loading order</Text>
|
||||||
|
) : (
|
||||||
|
<View>
|
||||||
|
<Text>
|
||||||
|
Current Loading order : {i.currentLoadingOrder}
|
||||||
|
</Text>
|
||||||
|
{loadingPlan && loadingPlan.length > 0 && (
|
||||||
|
<View>
|
||||||
|
<Text>
|
||||||
|
{`${loadingPlan[0].loadingPlanItems[0].articleId} - ${loadingPlan[0].loadingPlanItems[0].articleDescription}`}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Current Loaded :{" "}
|
||||||
|
{
|
||||||
|
loadingPlan[0].loadingPlanItems[0]
|
||||||
|
.loadedQuantityLUs
|
||||||
|
}{" "}
|
||||||
|
/{" "}
|
||||||
|
{
|
||||||
|
loadingPlan[0].loadingPlanItems[0]
|
||||||
|
.plannedQuantityLUs
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Link>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
3
lstMobile/src/app/(tabs)/logout.tsx
Normal file
3
lstMobile/src/app/(tabs)/logout.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function LogoutScreen() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,154 +1,81 @@
|
|||||||
import axios from "axios";
|
|
||||||
import { format } from "date-fns-tz";
|
import { format } from "date-fns-tz";
|
||||||
|
import * as Device from "expo-device";
|
||||||
import { useFocusEffect } from "expo-router";
|
import { useFocusEffect } from "expo-router";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { ScrollView, Text, View } from "react-native";
|
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
import { GlobalFooter } from "../../components/UpdateFooter";
|
|
||||||
import { Button } from "../../components/ui/button";
|
|
||||||
import { Card, CardContent, CardHeader } from "../../components/ui/card";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Button,
|
||||||
DialogContent,
|
ScrollView,
|
||||||
DialogDescription,
|
Text,
|
||||||
DialogHeader,
|
useWindowDimensions,
|
||||||
DialogTitle,
|
View,
|
||||||
DialogTrigger,
|
} from "react-native";
|
||||||
} from "../../components/ui/dialog";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
import { useAppStore } from "../../hooks/useAppStore";
|
import { Card, CardContent } from "../../components/ui/card";
|
||||||
import { scannerFeedback } from "../../lib/feedbackScan";
|
import { useSocketRoom } from "../../hooks/socket.io.hook";
|
||||||
import { type ZebraScanResult, zebraScanner } from "../../lib/ZebraScanner";
|
|
||||||
|
|
||||||
const InfoRow = ({
|
type PPOO = {
|
||||||
label,
|
type: string;
|
||||||
value,
|
items: any;
|
||||||
}: {
|
createdAt: Date;
|
||||||
label: string;
|
|
||||||
value: React.ReactNode;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<View className="flex-row justify-between gap-4 py-2 border-b border-gray-200">
|
|
||||||
<Text className="text-sm text-gray-500">{label}</Text>
|
|
||||||
<Text className="text-sm font-medium text-gray-900 text-right flex-1">
|
|
||||||
{value}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PPOO() {
|
export default function PPOO() {
|
||||||
const [units, setUnits] = useState<any>(null);
|
const { data } = useSocketRoom<any>("ppoo", undefined, "replace") as any;
|
||||||
const serverIp = useAppStore((s) => s.serverIp);
|
const [sortDir, setSortDir] = useState<"asc" | "desc">("desc");
|
||||||
|
|
||||||
const handleScan = useCallback(
|
const { width } = useWindowDimensions();
|
||||||
async (scan: ZebraScanResult) => {
|
const isTablet =
|
||||||
setUnits(null);
|
Device.modelName?.toLowerCase().includes("et40") ||
|
||||||
await scannerFeedback({
|
Device.modelName?.toLowerCase().includes("et45");
|
||||||
type: "scan",
|
|
||||||
sound: true,
|
|
||||||
vibrate: true,
|
|
||||||
led: true,
|
|
||||||
});
|
|
||||||
if (!scan.data.startsWith("loc")) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Scan error",
|
|
||||||
text2: "The last scan was not a lane please try again",
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
const columns = isTablet ? 3 : 1;
|
||||||
}
|
|
||||||
try {
|
|
||||||
const res = await axios.post(
|
|
||||||
`http://${serverIp.trim()}:3000/lst/api/mobile/lanecheck`,
|
|
||||||
{
|
|
||||||
lane: "loc#1#0<",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timeout: 5000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (res.status === 200) {
|
|
||||||
setUnits(res.data);
|
|
||||||
Toast.show({
|
|
||||||
type: "info",
|
|
||||||
text1: "Lane Data",
|
|
||||||
text2: "All Loading Units from this lane will be listed below",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Lane Data",
|
|
||||||
text2: "Error getting lane data please try again",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[serverIp.trim],
|
|
||||||
);
|
|
||||||
|
|
||||||
useFocusEffect(
|
const gap = 8;
|
||||||
useCallback(() => {
|
const cardWidth =
|
||||||
zebraScanner.startListening();
|
columns === 1 ? width - 16 : (width - gap * (columns + 1)) / columns;
|
||||||
|
|
||||||
const sub = zebraScanner.addScanListener((scan) => {
|
const items = data?.items ?? [];
|
||||||
//console.log("SCAN:", scan);
|
const sortedItems = useMemo(() => {
|
||||||
handleScan(scan);
|
return [...items].sort((a, b) => {
|
||||||
|
const aDate = new Date(a.lastMovingDate).getTime();
|
||||||
|
const bDate = new Date(b.lastMovingDate).getTime();
|
||||||
|
|
||||||
|
return sortDir === "asc" ? aDate - bDate : bDate - aDate;
|
||||||
});
|
});
|
||||||
|
}, [items, sortDir]);
|
||||||
return () => {
|
|
||||||
sub.remove();
|
|
||||||
zebraScanner.stopListening();
|
|
||||||
//setUnits(null);
|
|
||||||
};
|
|
||||||
}, [handleScan]),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View className="flex items-center mt-2">
|
||||||
style={{
|
<View className="flex m-2">
|
||||||
//justifyContent: "center",
|
<Button
|
||||||
alignItems: "center",
|
onPress={() =>
|
||||||
marginTop: 50,
|
setSortDir((prev) => (prev === "asc" ? "desc" : "asc"))
|
||||||
}}
|
}
|
||||||
>
|
title={`Sort: ${sortDir}`}
|
||||||
{units ? (
|
/>
|
||||||
// <SafeAreaView className={`flex-1 w-full items-center`}>
|
|
||||||
// <ScrollView className="w-full flex-1">
|
|
||||||
// <View className="flex items-center gap-2 w-full">
|
|
||||||
// {units.data?.map((i: any, index: any) => (
|
|
||||||
// <View key={`${i.runningNumber}-${index}`}>
|
|
||||||
// <Text>example</Text>
|
|
||||||
// </View>
|
|
||||||
// ))}
|
|
||||||
// </View>
|
|
||||||
// </ScrollView>
|
|
||||||
// </SafeAreaView>
|
|
||||||
<SafeAreaView className={`w-full items-center`}>
|
|
||||||
<View style={{ padding: 2 }}>
|
|
||||||
<Text>There Are {units.data.length} units in PPOO</Text>
|
|
||||||
</View>
|
</View>
|
||||||
<ScrollView className="w-full" style={{ marginBottom: 20 }}>
|
{sortedItems.length === 0 ? (
|
||||||
<View>
|
<View className="flex items-center">
|
||||||
{units.data.map((i, index) => (
|
<Text>Loading PPOO...</Text>
|
||||||
<View
|
</View>
|
||||||
key={`${i.runningNumber}-${index}`}
|
) : (
|
||||||
style={{
|
<SafeAreaView className="flex">
|
||||||
justifyContent: "center",
|
<ScrollView className="w-full">
|
||||||
margin: 2,
|
<View className="w-full flex-row flex-wrap gap-2 m-2">
|
||||||
}}
|
{sortedItems.map((i: any) => {
|
||||||
>
|
return (
|
||||||
<Dialog>
|
<View key={i.runningNumber}>
|
||||||
<DialogTrigger>
|
|
||||||
<Card
|
<Card
|
||||||
className="w-full"
|
//className={isTablet ? "w-[32%]" : "w-full"}
|
||||||
style={{
|
style={{
|
||||||
borderColor:
|
borderColor:
|
||||||
i.state === "QualityBlocked" ? "red" : undefined,
|
i.mainDefectId === 864
|
||||||
|
? "blue"
|
||||||
|
: i.state === "QualityBlocked"
|
||||||
|
? "red"
|
||||||
|
: undefined,
|
||||||
borderWidth: 4,
|
borderWidth: 4,
|
||||||
|
width: cardWidth,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -159,52 +86,18 @@ export default function PPOO() {
|
|||||||
<Text>
|
<Text>
|
||||||
Running Number: {i.runningNumber ?? "Non barcoded"}
|
Running Number: {i.runningNumber ?? "Non barcoded"}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Date: {format(i.lastMovingDate, "M/d/yyyy HH:mm")}
|
||||||
|
</Text>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>
|
|
||||||
Details for Article {i.articleId}, Rn:
|
|
||||||
{i.runningNumber ?? "Non barcoded"}{" "}
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
<InfoRow
|
|
||||||
label="Production Date"
|
|
||||||
value={format(i.productionDate, "MM/dd/yyyy HH:mm")}
|
|
||||||
/>
|
|
||||||
<InfoRow label="Quantity" value={i.quantity} />
|
|
||||||
{i.state === "QualityBlocked" && (
|
|
||||||
<InfoRow
|
|
||||||
label="Defect"
|
|
||||||
value={i.mainDefectGroupDescription}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{i.state === "QualityBlocked" && (
|
|
||||||
<InfoRow
|
|
||||||
label="Description"
|
|
||||||
value={i.mainDefectDescription}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</View>
|
</View>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
) : (
|
|
||||||
<View className="mt-50">
|
|
||||||
<Text className="text-2xl text-center">
|
|
||||||
Please scan a lane to see all Units that are in the lane.
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
)}
|
||||||
<View>
|
|
||||||
<GlobalFooter />
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,27 @@ import { PortalHost } from "@rn-primitives/portal";
|
|||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
import { StatusBar } from "expo-status-bar";
|
import { StatusBar } from "expo-status-bar";
|
||||||
import "../../global.css";
|
import "../../global.css";
|
||||||
|
import { QueryClientProvider } from "@tanstack/react-query";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
import useDeviceLock from "../hooks/useDeviceCheck";
|
import useDeviceLock from "../hooks/useDeviceCheck";
|
||||||
|
import { useDeviceOrientationLock } from "../hooks/useDeviceOrientationLock";
|
||||||
|
import { queryClient } from "../lib/queryStuff/queryClient";
|
||||||
|
import { connectSocket } from "../lib/socket.io";
|
||||||
import { zebraScanner } from "../lib/ZebraScanner";
|
import { zebraScanner } from "../lib/ZebraScanner";
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
useDeviceLock();
|
useDeviceLock();
|
||||||
|
useDeviceOrientationLock();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
zebraScanner.ensureProfile();
|
zebraScanner.ensureProfile();
|
||||||
|
connectSocket();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
<StatusBar style="dark" />
|
<StatusBar style="dark" />
|
||||||
<Stack screenOptions={{ headerShown: false }}>
|
<Stack screenOptions={{ headerShown: false }}>
|
||||||
<Stack.Screen name="index" />
|
<Stack.Screen name="index" />
|
||||||
@@ -24,6 +32,8 @@ export default function RootLayout() {
|
|||||||
<Stack.Screen name="(tabs)" />
|
<Stack.Screen name="(tabs)" />
|
||||||
</Stack>
|
</Stack>
|
||||||
<PortalHost />
|
<PortalHost />
|
||||||
|
</QueryClientProvider>
|
||||||
|
|
||||||
<Toast />
|
<Toast />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
169
lstMobile/src/app/dock/[id].tsx
Normal file
169
lstMobile/src/app/dock/[id].tsx
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||||
|
import * as Device from "expo-device";
|
||||||
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Pressable,
|
||||||
|
ScrollView,
|
||||||
|
Text,
|
||||||
|
useWindowDimensions,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
import { Card, CardContent } from "../../components/ui/card";
|
||||||
|
import { api } from "../../lib/apiHelper";
|
||||||
|
import { getActiveLoadingOrders } from "../../lib/queryStuff/getActiveLoadingOrders";
|
||||||
|
|
||||||
|
export default function DockPage() {
|
||||||
|
const { id, currentLoading } = useLocalSearchParams<{
|
||||||
|
id: string;
|
||||||
|
currentLoading: string;
|
||||||
|
}>();
|
||||||
|
const router = useRouter();
|
||||||
|
const [active] = useState(currentLoading !== "");
|
||||||
|
|
||||||
|
const { width } = useWindowDimensions();
|
||||||
|
const isTablet =
|
||||||
|
Device.modelName?.toLowerCase().includes("et40") ||
|
||||||
|
Device.modelName?.toLowerCase().includes("et45");
|
||||||
|
|
||||||
|
const columns = isTablet ? 3 : 1;
|
||||||
|
|
||||||
|
const gap = 8;
|
||||||
|
const cardWidth =
|
||||||
|
columns === 1 ? width - 16 : (width - gap * (columns + 1)) / columns;
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: loadingOrders,
|
||||||
|
refetch,
|
||||||
|
isLoading,
|
||||||
|
} = useSuspenseQuery(getActiveLoadingOrders());
|
||||||
|
|
||||||
|
const dockFilter = loadingOrders.filter((i: any) => i.dockId === Number(id));
|
||||||
|
|
||||||
|
// add in start loading order, if this is already on the dock we will disabled and change to view current pallets
|
||||||
|
const startLoad = async (loadingOrder: string, dockId: string) => {
|
||||||
|
try {
|
||||||
|
const res = await api.post("/dockDoor/startLoad", {
|
||||||
|
loadingOrder,
|
||||||
|
dockId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
Toast.show({ type: "success", text1: res.data.message });
|
||||||
|
refetch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: JSON.stringify(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const endLoad = async (loadingOrder: string, dockId: string) => {
|
||||||
|
try {
|
||||||
|
const res = await api.post("/dockDoor/endLoad", {
|
||||||
|
loadingOrder,
|
||||||
|
dockId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
Toast.show({ type: "success", text1: res.data.message });
|
||||||
|
refetch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: JSON.stringify(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// add in ending loading order disabeled until all pallets are loaded.
|
||||||
|
if (isLoading)
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Text>Loading</Text>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<View className="flex flex-row justify-between gap-1 ml-1 mr-1">
|
||||||
|
<View>
|
||||||
|
<Pressable
|
||||||
|
onPress={() => router.back()}
|
||||||
|
className="self-start rounded-xl bg-gray-200 px-4 py-2"
|
||||||
|
>
|
||||||
|
<Text className="font-semibold">← Back</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text className="text-xl mt-1">{dockFilter[0].dockDescription}</Text>
|
||||||
|
<View>
|
||||||
|
<Pressable
|
||||||
|
onPress={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/dock/scans/[scanner]",
|
||||||
|
params: {
|
||||||
|
scanner: id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="self-start rounded-xl bg-gray-200 px-4 py-2"
|
||||||
|
>
|
||||||
|
<Text className="font-semibold">Scans</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<ScrollView>
|
||||||
|
<View className="w-full flex-row flex-wrap gap-2 m-2">
|
||||||
|
{dockFilter.map((i: any) => {
|
||||||
|
return (
|
||||||
|
<View key={i.id}>
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
borderWidth: 4,
|
||||||
|
width: cardWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<View>
|
||||||
|
<Text>Loading Order: {dockFilter[0].id}</Text>
|
||||||
|
<Text>
|
||||||
|
{`${dockFilter[0].loadingPlanItems[0].articleId} - ${dockFilter[0].loadingPlanItems[0].articleDescription}`}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Current Loaded :{" "}
|
||||||
|
{dockFilter[0].loadingPlanItems[0].loadedQuantityLUs} /{" "}
|
||||||
|
{dockFilter[0].loadingPlanItems[0].plannedQuantityLUs}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View className="mt-2 flex flex-row gap-2 justify-between">
|
||||||
|
<Button
|
||||||
|
title="Start Load"
|
||||||
|
onPress={() =>
|
||||||
|
startLoad(dockFilter[0].id.toString(), id)
|
||||||
|
}
|
||||||
|
disabled={active}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="End Load"
|
||||||
|
onPress={() => endLoad(dockFilter[0].id.toString(), id)}
|
||||||
|
disabled={active}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
71
lstMobile/src/app/dock/scans/[scanner].tsx
Normal file
71
lstMobile/src/app/dock/scans/[scanner].tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||||
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
||||||
|
import { Pressable, Text, View } from "react-native";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
import { Card } from "../../../components/ui/card";
|
||||||
|
import { useSocketRoom } from "../../../hooks/socket.io.hook";
|
||||||
|
import { getActiveLoadingOrders } from "../../../lib/queryStuff/getActiveLoadingOrders";
|
||||||
|
|
||||||
|
export default function DockPage() {
|
||||||
|
const { scanner } = useLocalSearchParams<{
|
||||||
|
scanner: string;
|
||||||
|
}>();
|
||||||
|
const { data: loadingOrders, isLoading } = useSuspenseQuery(
|
||||||
|
getActiveLoadingOrders(),
|
||||||
|
);
|
||||||
|
const { data } = useSocketRoom<any>(
|
||||||
|
`dockDoorLoading:${scanner}`,
|
||||||
|
undefined,
|
||||||
|
"append",
|
||||||
|
) as any;
|
||||||
|
const dockFilter = loadingOrders.filter(
|
||||||
|
(i: any) => i.dockId === Number(scanner),
|
||||||
|
);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
if (isLoading)
|
||||||
|
return (
|
||||||
|
<SafeAreaView>
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<SafeAreaView className="w-full">
|
||||||
|
<View className="flex flex-row justify-between gap-1 ml-1 mr-1">
|
||||||
|
<View>
|
||||||
|
<Pressable
|
||||||
|
onPress={() => router.back()}
|
||||||
|
className="self-start rounded-xl bg-gray-200 px-4 py-2"
|
||||||
|
>
|
||||||
|
<Text className="font-semibold">← Back</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text className="text-xl mt-1">{dockFilter[0].dockDescription}</Text>
|
||||||
|
<View>
|
||||||
|
<Pressable
|
||||||
|
onPress={() =>
|
||||||
|
router.replace({
|
||||||
|
pathname: "/(tabs)/dockScan",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="self-start rounded-xl bg-gray-200 px-4 py-2"
|
||||||
|
>
|
||||||
|
<Text className="font-semibold">Docks</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
{data.map((i: any, index: any) => {
|
||||||
|
return (
|
||||||
|
<View key={index} className="m-2">
|
||||||
|
<Card>
|
||||||
|
<Text>{JSON.stringify(i)}</Text>
|
||||||
|
</Card>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
|
import { Settings } from "lucide-react-native";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Alert, Button, Text, View } from "react-native";
|
import { Alert, Button, Text, View } from "react-native";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
import { ConfigButton } from "../components/ui/configButton";
|
||||||
import { Input } from "../components/ui/input";
|
import { Input } from "../components/ui/input";
|
||||||
import { useAppStore } from "../hooks/useAppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import { useMobileAuthStore } from "../hooks/useMobileAuth";
|
import { useMobileAuthStore } from "../hooks/useMobileAuth";
|
||||||
@@ -52,11 +54,6 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = () => {
|
|
||||||
console.log("config");
|
|
||||||
return router.replace("/setup");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -67,9 +64,17 @@ export default function Login() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View className="flex items-center m-5">
|
<View className="flex items-center m-5">
|
||||||
<Text style={{ fontSize: 20, fontWeight: "600" }}>
|
<View className="flex flex-row">
|
||||||
|
<View>
|
||||||
|
<Text style={{ fontSize: 20, fontWeight: "600" }} className="mt-2">
|
||||||
LST Scanner Login
|
LST Scanner Login
|
||||||
</Text>
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<ConfigButton />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
<View className="w-64 p-4">
|
<View className="w-64 p-4">
|
||||||
<Input
|
<Input
|
||||||
className="w-fit"
|
className="w-fit"
|
||||||
@@ -89,7 +94,6 @@ export default function Login() {
|
|||||||
</View>
|
</View>
|
||||||
<View className="flex gap-2 flex-row">
|
<View className="flex gap-2 flex-row">
|
||||||
<Button title="Login" onPress={onLogin} />
|
<Button title="Login" onPress={onLogin} />
|
||||||
<Button title="Config" onPress={config} />
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -99,9 +99,16 @@ export default function Setup() {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
padding: 3,
|
padding: 3,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
gap: 3,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button title="Submit" onPress={authCheck} />
|
<Button title="Submit" onPress={authCheck} />
|
||||||
|
<Button
|
||||||
|
title="Home"
|
||||||
|
onPress={() => {
|
||||||
|
router.push("/(tabs)/scanner");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
@@ -145,14 +152,7 @@ export default function Setup() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<View
|
<View className="flex gap-2 flex-row">
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "center",
|
|
||||||
padding: 3,
|
|
||||||
gap: 3,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button title="Save Config" onPress={handleSave} />
|
<Button title="Save Config" onPress={handleSave} />
|
||||||
<Button
|
<Button
|
||||||
title="Home"
|
title="Home"
|
||||||
|
|||||||
@@ -182,14 +182,17 @@ export default function LSTScanner() {
|
|||||||
}, [handleScan]),
|
}, [handleScan]),
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<View className={`${bgColor ?? ""} flex-1 w-screen`}>
|
<View className={`${bgColor ?? ""} flex-1 w-full`}>
|
||||||
<View style={{ alignItems: "center", margin: 5 }}>
|
<View className="flex gap-2 items-center">
|
||||||
|
<View className="flex flex-col gap-2 items-center">
|
||||||
<Text style={{ fontSize: 14, fontWeight: "600" }}>
|
<Text style={{ fontSize: 14, fontWeight: "600" }}>
|
||||||
User: {formatName(user?.name ?? "")}
|
Lst user: {formatName(user?.name ?? "")}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{ fontSize: 18, fontWeight: "600" }}>
|
{/* <Text style={{ fontSize: 14, fontWeight: "600" }}>
|
||||||
LST Scanner id: {user?.scannerId}
|
LST Scanner id: {user?.scannerId}
|
||||||
</Text>
|
</Text> */}
|
||||||
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
@@ -197,8 +200,8 @@ export default function LSTScanner() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!lastScan ? (
|
{!lastScan ? (
|
||||||
<View style={{ marginTop: 10, alignItems: "center" }}>
|
<View style={{ marginTop: 2, alignItems: "center" }}>
|
||||||
<Text className="text-xl font-bold">Ready to scan</Text>
|
<Text className="text-lg font-bold">Ready to scan</Text>
|
||||||
<Text>Please Scan a command to start scanning...</Text>
|
<Text>Please Scan a command to start scanning...</Text>
|
||||||
<Text className="text-sm">
|
<Text className="text-sm">
|
||||||
Scanning a label could cause errors due to incorrect previous
|
Scanning a label could cause errors due to incorrect previous
|
||||||
@@ -208,7 +211,7 @@ export default function LSTScanner() {
|
|||||||
) : (
|
) : (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
marginTop: 10,
|
marginTop: 2,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -217,10 +220,10 @@ export default function LSTScanner() {
|
|||||||
.map((i) => {
|
.map((i) => {
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{ marginTop: 10, alignItems: "center" }}
|
style={{ marginTop: 2, alignItems: "center" }}
|
||||||
key={i}
|
key={i}
|
||||||
>
|
>
|
||||||
<Text style={{ fontSize: 18, fontWeight: "600" }}>
|
<Text style={{ fontSize: 12, fontWeight: "600" }}>
|
||||||
{i}
|
{i}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -237,18 +240,17 @@ export default function LSTScanner() {
|
|||||||
color={bgColor}
|
color={bgColor}
|
||||||
clearScan={clearScans}
|
clearScan={clearScans}
|
||||||
/>
|
/>
|
||||||
|
<GlobalFooter />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className="m-2">
|
{/* <View className="m-2">
|
||||||
{user && (
|
{user && (
|
||||||
<View className="items-center">
|
<View className="items-center">
|
||||||
<Button title="Logout" onPress={logoutScanner} />
|
<Button title="Logout" onPress={logoutScanner} />
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View> */}
|
||||||
<View>
|
{/* <View style={{ maxHeight: 75 }} className="flex-1 bg-slate-500"></View> */}
|
||||||
<GlobalFooter />
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,28 +13,30 @@ export function GlobalFooter() {
|
|||||||
if (serverVersion && serverVersion?.versionCode <= build) return;
|
if (serverVersion && serverVersion?.versionCode <= build) return;
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<View>
|
{(hasUpdate || shouldUpdate) && (
|
||||||
|
<View className="bg-slate-500">
|
||||||
{hasUpdate && (
|
{hasUpdate && (
|
||||||
<View className="items-center h-[75px] bg-[#EB091A]">
|
<View className="items-center h-[75px] bg-[#EB091A] justify-center">
|
||||||
<Link href={"/updateScreen"}>
|
<Link href="/updateScreen">
|
||||||
<Text className="h-[75px] font-medium text-base text-wrap text-center">
|
<Text className="font-medium text-base text-center">
|
||||||
Critical updates pending, once you are completed with your task
|
Critical updates pending, once you are completed with your
|
||||||
please click me for instructions to update
|
task please click me for instructions to update
|
||||||
</Text>
|
</Text>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!hasUpdate && shouldUpdate && (
|
{!hasUpdate && shouldUpdate && (
|
||||||
<View className="bg-[#FDBA74]">
|
<View className="bg-[#FDBA74] py-2 items-center">
|
||||||
<Link href={"/updateScreen"}>
|
<Link href="/updateScreen">
|
||||||
<Text className="h-[16] font-medium text-base text-wrap text-center">
|
<Text className="font-medium text-base text-center">
|
||||||
There is an update click me for instructions
|
There is an update click me for instructions
|
||||||
</Text>
|
</Text>
|
||||||
</Link>
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
20
lstMobile/src/components/ui/configButton.tsx
Normal file
20
lstMobile/src/components/ui/configButton.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useRouter } from "expo-router";
|
||||||
|
import { Settings } from "lucide-react-native";
|
||||||
|
import { Pressable } from "react-native";
|
||||||
|
|
||||||
|
export function ConfigButton() {
|
||||||
|
const router = useRouter();
|
||||||
|
const config = () => {
|
||||||
|
console.log("config");
|
||||||
|
return router.replace("/setup");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
onPress={config}
|
||||||
|
className="h-12 w-12 items-center justify-center rounded-x"
|
||||||
|
>
|
||||||
|
<Settings color="black" size={24} />
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
}
|
||||||
136
lstMobile/src/hooks/socket.io.hook.ts
Normal file
136
lstMobile/src/hooks/socket.io.hook.ts
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import { useFocusEffect } from "expo-router";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { getSocket } from "../lib/socket.io";
|
||||||
|
|
||||||
|
type RoomUpdatePayload<T> = {
|
||||||
|
roomId: string;
|
||||||
|
payloads: T[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type RoomErrorPayload = {
|
||||||
|
roomId?: string;
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UpdateMode = "append" | "replace";
|
||||||
|
|
||||||
|
export function useSocketRoom<T>(
|
||||||
|
roomId: string,
|
||||||
|
getKey?: (item: T) => string | number,
|
||||||
|
updateMode: UpdateMode = "append",
|
||||||
|
) {
|
||||||
|
const [data, setData] = useState<T[]>([]);
|
||||||
|
const [info, setInfo] = useState(
|
||||||
|
"No data yet — join the room to start receiving",
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearRoom = useCallback(
|
||||||
|
(id?: string | number) => {
|
||||||
|
if (id !== undefined && getKey) {
|
||||||
|
setData((prev) => prev.filter((item) => getKey(item) !== id));
|
||||||
|
setInfo(`Removed item ${id}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setData([]);
|
||||||
|
setInfo("Room data cleared");
|
||||||
|
},
|
||||||
|
[getKey],
|
||||||
|
);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
const socket = getSocket();
|
||||||
|
function handleConnect() {
|
||||||
|
socket.emit("join-room", roomId);
|
||||||
|
setInfo(`Joined room: ${roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdate(payload: RoomUpdatePayload<T>) {
|
||||||
|
// protects against other room updates hitting this hook
|
||||||
|
if (payload.roomId !== roomId) return;
|
||||||
|
|
||||||
|
// resetting room data for rooms that just need updated data.
|
||||||
|
if (updateMode === "replace") {
|
||||||
|
setData(payload.payloads);
|
||||||
|
} else {
|
||||||
|
setData((prev) => [...payload.payloads, ...prev]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInfo("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(err: RoomErrorPayload) {
|
||||||
|
if (err.roomId && err.roomId !== roomId) return;
|
||||||
|
setInfo(err.message ?? "Room error");
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on("connect", handleConnect);
|
||||||
|
socket.on("room-update", handleUpdate);
|
||||||
|
socket.on("room-error", handleError);
|
||||||
|
|
||||||
|
if (!socket.connected && socket.disconnected) {
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If already connected, join immediately
|
||||||
|
if (socket.connected) {
|
||||||
|
socket.emit("join-room", roomId);
|
||||||
|
setInfo(`Joined room: ${roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.emit("leave-room", roomId);
|
||||||
|
|
||||||
|
socket.off("connect", handleConnect);
|
||||||
|
socket.off("room-update", handleUpdate);
|
||||||
|
socket.off("room-error", handleError);
|
||||||
|
};
|
||||||
|
}, [roomId, updateMode]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// const socket = getSocket();
|
||||||
|
// function handleConnect() {
|
||||||
|
// socket.emit("join-room", roomId);
|
||||||
|
// setInfo(`Joined room: ${roomId}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function handleUpdate(payload: RoomUpdatePayload<T>) {
|
||||||
|
// // protects against other room updates hitting this hook
|
||||||
|
// if (payload.roomId !== roomId) return;
|
||||||
|
|
||||||
|
// setData((prev) => [...payload.payloads, ...prev]);
|
||||||
|
// setInfo("");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function handleError(err: RoomErrorPayload) {
|
||||||
|
// if (err.roomId && err.roomId !== roomId) return;
|
||||||
|
// setInfo(err.message ?? "Room error");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!socket.connected && socket.disconnected) {
|
||||||
|
// socket.connect();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // If already connected, join immediately
|
||||||
|
// if (socket.connected) {
|
||||||
|
// socket.emit("join-room", roomId);
|
||||||
|
// setInfo(`Joined room: ${roomId}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// socket.on("connect", handleConnect);
|
||||||
|
// socket.on("room-update", handleUpdate);
|
||||||
|
// socket.on("room-error", handleError);
|
||||||
|
|
||||||
|
// return () => {
|
||||||
|
// socket.emit("leave-room", roomId);
|
||||||
|
// console.log("leaving Room");
|
||||||
|
// socket.off("connect", handleConnect);
|
||||||
|
// socket.off("room-update", handleUpdate);
|
||||||
|
// socket.off("room-error", handleError);
|
||||||
|
// };
|
||||||
|
// }, [roomId]);
|
||||||
|
|
||||||
|
return { data, info, clearRoom };
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Constants from "expo-constants";
|
import Constants from "expo-constants";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { setApiConfig } from "../lib/apiHelper";
|
||||||
import { devDelay } from "../lib/devMode";
|
import { devDelay } from "../lib/devMode";
|
||||||
import { versionCheck } from "../lib/versionValidation";
|
import { versionCheck } from "../lib/versionValidation";
|
||||||
import { useAppStore } from "./useAppStore";
|
import { useAppStore } from "./useAppStore";
|
||||||
@@ -26,6 +27,11 @@ export function useAppStartup() {
|
|||||||
const serverPort = useAppStore((s) => s.serverPort);
|
const serverPort = useAppStore((s) => s.serverPort);
|
||||||
const serverIp = useAppStore((s) => s.serverIp);
|
const serverIp = useAppStore((s) => s.serverIp);
|
||||||
|
|
||||||
|
setApiConfig({
|
||||||
|
serverIp,
|
||||||
|
serverPort,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!hasHydrated) {
|
if (!hasHydrated) {
|
||||||
setStatus("loading");
|
setStatus("loading");
|
||||||
|
|||||||
32
lstMobile/src/hooks/useDeviceOrientationLock.ts
Normal file
32
lstMobile/src/hooks/useDeviceOrientationLock.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import * as Device from "expo-device";
|
||||||
|
import * as ScreenOrientation from "expo-screen-orientation";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
const LANDSCAPE_MODELS = ["ET45", "ET40"]; // tablets
|
||||||
|
const PORTRAIT_MODELS = ["TC21", "TC26", "TC8300"]; // scanners
|
||||||
|
|
||||||
|
const isTabletModel = (modelName?: string | null) => {
|
||||||
|
const model = modelName?.toUpperCase() ?? "";
|
||||||
|
|
||||||
|
return LANDSCAPE_MODELS.some((m) => model.includes(m));
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useDeviceOrientationLock() {
|
||||||
|
useEffect(() => {
|
||||||
|
async function lockOrientation() {
|
||||||
|
try {
|
||||||
|
const model = Device.modelName;
|
||||||
|
|
||||||
|
await ScreenOrientation.lockAsync(
|
||||||
|
isTabletModel(model)
|
||||||
|
? ScreenOrientation.OrientationLock.LANDSCAPE
|
||||||
|
: ScreenOrientation.OrientationLock.PORTRAIT_UP,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to lock orientation", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lockOrientation();
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
63
lstMobile/src/lib/apiHelper.ts
Normal file
63
lstMobile/src/lib/apiHelper.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
type ApiConfig = {
|
||||||
|
serverIp: string;
|
||||||
|
serverPort: string | number;
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentConfig: ApiConfig | null = null;
|
||||||
|
|
||||||
|
export function setApiConfig(config: ApiConfig) {
|
||||||
|
currentConfig = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBaseUrl() {
|
||||||
|
if (!currentConfig) {
|
||||||
|
throw new Error("API config not initialized");
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
`http://${currentConfig.serverIp}:${currentConfig.serverPort}/lst/api`,
|
||||||
|
);
|
||||||
|
return `http://${currentConfig.serverIp}:${currentConfig.serverPort}/lst/api`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const api = axios.create({
|
||||||
|
timeout: 15000,
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.request.use((config) => {
|
||||||
|
config.baseURL = getBaseUrl();
|
||||||
|
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
const isNetworkError =
|
||||||
|
error.code === "ERR_NETWORK" ||
|
||||||
|
error.code === "ECONNABORTED" ||
|
||||||
|
error.message === "Network Error" ||
|
||||||
|
error.message === "Failed to fetch" ||
|
||||||
|
!error.response;
|
||||||
|
|
||||||
|
// unauthorized
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
router.replace("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
// forbidden
|
||||||
|
if (error.response?.status === 403) {
|
||||||
|
router.replace("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// app/server offline
|
||||||
|
if (isNetworkError) {
|
||||||
|
router.replace("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(error);
|
||||||
|
return Promise.reject(error);
|
||||||
|
},
|
||||||
|
);
|
||||||
50
lstMobile/src/lib/lastestVersion.ts
Normal file
50
lstMobile/src/lib/lastestVersion.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import ReactNativeBlobUtil from "react-native-blob-util";
|
||||||
|
|
||||||
|
export async function downloadLatestApk(serverIp: string, port: string) {
|
||||||
|
const url = `http://${serverIp}:${port}/lst/api/mobile/apk/latest`;
|
||||||
|
|
||||||
|
const apkPath = `${ReactNativeBlobUtil.fs.dirs.DownloadDir}/lst-mobile.apk`;
|
||||||
|
|
||||||
|
// delete old apk if it exists
|
||||||
|
const exists = await ReactNativeBlobUtil.fs.exists(apkPath);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
const stat = await ReactNativeBlobUtil.fs.stat(apkPath);
|
||||||
|
|
||||||
|
// last modified time
|
||||||
|
const lastModified = Number(stat.lastModified);
|
||||||
|
|
||||||
|
// current time
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// 5 minutes in ms
|
||||||
|
const fiveMinutes = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
// skip download if file is fresh
|
||||||
|
if (now - lastModified < fiveMinutes) {
|
||||||
|
console.log("APK already downloaded recently, skipping.");
|
||||||
|
|
||||||
|
return {
|
||||||
|
skipped: true,
|
||||||
|
path: apkPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete old apk before redownload
|
||||||
|
await ReactNativeBlobUtil.fs.unlink(apkPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await ReactNativeBlobUtil.config({
|
||||||
|
addAndroidDownloads: {
|
||||||
|
useDownloadManager: true,
|
||||||
|
notification: true,
|
||||||
|
title: "LST Mobile Update",
|
||||||
|
description: "Downloading update for StageNow install",
|
||||||
|
mime: "application/vnd.android.package-archive",
|
||||||
|
path: apkPath,
|
||||||
|
mediaScannable: true,
|
||||||
|
},
|
||||||
|
}).fetch("GET", url);
|
||||||
|
|
||||||
|
return res.path();
|
||||||
|
}
|
||||||
21
lstMobile/src/lib/queryStuff/getActiveLoadingOrders.ts
Normal file
21
lstMobile/src/lib/queryStuff/getActiveLoadingOrders.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { keepPreviousData, queryOptions } from "@tanstack/react-query";
|
||||||
|
import { api } from "../apiHelper";
|
||||||
|
|
||||||
|
export function getActiveLoadingOrders() {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: ["getActiveLoadingOrders"],
|
||||||
|
queryFn: () => dataFetch(),
|
||||||
|
staleTime: 5000,
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataFetch = async () => {
|
||||||
|
const { data } = await api.get("/dockDoor/activeLoadingOrders");
|
||||||
|
if (!data.success) {
|
||||||
|
throw new Error(data.message ?? "Failed to load articles");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.data ?? [];
|
||||||
|
};
|
||||||
21
lstMobile/src/lib/queryStuff/getDocks.ts
Normal file
21
lstMobile/src/lib/queryStuff/getDocks.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { keepPreviousData, queryOptions } from "@tanstack/react-query";
|
||||||
|
import { api } from "../apiHelper";
|
||||||
|
|
||||||
|
export function getDocks() {
|
||||||
|
return queryOptions({
|
||||||
|
queryKey: ["getDocks"],
|
||||||
|
queryFn: () => dataFetch(),
|
||||||
|
staleTime: 5000,
|
||||||
|
refetchOnWindowFocus: true,
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataFetch = async () => {
|
||||||
|
const { data } = await api.get("/dockDoor/scanners");
|
||||||
|
if (!data.success) {
|
||||||
|
throw new Error(data.message ?? "Failed to load articles");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.data ?? [];
|
||||||
|
};
|
||||||
17
lstMobile/src/lib/queryStuff/queryClient.ts
Normal file
17
lstMobile/src/lib/queryStuff/queryClient.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { QueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
export const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: 30_000,
|
||||||
|
gcTime: 5 * 60_000,
|
||||||
|
retry: 2,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnReconnect: true,
|
||||||
|
refetchOnMount: false,
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
retry: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
34
lstMobile/src/lib/socket.io.ts
Normal file
34
lstMobile/src/lib/socket.io.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { io, type Socket } from "socket.io-client";
|
||||||
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
|
|
||||||
|
let socket: Socket | null = null;
|
||||||
|
|
||||||
|
export function getSocket() {
|
||||||
|
const { serverIp, serverPort } = useAppStore.getState();
|
||||||
|
|
||||||
|
//const port = Number(serverPort) >= 50000 ? "3000" : serverPort;
|
||||||
|
const url = `http://${serverIp}:${serverPort}`;
|
||||||
|
|
||||||
|
if (!socket) {
|
||||||
|
socket = io(url, {
|
||||||
|
path: "/lst/api/socket.io",
|
||||||
|
withCredentials: true,
|
||||||
|
autoConnect: true,
|
||||||
|
reconnection: true,
|
||||||
|
reconnectionAttempts: 5,
|
||||||
|
reconnectionDelay: 1000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function connectSocket() {
|
||||||
|
const socket = getSocket();
|
||||||
|
|
||||||
|
if (!socket.connected && socket.disconnected) {
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import Constants from "expo-constants";
|
||||||
import { useAppStore } from "../hooks/useAppStore";
|
import { useAppStore } from "../hooks/useAppStore";
|
||||||
import { useServerStore } from "../hooks/useServerCheck";
|
import { useServerStore } from "../hooks/useServerCheck";
|
||||||
|
import { downloadLatestApk } from "./lastestVersion";
|
||||||
|
|
||||||
export type ServerVersionInfo = {
|
export type ServerVersionInfo = {
|
||||||
packageName: string;
|
packageName: string;
|
||||||
@@ -60,13 +62,11 @@ export const versionCheck = async () => {
|
|||||||
setServerVersion(res.data);
|
setServerVersion(res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const build = Constants.expoConfig?.android?.versionCode ?? 1;
|
const build = Constants.expoConfig?.android?.versionCode ?? 1;
|
||||||
|
|
||||||
// if (build < res.data.minSupportedVersionCode) {
|
if (build < res.data.versionCode) {
|
||||||
// setStartupRoute("/updateScreen");
|
//await downloadLatestApk(serverIp, port);
|
||||||
// setReady(true);
|
}
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Version check error:", error);
|
console.log("Version check error:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
14
migrations/0057_worthless_trish_tilby.sql
Normal file
14
migrations/0057_worthless_trish_tilby.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE "dock_door_scanners" (
|
||||||
|
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||||
|
"ip" text NOT NULL,
|
||||||
|
"name" text,
|
||||||
|
"dock_id" text,
|
||||||
|
"dock_name" text NOT NULL,
|
||||||
|
"active" boolean DEFAULT true,
|
||||||
|
"current_loading_order" text DEFAULT '',
|
||||||
|
"add_date" timestamp DEFAULT now(),
|
||||||
|
"add_user" text DEFAULT 'lst-system',
|
||||||
|
"upd_date" timestamp DEFAULT now(),
|
||||||
|
"upd_user" text DEFAULT 'lst-system',
|
||||||
|
CONSTRAINT "dock_door_scanners_name_unique" UNIQUE("name")
|
||||||
|
);
|
||||||
1
migrations/0058_damp_donald_blake.sql
Normal file
1
migrations/0058_damp_donald_blake.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "dock_door_scanners" DROP COLUMN "dock_name";
|
||||||
69
migrations/0059_sparkling_joystick.sql
Normal file
69
migrations/0059_sparkling_joystick.sql
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "add_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "add_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "updated_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "alpla_purchase_history" ALTER COLUMN "updated_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "job_audit_log" ALTER COLUMN "finished_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "job_audit_log" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "job_audit_log" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ALTER COLUMN "updated_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ALTER COLUMN "updated_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "deployment_history" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "deployment_history" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics_daily" ALTER COLUMN "first_hit_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics_daily" ALTER COLUMN "last_hit_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics_daily" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics_daily" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics_daily" ALTER COLUMN "updated_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "analytics_daily" ALTER COLUMN "updated_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "datamart" ALTER COLUMN "add_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "datamart" ALTER COLUMN "add_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "datamart" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "datamart" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "dock_door_scanners" ALTER COLUMN "add_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "dock_door_scanners" ALTER COLUMN "add_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "dock_door_scanners" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "dock_door_scanners" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "inv_historical_data" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "inv_historical_data" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "logs" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "logs" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_apt" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_apt" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_apt" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_apt" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_article_setup" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_article_setup" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_article_setup" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_article_setup" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_dock_setup" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_dock_setup" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_dock_setup" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "opendock_dock_setup" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "printer_log" ALTER COLUMN "created_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "printer_log" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "printer_data" ALTER COLUMN "add_Date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "printer_data" ALTER COLUMN "add_Date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "printer_data" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "printer_data" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "scan_log" ALTER COLUMN "add_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "scan_log" ALTER COLUMN "add_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "scan_users" ALTER COLUMN "add_Date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "scan_users" ALTER COLUMN "add_Date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "scan_users" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "scan_users" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "server_data" ALTER COLUMN "last_updated" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "server_data" ALTER COLUMN "last_updated" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "settings" ALTER COLUMN "add_Date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "settings" ALTER COLUMN "add_Date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "settings" ALTER COLUMN "upd_date" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "settings" ALTER COLUMN "upd_date" SET DEFAULT now();--> statement-breakpoint
|
||||||
|
ALTER TABLE "app_stats" ALTER COLUMN "last_build_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "app_stats" ALTER COLUMN "last_deploy_at" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "app_stats" ALTER COLUMN "last_updated" SET DATA TYPE timestamp with time zone;--> statement-breakpoint
|
||||||
|
ALTER TABLE "app_stats" ALTER COLUMN "last_updated" SET DEFAULT now();
|
||||||
2609
migrations/meta/0057_snapshot.json
Normal file
2609
migrations/meta/0057_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2603
migrations/meta/0058_snapshot.json
Normal file
2603
migrations/meta/0058_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
2603
migrations/meta/0059_snapshot.json
Normal file
2603
migrations/meta/0059_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -400,6 +400,27 @@
|
|||||||
"when": 1779454561527,
|
"when": 1779454561527,
|
||||||
"tag": "0056_shallow_chimera",
|
"tag": "0056_shallow_chimera",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 57,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1779843750556,
|
||||||
|
"tag": "0057_worthless_trish_tilby",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 58,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1779846894283,
|
||||||
|
"tag": "0058_damp_donald_blake",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 59,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1780349486711,
|
||||||
|
"tag": "0059_sparkling_joystick",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user