Compare commits

...

16 Commits

Author SHA1 Message Date
2a6af066bc fix(datamart): corrected the date grab to be print date vs delivery date 2025-04-14 12:29:10 -05:00
acd89d926c fix(labeling process): corrected a typo in success 2025-04-14 12:28:38 -05:00
bb20046890 test(printers): added in management tab for printers 2025-04-14 12:28:11 -05:00
ae7ea2bb90 test(notification): added framework for managment tab 2025-04-14 12:27:45 -05:00
17773e9a23 refactor(modules): removed a console log 2025-04-14 12:27:13 -05:00
25785703db feat(exports): added in a button to export the lanes to cycle count 2025-04-14 12:26:56 -05:00
087d14c585 feat(cards): migrated cards over 2025-04-14 12:26:31 -05:00
328b61f6cc test(quality): added in framrwork 2025-04-14 12:25:16 -05:00
c02d289b80 ci(release): bump build number to 224 2025-04-14 09:37:30 -05:00
e0e7e03d37 ci(release): bump build number to 223 2025-04-14 09:02:49 -05:00
5fb7b5c59e ci(release): bump build number to 222 2025-04-13 15:29:14 -05:00
aabcf12bee ci(release): bump build number to 221 2025-04-13 15:24:22 -05:00
f0a5d6e949 ci(release): bump build number to 220 2025-04-13 13:41:41 -05:00
bdeea6bd1a ci(release): bump build number to 219 2025-04-13 13:27:30 -05:00
89ace4343c ci(release): bump build number to 218 2025-04-13 11:49:14 -05:00
242b90c75a feat(changelog): added in a place to see the changelogs 2025-04-13 10:40:31 -05:00
48 changed files with 3736 additions and 396 deletions

View File

@@ -0,0 +1,23 @@
CREATE TABLE "qualityRequest" (
"request_id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"article" numeric,
"description" text,
"runningNr" text,
"lotNr" numeric,
"warehouseAtRequest" text,
"locationAtRequest" text,
"warehouseMovedTo" text,
"locationMovedTo" text,
"durationToMove" numeric,
"locationDropOff" text,
"palletStatus" numeric,
"palletStatusText" text,
"palletRequest" numeric,
"add_date" timestamp DEFAULT now(),
"add_user" text DEFAULT 'LST',
"upd_date" timestamp DEFAULT now(),
"upd_user" text DEFAULT 'LST'
);
--> statement-breakpoint
ALTER TABLE "rfidReaders" ADD COLUMN "lastTagScanned" text;--> statement-breakpoint
ALTER TABLE "logs" DROP COLUMN "checkedAt";

File diff suppressed because it is too large Load Diff

View File

@@ -344,6 +344,13 @@
"when": 1743946411873,
"tag": "0048_tearful_bushwacker",
"breakpoints": true
},
{
"idx": 49,
"version": "7",
"when": 1744649552936,
"tag": "0049_certain_tarot",
"breakpoints": true
}
]
}

View File

@@ -0,0 +1,47 @@
import {
text,
pgTable,
numeric,
index,
timestamp,
boolean,
uuid,
uniqueIndex,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { z } from "zod";
export const qualityRequest = pgTable(
"qualityRequest",
{
request_id: uuid("request_id").defaultRandom().primaryKey(),
article: numeric("article"),
description: text("description"),
runningNr: text("runningNr"),
lotNr: numeric("lotNr"),
warehouseAtRequest: text("warehouseAtRequest"),
locationAtRequest: text("locationAtRequest"),
warehouseMovedTo: text("warehouseMovedTo"),
locationMovedTo: text("locationMovedTo"),
durationToMove: numeric("durationToMove"),
locationDropOff: text("locationDropOff"),
palletStatus: numeric("palletStatus"),
palletStatusText: text("palletStatusText"),
palletRequest: numeric("palletRequest"),
add_date: timestamp("add_date").defaultNow(),
add_user: text("add_user").default("LST"),
upd_date: timestamp("upd_date").defaultNow(),
upd_user: text("upd_user").default("LST"),
},
(table) => [
// uniqueIndex('emailUniqueIndex').on(sql`lower(${table.email})`),
// uniqueIndex("role_name").on(table.name),
]
);
// Schema for inserting a user - can be used to validate API requests
// export const insertRolesSchema = createInsertSchema(roles, {
// name: z.string().min(3, {message: "Role name must be more than 3 letters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectRolesSchema = createSelectSchema(qualityRequest);

View File

@@ -23,6 +23,7 @@
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.0.15",
"@tanstack/react-form": "^1.2.1",
"@tanstack/react-query": "^5.69.0",
@@ -37,6 +38,7 @@
"js-cookie": "^3.0.5",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.483.0",
"marked": "^15.0.8",
"next-themes": "^0.4.6",
"npm-check-updates": "^17.1.16",
"react": "^19.0.0",
@@ -2937,6 +2939,21 @@
"node": ">= 10"
}
},
"node_modules/@tailwindcss/typography": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
"integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
"license": "MIT",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.merge": "^4.6.2",
"postcss-selector-parser": "6.0.10"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
}
},
"node_modules/@tailwindcss/vite": {
"version": "4.0.15",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.15.tgz",
@@ -4177,6 +4194,18 @@
"node": ">= 8"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -5717,6 +5746,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.castarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
"license": "MIT"
},
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -5757,7 +5792,6 @@
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"license": "MIT"
},
"node_modules/lodash.once": {
@@ -5797,6 +5831,18 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/marked": {
"version": "15.0.8",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.8.tgz",
"integrity": "sha512-rli4l2LyZqpQuRve5C0rkn6pj3hT8EWPC+zkAxFTAJLxRbENfTAhEQq9itrmf1Y81QtAX5D/MYlGlIomNgj9lA==",
"license": "MIT",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -6075,6 +6121,19 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-selector-parser": {
"version": "6.0.10",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -7029,6 +7088,12 @@
"which-typed-array": "^1.1.2"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/victory-vendor": {
"version": "36.9.2",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",

View File

@@ -41,6 +41,7 @@
"js-cookie": "^3.0.5",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.483.0",
"marked": "^15.0.8",
"next-themes": "^0.4.6",
"npm-check-updates": "^17.1.16",
"react": "^19.0.0",

View File

@@ -36,7 +36,7 @@ export function ChangeModule({ module }: { module: any }) {
active: module.active,
},
onSubmit: async ({ value }) => {
console.log(value);
//console.log(value);
try {
const result = await axios.patch(
`/api/server/modules/${module.module_id}`,

View File

@@ -1,8 +1,22 @@
import { getnotifications } from "@/utils/querys/admin/notifications";
import { notifyColumns } from "@/utils/tableData/notifications/notifyColumns";
import { NotifyTable } from "@/utils/tableData/notifications/notifyData";
import { useQuery } from "@tanstack/react-query";
export default function NotificationMGT() {
const { data, isError, isLoading } = useQuery(getnotifications());
if (isLoading) return <div>Loading adjustmnet data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the adjustments.</p>
</div>
);
}
return (
<div>
Manage all notifications from here instad of going to the db,
locking some items that are auto updated on server restarts
<NotifyTable columns={notifyColumns} data={data} />
</div>
);
}

View File

@@ -0,0 +1,33 @@
// src/components/Changelog.jsx
import { marked } from "marked";
import changelog from "../../../../CHANGELOG.md?raw"; // assuming changelog.md is in root
const Changelog = () => {
const html: any = marked.parse(changelog);
const recentChanges = html.split("<h2>")[1];
// const [htmlContent, setHtmlContent] = useState("");
// useEffect(() => {
// fetch("/changelog.md")
// .then((res) => res.text())
// .then((md) => {
// const versionBlocks = md.split(/^##\s+/gm); // Split on headings
// if (versionBlocks.length > 1) {
// const latestBlock = `## ${versionBlocks[1]}`;
// const html: any = marked.parse(latestBlock, {
// breaks: true,
// gfm: true,
// });
// setHtmlContent(html);
// }
// });
// }, []);
return (
<div
className="prose m-3"
dangerouslySetInnerHTML={{ __html: `<h2>${recentChanges}` }}
/>
);
};
export default Changelog;

View File

@@ -3,55 +3,37 @@ import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { useCardStore } from "@/lib/store/useCardStore";
import { toast } from "sonner";
import Cards from "./Cards";
//import { toast } from "sonner";
export function AddCards() {
const { cards, addCard } = useCardStore();
const handleAddPPOO = () => {
const existingCard = cards.filter((c) => c.i === "PPOO");
//console.log(existingCard.length);
//console.log(data);
if (existingCard.length != 0) {
toast.error(`PPOO already exists no card will be added`);
return;
}
addCard({
i: "PPOO",
x: 0,
y: 0,
w: 5,
h: 3,
isResizable: true,
isDraggable: true,
});
};
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Add Cards</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogContent className="min-w-fit ">
<DialogHeader>
<DialogTitle>Cards</DialogTitle>
<DialogDescription>
Please add a card below.
Manage Cards and there settings.
</DialogDescription>
</DialogHeader>
<Button onClick={handleAddPPOO} className="w-48">
PPOO
</Button>
<DialogFooter>
<Cards name={"ppoo"} inventory />
<Cards name={"inv-empty"} rowType={"empty"} />
<Cards name={"inv-fg"} rowType={"fg"} />
<Cards name={"inv-materials"} rowType={"materials"} />
<Cards name={"inv-packaging"} rowType={"packaging"} />
<Cards name={"inv-waste"} rowType={"waste"} />
{/* <DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogFooter> */}
</DialogContent>
</Dialog>
);

View File

@@ -0,0 +1,183 @@
import { useCardStore } from "@/lib/store/useCardStore";
import { useForm } from "@tanstack/react-form";
import { Label } from "../ui/label";
import { Checkbox } from "../ui/checkbox";
import { Input } from "../ui/input";
// import {
// Select,
// SelectContent,
// SelectGroup,
// SelectItem,
// SelectLabel,
// SelectTrigger,
// SelectValue,
// } from "../ui/select";
import { Button } from "../ui/button";
import { toast } from "sonner";
export default function Cards(card: any) {
const { addCard, removeCard, cards } = useCardStore();
let existing: any = cards.filter((n: any) => n.name === card.name);
console.log(existing);
const form = useForm({
defaultValues: {
name: existing?.name || card.name,
rowType: existing?.type ?? card.rowType,
age: existing?.age ?? 90,
active: existing.active ? existing.active : false,
},
onSubmit: async ({ value }) => {
console.log(value);
const testCard: any = cards.filter(
(i: any) => i.name === value.name
);
if (value.active) {
if (testCard.length > 0) {
toast.error("Card already exists");
return;
}
// change the name for a type card
const newCard = {
name: `${value.name}`,
rowType: value.rowType,
age: value.age ?? 90,
active: value.active,
};
addCard(newCard);
} else {
removeCard(value.name);
}
},
});
return (
<div className="border-solid border-2">
<p>{card.name}</p>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
className="flex flex-row"
>
<form.Field
name="active"
// validators={{
// // We can choose between form-wide and field-specific validators
// onChange: ({ value }) =>
// value.length > 3
// ? undefined
// : "Username must be longer than 3 letters",
// }}
children={(field) => {
return (
<div className="m-2 p-2 flex flex-row">
<div>
<Label htmlFor="active">
<span>Active</span>
</Label>
</div>
<Checkbox
className="ml-2"
name={field.name}
onBlur={field.handleBlur}
checked={field.state.value}
onCheckedChange={(e) =>
field.handleChange(e)
}
/>
</div>
);
}}
/>
{!card.inventory && (
<>
<form.Field
name="age"
// validators={{
// // We can choose between form-wide and field-specific validators
// onChange: ({ value }) =>
// value.length > 3
// ? undefined
// : "Username must be longer than 3 letters",
// }}
children={(field) => {
return (
<div className="m-2 min-w-48 p-2">
<Label htmlFor="active" className="">
Age
</Label>
<Input
name={field.name}
onBlur={field.handleBlur}
type="number"
onChange={(e) =>
field.handleChange(
e.target.value
)
}
/>
</div>
);
}}
/>
{/* <form.Field
name="rowType"
//listeners={{onChange: ({value})=>{}}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor={field.name}>
Row Type
</Label>
<Select
value={field.state.value}
onValueChange={field.handleChange}
>
<SelectTrigger className="w-[180px]">
<SelectValue
id={field.name}
placeholder="Select Role"
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>
Row Type
</SelectLabel>
<SelectItem value="empty">
Empty
</SelectItem>
<SelectItem value="fg">
Finished Goods
</SelectItem>
<SelectItem value="materials">
Materials
</SelectItem>
<SelectItem value="waste">
Waste
</SelectItem>
<SelectItem value="packaging">
Packaging
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
);
}}
/> */}
</>
)}
<div className="mt-7">
<Button type="submit" onClick={() => form.handleSubmit()}>
Save Card
</Button>
</div>
</form>
</div>
);
}

View File

@@ -1,68 +1,74 @@
import { useCardStore } from "@/lib/store/useCardStore";
import INVCheckCard from "../logistics/warehouse/InventoryCard";
import PPOO from "../logistics/warehouse/PPOOCard";
const componentsMap: any = {
ppoo: PPOO,
inv: INVCheckCard,
//QualityRequest,
};
export default function DashBoard() {
// const handleResizeStop = (newLayout: any, newItem: any) => {
// updateCard(
// newItem.i,
// newLayout.filter((n: any) => n.i === newItem.i)[0]
// ); // Store the new layout in state
// };
// const handleDragStop = (newLayout: any, newItem: any) => {
// updateCard(
// newItem.i,
// newLayout.filter((n: any) => n.i === newItem.i)[0]
// ); // Persist the updated layout with custom name
// };
const { cards } = useCardStore();
//console.log(cards);
return (
<div className="ml-5 w-11/12 h-9/10">
<p>Comming soon...</p>
{/* <ResizablePanelGroup
direction="horizontal"
//className="rounded-lg border"
autoSaveId="persistence"
>
<ResizablePanel>
<ResizablePanelGroup direction="vertical">
<ResizablePanel>
<div className="overflow: auto">
<Lots />
</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel>
<ResizablePanelGroup direction="horizontal">
<ResizablePanel>
<LabelLog />
</ResizablePanel>
<ResizableHandle />
<ResizablePanel>
<OcpLogs />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={25}>
<ResizablePanelGroup direction="vertical">
{server[0].value === "usday1vms006" && (
<ResizablePanel className="max-h-[300px]">
<WrapperManualTrigger />
</ResizablePanel>
)}
{server[0].value === "usday1vms006" && (
<ResizablePanel className="max-h-[300px]">
<WrapperManualTrigger />
</ResizablePanel>
)}
<div className="ml-5 w-11/12 h-9/10 grid grid-cols-12 gap-1">
{cards.map((a: any) => {
const name = a.name; //.filter((c) => c.i === card.i)[0].i || "name";
<ResizableHandle />
<ResizablePanel>
<PrinterStatus />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup> */}
const Component = componentsMap[name.split("-")[0]];
return (
<div key={a.name} className="col-span-3">
<Component age={a.age} type={a.rowType} />{" "}
</div>
);
})}
</div>
);
// return (
// <div className="ml-5 w-11/12 h-9/10 grid grid-cols-12 gap-1">
// <div className="col-span-3">
// <PPOO />
// </div>
// <div className="col-span-3">
// <INVCheckCard age={90} type={"empty"} />
// </div>
// <div className="col-span-3">
// <INVCheckCard age={75} type={"fg"} />
// </div>
// <div className="col-span-3">
// <INVCheckCard age={30} type={"materials"} />
// </div>
// <div className="col-span-3">
// <INVCheckCard age={7} type={"waste"} />
// </div>
// <div className="col-span-3">
// <INVCheckCard age={7} type={"packaging"} />
// </div>
// </div>
// );
}
/*
<div className="col-span-3">
<PPOO />
</div>
<div className="col-span-3">
<INVCheckCard age={30} type={"empty"} />
</div>
<div className="col-span-3">
<INVCheckCard age={30} type={"fg"} />
</div>
<div className="col-span-3">
<INVCheckCard age={30} type={"materials"} />
</div>
<div className="col-span-3">
<INVCheckCard age={30} type={"waste"} />
</div>
<div className="col-span-3">
<INVCheckCard age={30} type={"packaging"} />
</div>
*/

View File

@@ -0,0 +1,153 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useForm } from "@tanstack/react-form";
import axios from "axios";
import { format } from "date-fns";
import { useState } from "react";
import { toast } from "sonner";
export default function ExportInventoryData() {
const [open, setOpen] = useState(false);
const [saving, setSaving] = useState(false);
const form = useForm({
defaultValues: {
age: "",
},
onSubmit: async ({ value }) => {
setSaving(true);
try {
const res = await axios.get(
`/api/logistics/getcyclecount?age=${value.age}`,
{
responseType: "blob",
}
);
const blob = new Blob([res.data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `CycleCount-${format(new Date(Date.now()), "M-d-yyyy")}.xlsx`; // You can make this dynamic
document.body.appendChild(link);
link.click();
// Clean up
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
toast.success(`File Downloaded`);
setSaving(false);
setOpen(false);
form.reset();
} catch (error) {
console.log(error);
console.log(`There was an error getting cycle counts.`);
}
},
});
return (
<div>
<Dialog
open={open}
onOpenChange={(isOpen) => {
if (!open) {
form.reset();
}
setOpen(isOpen);
// toast.message("Model was something", {
// description: isOpen ? "Modal is open" : "Modal is closed",
// });
}}
>
<DialogTrigger asChild>
<Button variant="outline">Export Inventory Check</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Export Inventory lane check</DialogTitle>
<DialogDescription>
Exports all lanes based on the age you enter, except
empty lanes.
</DialogDescription>
</DialogHeader>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<div>
<>
<form.Field
name="age"
// validators={{
// // We can choose between form-wide and field-specific validators
// onChange: ({ value }) =>
// value.length > 3
// ? undefined
// : "Username must be longer than 3 letters",
// }}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2 flex flex-row">
<Label htmlFor="active">
Age
</Label>
<Input
className="ml-2"
name={field.name}
onBlur={field.handleBlur}
type="number"
onChange={(e) =>
field.handleChange(
e.target.value
)
}
/>
</div>
);
}}
/>
</>
</div>
<DialogFooter>
<div className="flex justify-end mt-2">
<Button onClick={() => setOpen(false)}>
Close
</Button>
<Button
type="submit"
disabled={saving}
onClick={form.handleSubmit}
>
{saving ? (
<>
<span>Saving....</span>
</>
) : (
<span>Save setting</span>
)}
</Button>
</div>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
);
}

View File

@@ -0,0 +1,37 @@
//import { LstCard } from "@/components/extendedUI/LstCard";
import { getinventoryCheck } from "@/utils/querys/logistics/getInventoryCheck";
import { invColumns } from "@/utils/tableData/InventoryCards/inventoryColumns";
import { InvTable } from "@/utils/tableData/InventoryCards/inventoryData";
import { useQuery } from "@tanstack/react-query";
//import { CircleX } from "lucide-react";
//import { Suspense } from "react";
//import { toast } from "sonner";
export default function INVCheckCard(props: any) {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getinventoryCheck(props));
if (isLoading) return <div>Loading inventory data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the inv.</p>
</div>
);
}
let laneData: any = data;
if (props.type != "") {
laneData = laneData.filter(
(l: any) => l.rowType === props.type.toUpperCase()
);
}
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return <InvTable columns={invColumns} data={laneData} info={props} />;
}

View File

@@ -1,15 +1,14 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { useCardStore } from "@/lib/store/useCardStore";
//import { LstCard } from "@/components/extendedUI/LstCard";
import { getPPOO } from "@/utils/querys/logistics/getPPOO";
import { columns } from "@/utils/tableData/ppoo/ppooColumns";
import { PPOOTable } from "@/utils/tableData/ppoo/ppooData";
import { useQuery } from "@tanstack/react-query";
import { CircleX } from "lucide-react";
import { Suspense } from "react";
import { toast } from "sonner";
//import { CircleX } from "lucide-react";
//import { Suspense } from "react";
//import { toast } from "sonner";
export default function PPOO({ style = {} }) {
const { removeCard } = useCardStore();
export default function PPOO() {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getPPOO());
if (isLoading) return <div>Loading adjustmnet data...</div>;
@@ -21,30 +20,42 @@ export default function PPOO({ style = {} }) {
);
}
const handleCloseCard = () => {
removeCard("PPOO");
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
toast.success("card removed");
};
return (
<div style={style}>
<LstCard style={style}>
<Suspense fallback={<p>Loading PPOO...</p>}>
<div className={`flex justify-center`}>
<p
className={`drag-handle w-fit`}
style={{ cursor: "move", padding: "5px" }}
>
PPOO
</p>
<button onClick={handleCloseCard}>
<CircleX />
</button>
</div>
<PPOOTable columns={columns} data={data} style={style} />
</Suspense>
</LstCard>
</div>
<PPOOTable
columns={columns}
data={data}
//style={style}
/>
);
// return (
// <div style={style}>
// <LstCard style={style}>
// <Suspense fallback={<p>Loading PPOO...</p>}>
// <div className={`flex justify-center`}>
// <p
// className={`drag-handle w-fit`}
// style={{ cursor: "move", padding: "5px" }}
// >
// PPOO
// </p>
// <button onClick={handleCloseCard}>
// <CircleX />
// </button>
// </div>
// <PPOOTable
// columns={columns}
// data={data}
// //style={style}
// />
// </Suspense>
// </LstCard>
// </div>
// );
}

View File

@@ -1,31 +1,23 @@
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
interface CardSettings {
daysSinceLast?: number;
rowType?: string;
}
// interface CardSettings {
// daysSinceLast?: number;
// rowType?: string;
// }
export interface Card {
i: string;
x: number;
y: number;
w: number;
h: number;
minW?: number;
maxW?: number;
minH?: number;
maxH?: number;
isResizable: boolean;
isDraggable: boolean;
cardSettings?: CardSettings;
name: string;
rowType: string;
age: string;
active: boolean;
}
interface CardStore {
cards: Card[]; // Array of card objects
addCard: (card: Card) => void;
updateCard: (id: string, updatedCard: Partial<Card>) => void;
removeCard: (id: string) => void;
updateCard: (name: string, updatedCard: Partial<Card>) => void;
removeCard: (name: string) => void;
}
export const useCardStore = create<CardStore>()(
@@ -37,16 +29,18 @@ export const useCardStore = create<CardStore>()(
addCard: (card) =>
set((state) => ({ cards: [...state.cards, card] })),
updateCard: (id, updatedCard) =>
updateCard: (name, updatedCard) =>
set((state) => ({
cards: state.cards.map((card) =>
card.i === id ? { ...card, ...updatedCard } : card
card.name === name
? { ...card, ...updatedCard }
: card
),
})),
removeCard: (id) =>
removeCard: (name) =>
set((state) => ({
cards: state.cards.filter((card) => card.i !== id),
cards: state.cards.filter((card) => card.name !== name),
})),
}),
{ name: "card-storage" }

View File

@@ -12,6 +12,7 @@
import { Route as rootRoute } from './routes/__root'
import { Route as LoginImport } from './routes/login'
import { Route as ChangelogImport } from './routes/changelog'
import { Route as AboutImport } from './routes/about'
import { Route as EomImport } from './routes/_eom'
import { Route as AuthImport } from './routes/_auth'
@@ -43,6 +44,12 @@ const LoginRoute = LoginImport.update({
getParentRoute: () => rootRoute,
} as any)
const ChangelogRoute = ChangelogImport.update({
id: '/changelog',
path: '/changelog',
getParentRoute: () => rootRoute,
} as any)
const AboutRoute = AboutImport.update({
id: '/about',
path: '/about',
@@ -217,6 +224,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AboutImport
parentRoute: typeof rootRoute
}
'/changelog': {
id: '/changelog'
path: '/changelog'
fullPath: '/changelog'
preLoaderRoute: typeof ChangelogImport
parentRoute: typeof rootRoute
}
'/login': {
id: '/login'
path: '/login'
@@ -394,6 +408,7 @@ export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'': typeof EomRouteWithChildren
'/about': typeof AboutRoute
'/changelog': typeof ChangelogRoute
'/login': typeof LoginRoute
'/modules': typeof AdminModulesRoute
'/notificationMGT': typeof AdminNotificationMGTRoute
@@ -418,6 +433,7 @@ export interface FileRoutesByTo {
'/': typeof IndexRoute
'': typeof EomRouteWithChildren
'/about': typeof AboutRoute
'/changelog': typeof ChangelogRoute
'/login': typeof LoginRoute
'/modules': typeof AdminModulesRoute
'/notificationMGT': typeof AdminNotificationMGTRoute
@@ -445,6 +461,7 @@ export interface FileRoutesById {
'/_auth': typeof AuthRouteWithChildren
'/_eom': typeof EomRouteWithChildren
'/about': typeof AboutRoute
'/changelog': typeof ChangelogRoute
'/login': typeof LoginRoute
'/_admin/modules': typeof AdminModulesRoute
'/_admin/notificationMGT': typeof AdminNotificationMGTRoute
@@ -471,6 +488,7 @@ export interface FileRouteTypes {
| '/'
| ''
| '/about'
| '/changelog'
| '/login'
| '/modules'
| '/notificationMGT'
@@ -494,6 +512,7 @@ export interface FileRouteTypes {
| '/'
| ''
| '/about'
| '/changelog'
| '/login'
| '/modules'
| '/notificationMGT'
@@ -519,6 +538,7 @@ export interface FileRouteTypes {
| '/_auth'
| '/_eom'
| '/about'
| '/changelog'
| '/login'
| '/_admin/modules'
| '/_admin/notificationMGT'
@@ -546,6 +566,7 @@ export interface RootRouteChildren {
AuthRoute: typeof AuthRouteWithChildren
EomRoute: typeof EomRouteWithChildren
AboutRoute: typeof AboutRoute
ChangelogRoute: typeof ChangelogRoute
LoginRoute: typeof LoginRoute
OcpIndexRoute: typeof OcpIndexRoute
logisticsSiloAdjustmentsHistRoute: typeof logisticsSiloAdjustmentsHistRoute
@@ -563,6 +584,7 @@ const rootRouteChildren: RootRouteChildren = {
AuthRoute: AuthRouteWithChildren,
EomRoute: EomRouteWithChildren,
AboutRoute: AboutRoute,
ChangelogRoute: ChangelogRoute,
LoginRoute: LoginRoute,
OcpIndexRoute: OcpIndexRoute,
logisticsSiloAdjustmentsHistRoute: logisticsSiloAdjustmentsHistRoute,
@@ -592,6 +614,7 @@ export const routeTree = rootRoute
"/_auth",
"/_eom",
"/about",
"/changelog",
"/login",
"/ocp/",
"/(logistics)/siloAdjustments/$hist",
@@ -633,6 +656,9 @@ export const routeTree = rootRoute
"/about": {
"filePath": "about.tsx"
},
"/changelog": {
"filePath": "changelog.tsx"
},
"/login": {
"filePath": "login.tsx"
},

View File

@@ -1,4 +1,9 @@
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
import {
createRootRoute,
Link,
Outlet,
useLocation,
} from "@tanstack/react-router";
//import {TanStackRouterDevtools} from "@tanstack/router-devtools";
import Cookies from "js-cookie";
import { SidebarProvider } from "../components/ui/sidebar";
@@ -17,11 +22,12 @@ import {
import { SessionProvider } from "../components/providers/Providers";
import { Toaster } from "sonner";
//import { Button } from "../components/ui/button";
import { useSessionStore } from "../lib/store/sessionStore";
import { useSession } from "@/hooks/useSession";
import { useLogout } from "@/hooks/useLogout";
import ExportInventoryData from "@/components/logistics/warehouse/ExportInventoryData";
import { AddCards } from "@/components/dashboard/AddCards";
//import { AddCards } from "@/components/dashboard/AddCards";
// same as the layout
export const Route = createRootRoute({
@@ -30,16 +36,20 @@ export const Route = createRootRoute({
const { session } = useSession();
const { user } = useSessionStore();
const logout = useLogout();
const location = useLocation();
return (
<>
<div className="overflow-hidden">
<SessionProvider>
<ThemeProvider>
<nav className="flex justify-end">
<nav className="flex justify-end w-full shadow ">
<div className="m-2 flex flex-row">
<div className="m-auto pr-2">
<AddCards />
</div>
{location.pathname === "/" && (
<div className="m-auto pr-2 flex flex-row gap-2">
<ExportInventoryData />
<AddCards />
</div>
)}
<div className="m-1">
<ModeToggle />
</div>
@@ -88,16 +98,18 @@ export const Route = createRootRoute({
)}
</div>
</nav>
<SidebarProvider defaultOpen={sidebarState}>
<AppSidebar />
<Toaster expand={true} richColors closeButton />
<Outlet />
</SidebarProvider>
<main>
<SidebarProvider defaultOpen={sidebarState}>
<AppSidebar />
<Toaster expand={true} richColors closeButton />
<Outlet />
</SidebarProvider>
</main>
</ThemeProvider>
</SessionProvider>
{/* <TanStackRouterDevtools position="bottom-right" /> */}
</>
</div>
);
},
});

View File

@@ -0,0 +1,14 @@
import Changelog from "@/components/changelog/ChangeLog";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/changelog")({
component: RouteComponent,
});
function RouteComponent() {
return (
<div>
<Changelog />
</div>
);
}

View File

@@ -5,120 +5,120 @@
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.87 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.87 0 0);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.87 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.87 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -0,0 +1,26 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getnotifications() {
const token = localStorage.getItem("auth_token");
return queryOptions({
queryKey: ["getNotifications"],
queryFn: () => fetchUsers(token),
enabled: !!token, // Prevents query if token is null
staleTime: 1000,
refetchInterval: 2 * 2000,
refetchOnWindowFocus: true,
});
}
const fetchUsers = async (token: string | null) => {
const { data } = await axios.get(`/api/notify/notifications`, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
return data.data ?? [];
};

View File

@@ -0,0 +1,25 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getinventoryCheck(data: any) {
return queryOptions({
queryKey: ["getInvCheck"],
queryFn: () => fetchStockSilo(data),
//enabled:
staleTime: 1000,
refetchInterval: 60 * 1000,
refetchOnWindowFocus: true,
});
}
const fetchStockSilo = async (info: any) => {
console.log(info);
const { data } = await axios.post(`/api/logistics/cyclecountcheck`, {
age: info.age ? parseInt(info.age) : null,
type: "",
});
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
return data.data ?? [];
};

View File

@@ -7,7 +7,7 @@ export function getPPOO() {
queryFn: () => fetchStockSilo(),
//enabled:
staleTime: 1000,
refetchInterval: 2 * 2000,
refetchInterval: 60 * 1000,
refetchOnWindowFocus: true,
});
}

View File

@@ -0,0 +1,27 @@
import { ColumnDef } from "@tanstack/react-table";
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type Adjustmnets = {
siloAdjust_id: string;
currentStockLevel: string;
newLevel: number;
dateAdjusted: string;
lastDateAdjusted: string;
comment: string;
commentAddedBy: string;
commentDate: string;
add_user: string;
};
export const invColumns: ColumnDef<Adjustmnets>[] = [
{
accessorKey: "Description",
header: () => <div className="text-left">Lane</div>,
},
{
accessorKey: "DaysSinceLast",
header: "Age",
//enableSorting: true,
},
];

View File

@@ -0,0 +1,166 @@
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
getPaginationRowModel,
getSortedRowModel,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
//import { Button } from "@/components/ui/button";
import { useState } from "react";
import { ScrollArea } from "@/components/ui/scroll-area";
import { LstCard } from "@/components/extendedUI/LstCard";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
info: any;
}
type ColumnSort = {
id: string;
desc: boolean;
};
type SortingState = ColumnSort[];
export function InvTable<TData, TValue>({
columns,
data,
info,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([]);
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 5, //default page size
});
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
getSortedRowModel: getSortedRowModel(),
state: {
//...
pagination,
sorting,
},
manualSorting: true,
onSortingChange: setSorting,
initialState: {
sorting: [
{
id: "DaysSinceLast",
desc: true,
},
],
},
});
//console.log(table.getState().sorting);
//console.log(parseInt(style.height.replace("px", "")) - 50);
return (
<LstCard
className="p-3"
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 150}px`,
// cursor: "move",
// }}
//style={{ overflow: "auto" }}
>
<div>
<div className="flex flex-row justify-between">
<p className="text-center text-pretty">
{info.type} {data.length > 0 ? "lanes" : "lane"} older
than: {info.age}, needing to be completed
</p>
</div>
<ScrollArea className="h-72 rounded-md border m-2">
<Table
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 200}px`,
// cursor: "move",
// }}
>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column
.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</ScrollArea>
</div>
{/* <div className="flex items-center justify-end space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div> */}
</LstCard>
);
}

View File

@@ -0,0 +1,68 @@
import { ColumnDef } from "@tanstack/react-table";
import { format } from "date-fns";
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type Adjustmnets = {
siloAdjust_id: string;
currentStockLevel: string;
newLevel: number;
dateAdjusted: string;
lastDateAdjusted: string;
comment: string;
commentAddedBy: string;
commentDate: string;
add_user: string;
};
export const notifyColumns: ColumnDef<Adjustmnets>[] = [
{
accessorKey: "name",
header: () => <div className="text-left">Name</div>,
},
{
accessorKey: "description",
header: "Description",
cell: ({ row }) => {
return (
<div className="text-left font-medium">
<p className="max-w-48 text-pretty">
{row.getValue("description")}
</p>
</div>
);
},
},
{
accessorKey: "checkInterval",
header: "Check Interval",
},
{
accessorKey: "timeType",
header: "Time tpye",
},
{
accessorKey: "emails",
header: "Emails",
},
{
accessorKey: "active",
header: "Active",
},
{
accessorKey: "lastRan",
header: "Last Ran",
cell: ({ row }) => {
if (row.getValue("lastRan")) {
const correctDate = format(
row.getValue("lastRan"),
"M/d/yyyy hh:mm"
);
return (
<div className="text-left font-medium">{correctDate}</div>
);
}
},
},
];

View File

@@ -0,0 +1,134 @@
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
getPaginationRowModel,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { useState } from "react";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
//style: any;
}
export function NotifyTable<TData, TValue>({
columns,
data,
//style,
}: DataTableProps<TData, TValue>) {
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 5, //default page size
});
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
state: {
//...
pagination,
},
});
// console.log(parseInt(style.height.replace("px", "")) - 50);
return (
<div
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 150}px`,
// cursor: "move",
// }}
>
<div>
<Table
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 200}px`,
// cursor: "move",
// }}
>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef
.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
);
}

View File

@@ -14,94 +14,147 @@ import {
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { useState } from "react";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { ScrollArea } from "@/components/ui/scroll-area";
import { LstCard } from "@/components/extendedUI/LstCard";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
style: any;
//style: any;
}
export function PPOOTable<TData, TValue>({
columns,
data,
style,
//style,
}: DataTableProps<TData, TValue>) {
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 5, //default page size
});
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
state: {
//...
pagination,
},
});
console.log(parseInt(style.height.replace("px", "")) - 50);
//console.log(parseInt(style.height.replace("px", "")) - 50);
return (
<div
style={{
width: `${parseInt(style.width.replace("px", "")) - 50}px`,
height: `${parseInt(style.height.replace("px", "")) - 150}px`,
cursor: "move",
}}
<LstCard
className="p-3"
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 150}px`,
// cursor: "move",
// }}
//style={{ overflow: "auto" }}
>
<div>
<Table
style={{
width: `${parseInt(style.width.replace("px", "")) - 50}px`,
height: `${parseInt(style.height.replace("px", "")) - 200}px`,
cursor: "move",
}}
>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef
.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
<div className="flex flex-row justify-between">
<p className="text-center">PPOO Data </p>
<Select
value={pagination.pageSize.toString()}
onValueChange={(e) =>
setPagination({
...pagination,
pageSize: parseInt(e),
})
}
>
<SelectTrigger className="w-[180px]">
<SelectValue
//id={field.name}
placeholder="Select Page"
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Page Size</SelectLabel>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="50">50</SelectItem>
<SelectItem value="100">100</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<ScrollArea className="h-72 rounded-md border m-2">
<Table
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 200}px`,
// cursor: "move",
// }}
>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column
.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</ScrollArea>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex items-center justify-end space-x-2">
<Button
variant="outline"
size="sm"
@@ -119,6 +172,6 @@ export function PPOOTable<TData, TValue>({
Next
</Button>
</div>
</div>
</LstCard>
);
}

View File

@@ -0,0 +1,68 @@
import { ColumnDef } from "@tanstack/react-table";
import { format } from "date-fns";
// This type is used to define the shape of our data.
// You can use a Zod schema here if you want.
export type Adjustmnets = {
siloAdjust_id: string;
currentStockLevel: string;
newLevel: number;
dateAdjusted: string;
lastDateAdjusted: string;
comment: string;
commentAddedBy: string;
commentDate: string;
add_user: string;
};
export const notifyColumns: ColumnDef<Adjustmnets>[] = [
{
accessorKey: "name",
header: () => <div className="text-left">Name</div>,
},
{
accessorKey: "description",
header: "Description",
cell: ({ row }) => {
return (
<div className="text-left font-medium">
<p className="max-w-48 text-pretty">
{row.getValue("description")}
</p>
</div>
);
},
},
{
accessorKey: "checkInterval",
header: "Check Interval",
},
{
accessorKey: "timeType",
header: "Time tpye",
},
{
accessorKey: "emails",
header: "Emails",
},
{
accessorKey: "active",
header: "Active",
},
{
accessorKey: "lastRan",
header: "Last Ran",
cell: ({ row }) => {
if (row.getValue("lastRan")) {
const correctDate = format(
row.getValue("lastRan"),
"M/d/yyyy hh:mm"
);
return (
<div className="text-left font-medium">{correctDate}</div>
);
}
},
},
];

View File

@@ -0,0 +1,134 @@
import {
ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
getPaginationRowModel,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { useState } from "react";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
//style: any;
}
export function NotifyTable<TData, TValue>({
columns,
data,
//style,
}: DataTableProps<TData, TValue>) {
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 5, //default page size
});
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
state: {
//...
pagination,
},
});
// console.log(parseInt(style.height.replace("px", "")) - 50);
return (
<div
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 150}px`,
// cursor: "move",
// }}
>
<div>
<Table
// style={{
// width: `${parseInt(style.width.replace("px", "")) - 50}px`,
// height: `${parseInt(style.height.replace("px", "")) - 200}px`,
// cursor: "move",
// }}
>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef
.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={
row.getIsSelected() && "selected"
}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
);
}

View File

@@ -1,16 +1,22 @@
import {defineConfig} from "vite";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import tailwindcss from "@tailwindcss/vite";
import {TanStackRouterVite} from "@tanstack/router-plugin/vite";
import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
import path from "path";
import dotenv from "dotenv";
import {fileURLToPath} from "url";
dotenv.config({path: path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.env")});
import { fileURLToPath } from "url";
dotenv.config({
path: path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.env"),
});
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss(), TanStackRouterVite({target: "react", autoCodeSplitting: true})],
plugins: [
react(),
tailwindcss(),
TanStackRouterVite({ target: "react", autoCodeSplitting: true }),
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),

104
package-lock.json generated
View File

@@ -40,6 +40,7 @@
"rimraf": "^6.0.1",
"st-ethernet-ip": "^2.7.3",
"ws": "^8.18.1",
"xlsx": "^0.18.5",
"zod": "^3.24.2"
},
"devDependencies": {
@@ -2070,6 +2071,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/adm-zip": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz",
@@ -2452,6 +2462,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -2532,6 +2555,15 @@
"node": ">=0.8"
}
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -3239,6 +3271,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"license": "Apache-2.0",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/croner": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/croner/-/croner-9.0.0.tgz",
@@ -4293,6 +4337,15 @@
"node": ">= 6"
}
},
"node_modules/frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/fs-extra": {
"version": "11.3.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
@@ -7781,6 +7834,18 @@
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"license": "BSD-3-Clause"
},
"node_modules/ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"license": "Apache-2.0",
"dependencies": {
"frac": "~1.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/st-ethernet-ip": {
"version": "2.7.3",
"resolved": "https://registry.npmjs.org/st-ethernet-ip/-/st-ethernet-ip-2.7.3.tgz",
@@ -8824,6 +8889,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
@@ -8972,6 +9055,27 @@
}
}
},
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",

View File

@@ -35,7 +35,7 @@
}
},
"admConfig": {
"build": 217,
"build": 224,
"oldBuild": "backend-0.1.3.zip"
},
"devDependencies": {
@@ -88,6 +88,7 @@
"rimraf": "^6.0.1",
"st-ethernet-ip": "^2.7.3",
"ws": "^8.18.1",
"xlsx": "^0.18.5",
"zod": "^3.24.2"
}
}

View File

@@ -27,6 +27,7 @@ import { sendEmail } from "./services/notifications/controller/sendMail.js";
import notify from "./services/notifications/notifyService.js";
import eom from "./services/eom/eomService.js";
import dataMart from "./services/dataMart/dataMartService.js";
import qualityRequest from "./services/quality/qualityService.js";
// create the main prodlogin here
const username = "lst_user";
@@ -104,6 +105,7 @@ const routes = [
notify,
eom,
dataMart,
qualityRequest,
] as const;
const appRoutes = routes.forEach((route) => {

View File

@@ -9,14 +9,14 @@ import { cycleCountCheck } from "../../../../sqlServer/querys/warehouse/cycleCou
// setting timer for updating stockCheck on a restart will always check.
let lastCheck = 0;
export const lanes: any = [];
export let lanes: any = [];
export const getLanesToCycleCount = async () => {
const currentTime: any = timeZoneFix();
// store the lanes in memeory
createLog("info", "warehouse", "logistics", "Lane triggered update.");
lastCheck = currentTime;
const ageQuery = cycleCountCheck.replaceAll("[ageOfRow]", `1000`);
const ageQuery = cycleCountCheck.replaceAll("[ageOfRow]", "90");
const { data: prodLanes, error: pl } = await tryCatch(
query(ageQuery, "Get Stock lane date.")
);
@@ -39,23 +39,25 @@ export const getLanesToCycleCount = async () => {
warehouseName: prodLanes[i]?.warehouseName || "na",
Description: prodLanes[i]?.Description,
LastMoveDate: prodLanes[i]?.LastMoveDate
? format(prodLanes[i]?.LastInv, "M/d/yyyy")
? format(prodLanes[i]?.LastMoveDate, "M/d/yyyy")
: undefined,
LastInv: format(prodLanes[i]?.LastInv, "M/d/yyyy"),
rowType: prodLanes[i].rowType,
DaysSinceLast:
differenceInDays(
new Date(prodLanes[i].LastInv),
new Date(prodLanes[i].LastMoveDate)
) <= 0
? 0
: differenceInDays(
new Date(prodLanes[i].LastInv),
new Date(prodLanes[i].LastMoveDate)
),
DaysSinceLast: differenceInDays(
new Date(Date.now()),
new Date(prodLanes[i].LastInv)
),
upd_date: format(new Date(Date.now()), "M/d/yyyy"),
};
const existing = lanes.filter(
(l: any) => l.laneID === prodLanes[i]?.laneID
);
if (existing) {
lanes = lanes.filter((l: any) => l.laneID !== prodLanes[i]?.laneID);
}
lanes.push(createLane);
createLog(
"debug",

View File

@@ -0,0 +1,42 @@
import * as XLSX from "xlsx";
import { lanes } from "./cyclecountCheck.js";
export const lanesToExcel = async (age: string | null) => {
// Convert JSON data to an array of arrays (AOA)
let processLanes = lanes;
if (age) {
processLanes = lanes.filter(
(l: any) => l.DaysSinceLast >= parseInt(age)
);
}
const headers = Object.keys(processLanes[0]); // Get headers from JSON keys
const data = processLanes.map((item: any) =>
headers.map((header) => item[header])
);
// Create the workbook and worksheet
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.aoa_to_sheet([headers, ...data]); // Combine headers and data
// Auto-resize columns based on the longest content in each column
const colWidths = headers.map((_, colIndex) => {
let maxLength = 0;
data.forEach((row: any) => {
const cellValue = row[colIndex] ? row[colIndex].toString() : "";
maxLength = Math.max(maxLength, cellValue.length);
});
return { wch: maxLength + 2 }; // Add a little padding
});
ws["!cols"] = colWidths; // Set the column widths
// Add the worksheet to the workbook
XLSX.utils.book_append_sheet(wb, ws, "CycleCount");
// Write the workbook to a buffer and return it
const excelBuffer = XLSX.write(wb, { bookType: "xlsx", type: "buffer" });
return excelBuffer;
};

View File

@@ -1,28 +1,41 @@
import { lanes } from "./cyclecountCheck.js";
export const getCycleCountCheck = async (
age: number = 1000,
type: string = ""
) => {
export const getCycleCountCheck = async (age: number = 1000, type: any) => {
/**
* Get the lane data based on the age and type
*/
let filteredLanes = lanes.filter((t: any) => t.DaysSinceLast >= age);
let filteredLanes = lanes;
if (type === "empty") {
let empty = lanes.filter((t: any) => t.rowType === type.toUpperCase());
if (type != "") {
return {
sucess: true,
message: `${filteredLanes.length} lanes that are of type ${type} and have not been cycle counted in the last ${age} days.`,
data: filteredLanes.filter(
(t: any) => t.rowType === type.toUpperCase()
message: `${empty.length} lanes that are of type ${type}.`,
data: empty.sort(
(a: any, b: any) => b.DaysSinceLast - a.DaysSinceLast
),
};
} else {
}
if (type != "") {
let noType = lanes.filter((t: any) => t.DaysSinceLast >= age);
return {
success: true,
message: `${filteredLanes.length} lanes grabed that have not been cycle counted in the last ${age} days.`,
data: filteredLanes,
sucess: true,
message: `${noType.length} lanes that are of type ${type} and have not been cycle counted in the last ${age} days.`,
data: noType
.filter((t: any) => t.rowType === type?.toUpperCase())
.sort((a: any, b: any) => b.DaysSinceLast - a.DaysSinceLast),
};
}
return {
success: true,
message: `${filteredLanes.length} lanes grabed that have not been cycle counted in the last ${age} days.`,
data: filteredLanes.sort(
(a: any, b: any) => b.DaysSinceLast - a.DaysSinceLast
),
};
};

View File

@@ -10,6 +10,7 @@ import getSiloAdjustments from "./route/siloAdjustments/getSiloAdjustments.js";
import { getLanesToCycleCount } from "./controller/warehouse/cycleCountChecks/cyclecountCheck.js";
import getCycleCountCheck from "./route/getCycleCountChecks.js";
import getPPOO from "./route/getPPOO.js";
import getcyclecount from "./route/getCycleCountLanes.js";
const app = new OpenAPIHono();
@@ -26,6 +27,7 @@ const routes = [
getCycleCountCheck,
//warehouse
getPPOO,
getcyclecount,
] as const;
// app.route("/server", modules);

View File

@@ -37,7 +37,7 @@ app.openapi(
return c.json({ success: false, message: "Missing Data." });
}
const check: any = body;
const check: any = body ?? { age: 90, type: null };
const { data: lanes, error: le } = await tryCatch(
getCycleCountCheck(check.age, check.type)
);

View File

@@ -0,0 +1,66 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { lanesToExcel } from "../controller/warehouse/cycleCountChecks/exportCycleCountData.js";
import { format } from "date-fns";
const app = new OpenAPIHono();
// const Body = z
// .object({
// age: z.number().optional().openapi({ example: 90 }),
// //email: z.string().optional().openapi({example: "s.smith@example.com"}),
// type: z.string().optional().openapi({ example: "fg" }),
// })
// .openapi("User");
app.openapi(
createRoute({
tags: ["logistics"],
summary: "Returns the lanes that need to be counted",
method: "get",
path: "/getcyclecount",
// request: {
// body: {
// content: {
// "application/json": { schema: Body },
// },
// },
// },
// description:
// "Provided a running number and lot number you can consume material.",
responses: responses(),
}),
async (c: any) => {
//apiHit(c, { endpoint: "api/sqlProd/close" });
const defaultFilename = `cycleCount-${format(
new Date(Date.now()),
"M-d-yyyy"
)}.xlsx`;
const filename = c.req.query("filename") || defaultFilename;
const age = c.req.query("age") || null;
const { data, error } = await tryCatch(lanesToExcel(age));
if (error) {
return c.json({
success: false,
message: "Error getting lane data.",
data: error,
});
}
return new Response(data, {
headers: {
"Content-Type":
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Content-Disposition": `attachment; filename="${filename}"`,
},
});
// return c.json({
// success: data.success,
// message: data.message,
// data: data.data,
// });
}
);
export default app;

View File

@@ -0,0 +1,30 @@
import { and, desc, eq, gte, inArray, lte, sql } from "drizzle-orm";
import { db } from "../../../../database/dbclient.js";
import { logs } from "../../../../database/schema/logs.js";
import { createLog } from "../../logger/logger.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { notifications } from "../../../../database/schema/notifications.js";
export const getNotifications = async () => {
const { data, error } = await tryCatch(db.select().from(notifications));
if (error) {
createLog(
"error",
"notify",
"notify",
`Error getting notifications: ${error}`
);
return {
success: false,
message: "Error getting notifications.",
data: error,
};
}
return {
sucess: true,
message: "Current notifications.",
data: data,
};
};

View File

@@ -11,10 +11,11 @@ import { startNotificationMonitor } from "./utils/processNotifications.js";
import notifyStats from "./routes/getActiveNotifications.js";
import tiTrigger from "./routes/manualTiggerTi.js";
import blocking from "./routes/qualityBlocking.js";
import notify from "./routes/getNotifications.js";
const app = new OpenAPIHono();
const routes = [sendemail, notifyStats, tiTrigger, blocking] as const;
const routes = [sendemail, notifyStats, tiTrigger, blocking, notify] as const;
const appRoutes = routes.forEach((route) => {
app.route("/notify", route);

View File

@@ -0,0 +1,44 @@
// an external way to creating logs
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
//import { apiHit } from "../../../globalUtils/apiHits.js";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { getNotifications } from "../controller/getNotifications.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
const app = new OpenAPIHono({ strict: false });
app.openapi(
createRoute({
tags: ["notify"],
summary: "Gets notifications.",
method: "get",
path: "/notifications",
// description:
// "This might be a temp soltuin during the transtion between versions",
responses: responses(),
}),
async (c: any) => {
//apiHit(c, { endpoint: `api/logger/logs` });
const { data, error } = await tryCatch(getNotifications());
if (error) {
return c.json(
{
success: false,
message: "There was an error clearing the log.",
data: error,
},
400
);
}
return c.json(
{
success: data?.success,
message: data?.message,
data: data?.data,
},
200
);
}
);
export default app;

View File

@@ -130,7 +130,7 @@ export const createLabel = async (data: any, userPrinted: any) => {
// check if we can remove labels or not
//deleteLabels();
return { sucess: true, message: "Label created", data: returnData }; // returning label data to be able to book in if active
return { success: true, message: "Label created", data: returnData }; // returning label data to be able to book in if active
} catch (error) {
createLog(
"info",

View File

@@ -214,15 +214,15 @@ export const labelingProcess = async ({
// create the label
const label = await createLabel(filteredLot[0], userPrinted);
// if (!label.success) {
// createLog(
// "error",
// "labeling",
// "ocp",
// `There was an error creating the label: ${label.message}`
// );
// return { sucess: false, message: label.message, data: label.data };
// }
if (!label.success) {
createLog(
"error",
"labeling",
"ocp",
`There was an error creating the label: ${label.message}`
);
return { sucess: false, message: label.message, data: label.data };
}
// send over to be booked in if we can do it.
const bookin = settingData.filter((s) => s.name === "bookin");

View File

@@ -0,0 +1,20 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import { qualityRequest } from "../../../database/schema/qualityRequest.js";
import { db } from "../../../database/dbclient.js";
const app = new OpenAPIHono();
const routes = [] as const;
const appRoutes = routes.forEach((route) => {
app.route("/quality", route);
});
app.all("/quality/*", (c) => {
return c.json({
success: false,
message: "You have encounters a quality route that dose not exist.",
});
});
await db.select().from(qualityRequest);
export default app;

View File

@@ -1,10 +1,9 @@
export const deliveryByDateRange = `
use AlplaPROD_test1
DECLARE @StartDate DATE = ' [startDate] ' -- 2025-1-1
DECLARE @EndDate DATE = '[endDate]' -- 2025-1-31
select * from
(select (select wert from dbo.T_SystemParameter where Bezeichnung = 'Werkskuerzel') as Plant,
DECLARE @StartDate DATE = ' 1990-1-1 ' -- 2025-1-1
DECLARE @EndDate DATE = '2025-4-18' -- 2025-1-31
select * from
(select (select wert from dbo.T_SystemParameter where Bezeichnung = 'Werkskuerzel') as Plant,
AuftragsNummer as OrderNumber,
PositionsNummer as CustomerLineNumber,
AbrufNummer as CustomerReleaseNumber,
@@ -16,7 +15,7 @@ GelieferteMenge AS DeliveredQTY,
GelieferteMengeVPK as DeliverdPallets,
JournalNummer as BOLNum,
ProdArtikelBez AS ProductFamily,
dbo.V_LadePlanungenLadeAuftragAbruf.AbrufIdKundenAdresse AS IdCustomer,
dbo.V_LadePlanungenLadeAuftragAbruf.AbrufIdKundenAdresse AS IdCustomer,
dbo.V_LadePlanungenLadeAuftragAbruf.AdressBez AS CustName,
dbo.T_EAIJournal.IdJournalStatus as bolStatus,
V_TrackerAuftragsAbrufe.IdAuftragsAbruf as releaseNum,
@@ -24,20 +23,20 @@ V_LadePlanungenLadeAuftragAbruf.IdLadeAuftrag as truckPostion
,'Base Plant' as plantType
from dbo.V_TrackerAuftragsAbrufe (nolock)
left join
dbo.V_LadePlanungenLadeAuftragAbruf on V_TrackerAuftragsAbrufe.IdAuftragsAbruf =
left join
dbo.V_LadePlanungenLadeAuftragAbruf on V_TrackerAuftragsAbrufe.IdAuftragsAbruf =
dbo.V_LadePlanungenLadeAuftragAbruf.AbrufIdAuftragsAbruf
left join
dbo.T_EAIJournal on dbo.V_LadePlanungenLadeAuftragAbruf.IdLadeAuftrag =
dbo.T_EAIJournal.IdLadeAuftrag
left join
dbo.V_ArtikelKomplett on V_TrackerAuftragsAbrufe.IdArtikelVarianten =
dbo.T_EAIJournal on dbo.V_LadePlanungenLadeAuftragAbruf.IdLadeAuftrag =
dbo.T_EAIJournal.IdLadeAuftrag
left join
dbo.V_ArtikelKomplett on V_TrackerAuftragsAbrufe.IdArtikelVarianten =
dbo.V_ArtikelKomplett.IdArtikelvarianten
where GelieferteMengeVPK > 0 AND (
AbrufLiefertermin IS NULL
OR CONVERT(date, AbrufLiefertermin) BETWEEN @StartDate AND @EndDate
AbrufLiefertermin IS NULL
OR CONVERT(date, JournalDatum) BETWEEN @StartDate AND @EndDate
)
/*in house*/
@@ -69,5 +68,5 @@ where CONVERT(date, Upd_Date) BETWEEN @StartDate AND @EndDate
) x
order by DeliveryDate desc
order by Bol_PrintDate desc
`;