feat(cards): migrated cards over

This commit is contained in:
2025-04-14 12:26:31 -05:00
parent 328b61f6cc
commit 087d14c585
21 changed files with 1318 additions and 334 deletions

View File

@@ -23,6 +23,7 @@
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.0.15", "@tailwindcss/vite": "^4.0.15",
"@tanstack/react-form": "^1.2.1", "@tanstack/react-form": "^1.2.1",
"@tanstack/react-query": "^5.69.0", "@tanstack/react-query": "^5.69.0",
@@ -37,6 +38,7 @@
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"lucide-react": "^0.483.0", "lucide-react": "^0.483.0",
"marked": "^15.0.8",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"npm-check-updates": "^17.1.16", "npm-check-updates": "^17.1.16",
"react": "^19.0.0", "react": "^19.0.0",
@@ -2937,6 +2939,21 @@
"node": ">= 10" "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": { "node_modules/@tailwindcss/vite": {
"version": "4.0.15", "version": "4.0.15",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.15.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.15.tgz",
@@ -4177,6 +4194,18 @@
"node": ">= 8" "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": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -5717,6 +5746,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT" "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": { "node_modules/lodash.includes": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -5757,7 +5792,6 @@
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.once": { "node_modules/lodash.once": {
@@ -5797,6 +5831,18 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" "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": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -6075,6 +6121,19 @@
"node": "^10 || ^12 || >=14" "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": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -7029,6 +7088,12 @@
"which-typed-array": "^1.1.2" "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": { "node_modules/victory-vendor": {
"version": "36.9.2", "version": "36.9.2",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",

View File

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

View File

@@ -3,55 +3,37 @@ import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import Cards from "./Cards";
import { useCardStore } from "@/lib/store/useCardStore"; //import { toast } from "sonner";
import { toast } from "sonner";
export function AddCards() { 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 ( return (
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline">Add Cards</Button> <Button variant="outline">Add Cards</Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-[425px]"> <DialogContent className="min-w-fit ">
<DialogHeader> <DialogHeader>
<DialogTitle>Cards</DialogTitle> <DialogTitle>Cards</DialogTitle>
<DialogDescription> <DialogDescription>
Please add a card below. Manage Cards and there settings.
</DialogDescription> </DialogDescription>
</DialogHeader> </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> <Button type="submit">Save changes</Button>
</DialogFooter> </DialogFooter> */}
</DialogContent> </DialogContent>
</Dialog> </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 @@
export default function DashBoard() { import { useCardStore } from "@/lib/store/useCardStore";
// const handleResizeStop = (newLayout: any, newItem: any) => { import INVCheckCard from "../logistics/warehouse/InventoryCard";
// updateCard( import PPOO from "../logistics/warehouse/PPOOCard";
// newItem.i,
// newLayout.filter((n: any) => n.i === newItem.i)[0]
// ); // Store the new layout in state
// };
// const handleDragStop = (newLayout: any, newItem: any) => { const componentsMap: any = {
// updateCard( ppoo: PPOO,
// newItem.i, inv: INVCheckCard,
// newLayout.filter((n: any) => n.i === newItem.i)[0] //QualityRequest,
// ); // Persist the updated layout with custom name };
// };
export default function DashBoard() {
const { cards } = useCardStore();
//console.log(cards);
return (
<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";
const Component = componentsMap[name.split("-")[0]];
return ( return (
<div className="ml-5 w-11/12 h-9/10"> <div key={a.name} className="col-span-3">
<p>Comming soon...</p> <Component age={a.age} type={a.rowType} />{" "}
{/* <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>
)}
<ResizableHandle />
<ResizablePanel>
<PrinterStatus />
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup> */}
</div> </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 { LstCard } from "@/components/extendedUI/LstCard";
import { useCardStore } from "@/lib/store/useCardStore";
import { getPPOO } from "@/utils/querys/logistics/getPPOO"; import { getPPOO } from "@/utils/querys/logistics/getPPOO";
import { columns } from "@/utils/tableData/ppoo/ppooColumns"; import { columns } from "@/utils/tableData/ppoo/ppooColumns";
import { PPOOTable } from "@/utils/tableData/ppoo/ppooData"; import { PPOOTable } from "@/utils/tableData/ppoo/ppooData";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { CircleX } from "lucide-react"; //import { CircleX } from "lucide-react";
import { Suspense } from "react"; //import { Suspense } from "react";
import { toast } from "sonner"; //import { toast } from "sonner";
export default function PPOO({ style = {} }) { export default function PPOO() {
const { removeCard } = useCardStore(); //{ style = {} }
const { data, isError, isLoading } = useQuery(getPPOO()); const { data, isError, isLoading } = useQuery(getPPOO());
if (isLoading) return <div>Loading adjustmnet data...</div>; if (isLoading) return <div>Loading adjustmnet data...</div>;
@@ -21,30 +20,42 @@ export default function PPOO({ style = {} }) {
); );
} }
const handleCloseCard = () => { // const handleCloseCard = () => {
removeCard("PPOO"); // //removeCard("PPOO");
// toast.success("card removed");
// };
toast.success("card removed");
};
return ( return (
<div style={style}> <PPOOTable
<LstCard style={style}> columns={columns}
<Suspense fallback={<p>Loading PPOO...</p>}> data={data}
<div className={`flex justify-center`}> //style={style}
<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>
); );
// 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 { create } from "zustand";
import { devtools, persist } from "zustand/middleware"; import { devtools, persist } from "zustand/middleware";
interface CardSettings { // interface CardSettings {
daysSinceLast?: number; // daysSinceLast?: number;
rowType?: string; // rowType?: string;
} // }
export interface Card { export interface Card {
i: string; name: string;
x: number; rowType: string;
y: number; age: string;
w: number; active: boolean;
h: number;
minW?: number;
maxW?: number;
minH?: number;
maxH?: number;
isResizable: boolean;
isDraggable: boolean;
cardSettings?: CardSettings;
} }
interface CardStore { interface CardStore {
cards: Card[]; // Array of card objects cards: Card[]; // Array of card objects
addCard: (card: Card) => void; addCard: (card: Card) => void;
updateCard: (id: string, updatedCard: Partial<Card>) => void; updateCard: (name: string, updatedCard: Partial<Card>) => void;
removeCard: (id: string) => void; removeCard: (name: string) => void;
} }
export const useCardStore = create<CardStore>()( export const useCardStore = create<CardStore>()(
@@ -37,16 +29,18 @@ export const useCardStore = create<CardStore>()(
addCard: (card) => addCard: (card) =>
set((state) => ({ cards: [...state.cards, card] })), set((state) => ({ cards: [...state.cards, card] })),
updateCard: (id, updatedCard) => updateCard: (name, updatedCard) =>
set((state) => ({ set((state) => ({
cards: state.cards.map((card) => cards: state.cards.map((card) =>
card.i === id ? { ...card, ...updatedCard } : card card.name === name
? { ...card, ...updatedCard }
: card
), ),
})), })),
removeCard: (id) => removeCard: (name) =>
set((state) => ({ set((state) => ({
cards: state.cards.filter((card) => card.i !== id), cards: state.cards.filter((card) => card.name !== name),
})), })),
}), }),
{ name: "card-storage" } { name: "card-storage" }

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(), queryFn: () => fetchStockSilo(),
//enabled: //enabled:
staleTime: 1000, staleTime: 1000,
refetchInterval: 2 * 2000, refetchInterval: 60 * 1000,
refetchOnWindowFocus: true, 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,41 +14,93 @@ import {
TableRow, TableRow,
} from "@/components/ui/table"; } from "@/components/ui/table";
import { Button } from "@/components/ui/button"; 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> { interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]; columns: ColumnDef<TData, TValue>[];
data: TData[]; data: TData[];
style: any; //style: any;
} }
export function PPOOTable<TData, TValue>({ export function PPOOTable<TData, TValue>({
columns, columns,
data, data,
style, //style,
}: DataTableProps<TData, TValue>) { }: DataTableProps<TData, TValue>) {
const [pagination, setPagination] = useState({
pageIndex: 0, //initial page index
pageSize: 5, //default page size
});
const table = useReactTable({ const table = useReactTable({
data, data,
columns, columns,
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(), getPaginationRowModel: getPaginationRowModel(),
onPaginationChange: setPagination,
state: {
//...
pagination,
},
}); });
console.log(parseInt(style.height.replace("px", "")) - 50); //console.log(parseInt(style.height.replace("px", "")) - 50);
return ( return (
<div <LstCard
style={{ className="p-3"
width: `${parseInt(style.width.replace("px", "")) - 50}px`, // style={{
height: `${parseInt(style.height.replace("px", "")) - 150}px`, // width: `${parseInt(style.width.replace("px", "")) - 50}px`,
cursor: "move", // height: `${parseInt(style.height.replace("px", "")) - 150}px`,
}} // cursor: "move",
// }}
//style={{ overflow: "auto" }}
> >
<div> <div>
<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 <Table
style={{ // style={{
width: `${parseInt(style.width.replace("px", "")) - 50}px`, // width: `${parseInt(style.width.replace("px", "")) - 50}px`,
height: `${parseInt(style.height.replace("px", "")) - 200}px`, // height: `${parseInt(style.height.replace("px", "")) - 200}px`,
cursor: "move", // cursor: "move",
}} // }}
> >
<TableHeader> <TableHeader>
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
@@ -59,8 +111,8 @@ export function PPOOTable<TData, TValue>({
{header.isPlaceholder {header.isPlaceholder
? null ? null
: flexRender( : flexRender(
header.column.columnDef header.column
.header, .columnDef.header,
header.getContext() header.getContext()
)} )}
</TableHead> </TableHead>
@@ -100,8 +152,9 @@ export function PPOOTable<TData, TValue>({
)} )}
</TableBody> </TableBody>
</Table> </Table>
</ScrollArea>
</div> </div>
<div className="flex items-center justify-end space-x-2 py-4"> <div className="flex items-center justify-end space-x-2">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@@ -119,6 +172,6 @@ export function PPOOTable<TData, TValue>({
Next Next
</Button> </Button>
</div> </div>
</div> </LstCard>
); );
} }

View File

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

View File

@@ -37,7 +37,7 @@ app.openapi(
return c.json({ success: false, message: "Missing Data." }); 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( const { data: lanes, error: le } = await tryCatch(
getCycleCountCheck(check.age, check.type) 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;