diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0218057..efce702 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", diff --git a/frontend/package.json b/frontend/package.json index d71f2c6..dafe2de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/components/dashboard/AddCards.tsx b/frontend/src/components/dashboard/AddCards.tsx index 73c7216..037603c 100644 --- a/frontend/src/components/dashboard/AddCards.tsx +++ b/frontend/src/components/dashboard/AddCards.tsx @@ -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 ( - + Cards - Please add a card below. + Manage Cards and there settings. - - + + + + + + + + {/* - + */} ); diff --git a/frontend/src/components/dashboard/Cards.tsx b/frontend/src/components/dashboard/Cards.tsx new file mode 100644 index 0000000..f2a639c --- /dev/null +++ b/frontend/src/components/dashboard/Cards.tsx @@ -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 ( +
+

{card.name}

+
{ + e.preventDefault(); + e.stopPropagation(); + }} + className="flex flex-row" + > + + // value.length > 3 + // ? undefined + // : "Username must be longer than 3 letters", + // }} + children={(field) => { + return ( +
+
+ +
+ + + field.handleChange(e) + } + /> +
+ ); + }} + /> + {!card.inventory && ( + <> + + // value.length > 3 + // ? undefined + // : "Username must be longer than 3 letters", + // }} + children={(field) => { + return ( +
+ + + field.handleChange( + e.target.value + ) + } + /> +
+ ); + }} + /> + + {/* {}}} + children={(field) => { + return ( +
+ + +
+ ); + }} + /> */} + + )} +
+ +
+ +
+ ); +} diff --git a/frontend/src/components/dashboard/DashBoard.tsx b/frontend/src/components/dashboard/DashBoard.tsx index 659f2ad..a0a6b38 100644 --- a/frontend/src/components/dashboard/DashBoard.tsx +++ b/frontend/src/components/dashboard/DashBoard.tsx @@ -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 ( -
-

Comming soon...

- {/* - - - -
- -
-
- - - - - - - - - - - - -
-
- - - - {server[0].value === "usday1vms006" && ( - - - - )} - {server[0].value === "usday1vms006" && ( - - - - )} +
+ {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 ( + //
+ //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ //
+ // + //
+ //
+ // ); } + +/* +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+*/ diff --git a/frontend/src/components/logistics/warehouse/CardSettings.tsx b/frontend/src/components/logistics/warehouse/CardSettings.tsx new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/logistics/warehouse/ExportInventoryData.tsx b/frontend/src/components/logistics/warehouse/ExportInventoryData.tsx new file mode 100644 index 0000000..5de04c1 --- /dev/null +++ b/frontend/src/components/logistics/warehouse/ExportInventoryData.tsx @@ -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 ( +
+ { + if (!open) { + form.reset(); + } + setOpen(isOpen); + // toast.message("Model was something", { + // description: isOpen ? "Modal is open" : "Modal is closed", + // }); + }} + > + + + + + + Export Inventory lane check + + Exports all lanes based on the age you enter, except + empty lanes. + + +
{ + e.preventDefault(); + e.stopPropagation(); + }} + > +
+ <> + + // value.length > 3 + // ? undefined + // : "Username must be longer than 3 letters", + // }} + children={(field) => { + return ( +
+ + + field.handleChange( + e.target.value + ) + } + /> +
+ ); + }} + /> + +
+ + +
+ + +
+
+
+
+
+
+ ); +} diff --git a/frontend/src/components/logistics/warehouse/InventoryCard.tsx b/frontend/src/components/logistics/warehouse/InventoryCard.tsx new file mode 100644 index 0000000..aa49287 --- /dev/null +++ b/frontend/src/components/logistics/warehouse/InventoryCard.tsx @@ -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
Loading inventory data...
; + if (isError) { + return ( +
+

There was an error getting the inv.

+
+ ); + } + 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 ; +} diff --git a/frontend/src/components/logistics/warehouse/PPOOCard.tsx b/frontend/src/components/logistics/warehouse/PPOOCard.tsx index 1ec5f61..bcb410c 100644 --- a/frontend/src/components/logistics/warehouse/PPOOCard.tsx +++ b/frontend/src/components/logistics/warehouse/PPOOCard.tsx @@ -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
Loading adjustmnet data...
; @@ -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 ( -
- - Loading PPOO...

}> -
-

- PPOO -

- - -
- -
-
-
+ ); + // return ( + //
+ // + // Loading PPOO...

}> + //
+ //

+ // PPOO + //

+ + // + //
+ // + //
+ //
+ //
+ // ); } diff --git a/frontend/src/lib/store/useCardStore.ts b/frontend/src/lib/store/useCardStore.ts index 9224b14..6987285 100644 --- a/frontend/src/lib/store/useCardStore.ts +++ b/frontend/src/lib/store/useCardStore.ts @@ -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) => void; - removeCard: (id: string) => void; + updateCard: (name: string, updatedCard: Partial) => void; + removeCard: (name: string) => void; } export const useCardStore = create()( @@ -37,16 +29,18 @@ export const useCardStore = create()( 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" } diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 1f613d0..748fe58 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -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; + } } diff --git a/frontend/src/utils/querys/logistics/getInventoryCheck.tsx b/frontend/src/utils/querys/logistics/getInventoryCheck.tsx new file mode 100644 index 0000000..1051619 --- /dev/null +++ b/frontend/src/utils/querys/logistics/getInventoryCheck.tsx @@ -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 ?? []; +}; diff --git a/frontend/src/utils/querys/logistics/getPPOO.tsx b/frontend/src/utils/querys/logistics/getPPOO.tsx index 1f63b5c..0d9a648 100644 --- a/frontend/src/utils/querys/logistics/getPPOO.tsx +++ b/frontend/src/utils/querys/logistics/getPPOO.tsx @@ -7,7 +7,7 @@ export function getPPOO() { queryFn: () => fetchStockSilo(), //enabled: staleTime: 1000, - refetchInterval: 2 * 2000, + refetchInterval: 60 * 1000, refetchOnWindowFocus: true, }); } diff --git a/frontend/src/utils/tableData/InventoryCards/inventoryColumns.tsx b/frontend/src/utils/tableData/InventoryCards/inventoryColumns.tsx new file mode 100644 index 0000000..844fa97 --- /dev/null +++ b/frontend/src/utils/tableData/InventoryCards/inventoryColumns.tsx @@ -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[] = [ + { + accessorKey: "Description", + header: () =>
Lane
, + }, + { + accessorKey: "DaysSinceLast", + header: "Age", + //enableSorting: true, + }, +]; diff --git a/frontend/src/utils/tableData/InventoryCards/inventoryData.tsx b/frontend/src/utils/tableData/InventoryCards/inventoryData.tsx new file mode 100644 index 0000000..31248a8 --- /dev/null +++ b/frontend/src/utils/tableData/InventoryCards/inventoryData.tsx @@ -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 { + columns: ColumnDef[]; + data: TData[]; + info: any; +} + +type ColumnSort = { + id: string; + desc: boolean; +}; +type SortingState = ColumnSort[]; + +export function InvTable({ + columns, + data, + info, +}: DataTableProps) { + const [sorting, setSorting] = useState([]); + 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 ( + +
+
+

+ {info.type} {data.length > 0 ? "lanes" : "lane"} older + than: {info.age}, needing to be completed +

+
+ + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column + .columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+ {/*
+ + +
*/} +
+ ); +} diff --git a/frontend/src/utils/tableData/notifications/notifyColumns.tsx b/frontend/src/utils/tableData/notifications/notifyColumns.tsx new file mode 100644 index 0000000..f640563 --- /dev/null +++ b/frontend/src/utils/tableData/notifications/notifyColumns.tsx @@ -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[] = [ + { + accessorKey: "name", + header: () =>
Name
, + }, + { + accessorKey: "description", + header: "Description", + cell: ({ row }) => { + return ( +
+

+ {row.getValue("description")} +

+
+ ); + }, + }, + + { + 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 ( +
{correctDate}
+ ); + } + }, + }, +]; diff --git a/frontend/src/utils/tableData/notifications/notifyData.tsx b/frontend/src/utils/tableData/notifications/notifyData.tsx new file mode 100644 index 0000000..6db143a --- /dev/null +++ b/frontend/src/utils/tableData/notifications/notifyData.tsx @@ -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 { + columns: ColumnDef[]; + data: TData[]; + //style: any; +} + +export function NotifyTable({ + columns, + data, + //style, +}: DataTableProps) { + 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 ( +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef + .header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+ + +
+
+ ); +} diff --git a/frontend/src/utils/tableData/ppoo/ppooData.tsx b/frontend/src/utils/tableData/ppoo/ppooData.tsx index 3534239..a96590c 100644 --- a/frontend/src/utils/tableData/ppoo/ppooData.tsx +++ b/frontend/src/utils/tableData/ppoo/ppooData.tsx @@ -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 { columns: ColumnDef[]; data: TData[]; - style: any; + //style: any; } export function PPOOTable({ columns, data, - style, + //style, }: DataTableProps) { + 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 ( -
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef - .header, - header.getContext() - )} - - ); - })} - - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext() - )} - - ))} +
+

PPOO Data

+ +
+ +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column + .columnDef.header, + header.getContext() + )} + + ); + })} - )) - ) : ( - - - No results. - - - )} - -
+ ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + + +
-
+
-
+ ); } diff --git a/server/services/logistics/controller/warehouse/cycleCountChecks/getCycleCountCheck.ts b/server/services/logistics/controller/warehouse/cycleCountChecks/getCycleCountCheck.ts index c3a9ba6..19e8eac 100644 --- a/server/services/logistics/controller/warehouse/cycleCountChecks/getCycleCountCheck.ts +++ b/server/services/logistics/controller/warehouse/cycleCountChecks/getCycleCountCheck.ts @@ -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 + ), + }; }; diff --git a/server/services/logistics/route/getCycleCountChecks.ts b/server/services/logistics/route/getCycleCountChecks.ts index ac53ea2..6d2db34 100644 --- a/server/services/logistics/route/getCycleCountChecks.ts +++ b/server/services/logistics/route/getCycleCountChecks.ts @@ -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) ); diff --git a/server/services/logistics/route/getCycleCountLanes.ts b/server/services/logistics/route/getCycleCountLanes.ts new file mode 100644 index 0000000..161f7e9 --- /dev/null +++ b/server/services/logistics/route/getCycleCountLanes.ts @@ -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;