Compare commits

...

13 Commits

45 changed files with 3858 additions and 3936 deletions

View File

@@ -1,6 +1,14 @@
import {text, pgTable, timestamp, uuid, uniqueIndex, jsonb, integer} from "drizzle-orm/pg-core";
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {
text,
pgTable,
timestamp,
uuid,
uniqueIndex,
jsonb,
integer,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { z } from "zod";
export const rfidTags = pgTable(
"rfidTags",
@@ -23,8 +31,8 @@ export const rfidTags = pgTable(
);
// Schema for inserting a user - can be used to validate API requests
export const insertRolesSchema = createInsertSchema(rfidTags, {
tagHex: z.string().min(3, {message: "Tag Should have more than 3 characters"}),
});
// export const insertRolesSchema = createInsertSchema(rfidTags, {
// tagHex: z.string().min(3, {message: "Tag Should have more than 3 characters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectRolesSchema = createSelectSchema(rfidTags);

View File

@@ -1,5 +1,15 @@
import {boolean, date, jsonb, numeric, pgTable, text, timestamp, uniqueIndex, uuid} from "drizzle-orm/pg-core";
import {createSelectSchema} from "drizzle-zod";
import {
boolean,
date,
jsonb,
numeric,
pgTable,
text,
timestamp,
uniqueIndex,
uuid,
} from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod";
export const serverData = pgTable(
"serverData",
@@ -21,9 +31,13 @@ export const serverData = pgTable(
serverLoc: text("serverLoc"),
oldVersion: text("oldVersion"),
lastUpdated: timestamp("lastUpdated").defaultNow(),
shippingHours: text("shippingHours").default('[{"early": "06:30", "late": "23:00"}]'),
shippingHours: text("shippingHours").default(
'[{"early": "06:30", "late": "23:00"}]'
),
tiPostTime: text("tiPostTime").default('[{"from": "24", "to": "24"}]'),
otherSettings: jsonb("otherSettings").default([{specialInstructions: "something for ti", active: false}]),
otherSettings: jsonb("otherSettings").default([
{ specialInstructions: "something for ti", active: false },
]),
isUpgrading: boolean("isUpgrading").default(false),
},

View File

@@ -1,9 +1,18 @@
import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex} from "drizzle-orm/pg-core";
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {users} from "./users.js";
import {roles} from "./roles.js";
import {modules} from "./modules.js";
import {
text,
pgTable,
numeric,
index,
timestamp,
boolean,
uuid,
uniqueIndex,
} from "drizzle-orm/pg-core";
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
import { z } from "zod";
import { users } from "./users.js";
import { roles } from "./roles.js";
import { modules } from "./modules.js";
/*
we will add the user
@@ -17,14 +26,14 @@ export const userRoles = pgTable(
"userRoles",
{
user_id: uuid("user_id")
.notNull()
.references(() => users.user_id),
.notNull()
.references(() => users.user_id),
role_id: uuid("role_id")
.notNull()
.references(() => roles.role_id),
.notNull()
.references(() => roles.role_id),
module_id: uuid("module_id")
.notNull()
.references(() => modules.module_id),
.notNull()
.references(() => modules.module_id),
role: text("role").notNull(), // "view", "technician", "supervisor","manager", "admin", "systemAdmin"
add_User: text("add_User").default("LST_System").notNull(),
add_Date: timestamp("add_Date").defaultNow(),
@@ -33,13 +42,18 @@ export const userRoles = pgTable(
},
(table) => {
// ensures only one user gets permissions to one role
return [uniqueIndex("user_module_unique").on(table.user_id, table.module_id)];
return [
uniqueIndex("user_module_unique").on(
table.user_id,
table.module_id
),
];
}
);
// Schema for inserting a user - can be used to validate API requests
export const insertUserRolesSchema = createInsertSchema(userRoles, {
role: z.string().min(3, {message: "Role must be at least 3 characters"}),
});
// export const insertUserRolesSchema = createInsertSchema(userRoles, {
// role: z.string().min(3, {message: "Role must be at least 3 characters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectUserRolesSchema = createSelectSchema(userRoles);

View File

@@ -1,6 +1,15 @@
import {text, pgTable, numeric, index, timestamp, boolean, uuid, uniqueIndex} from "drizzle-orm/pg-core";
import {createInsertSchema, createSelectSchema} from "drizzle-zod";
import {z} from "zod";
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 users = pgTable(
"users",
@@ -27,10 +36,10 @@ export const users = pgTable(
);
// Schema for inserting a user - can be used to validate API requests
export const insertUsersSchema = createInsertSchema(users, {
username: z.string().min(3, {message: "Username must be at least 3 characters"}),
email: z.string().email({message: "Invalid email"}),
password: z.string().min(8, {message: "Password must be at least 8 characters"}),
});
// export const insertUsersSchema = createInsertSchema(users, {
// username: z.string().min(3, {message: "Username must be at least 3 characters"}),
// email: z.string().email({message: "Invalid email"}),
// password: z.string().min(8, {message: "Password must be at least 8 characters"}),
// });
// Schema for selecting a Expenses - can be used to validate API responses
export const selectUsersSchema = createSelectSchema(users);

File diff suppressed because it is too large Load Diff

View File

@@ -12,69 +12,72 @@
"checkupdates": "npm-check-updates"
},
"dependencies": {
"@hookform/resolvers": "^4.1.3",
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-collapsible": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8",
"@hookform/resolvers": "^5.1.1",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.2",
"@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.14",
"@radix-ui/react-scroll-area": "^1.2.9",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-tooltip": "^1.2.7",
"@react-pdf/renderer": "^4.3.0",
"@tailwindcss/vite": "^4.0.15",
"@tanstack/react-form": "^1.2.1",
"@tanstack/react-query": "^5.69.0",
"@tanstack/react-router": "^1.114.27",
"@tanstack/react-table": "^8.21.2",
"@tailwindcss/vite": "^4.1.10",
"@tanstack/react-form": "^1.12.3",
"@tanstack/react-query": "^5.81.2",
"@tanstack/react-router": "^1.121.34",
"@tanstack/react-table": "^8.21.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"dotenv": "^16.4.7",
"hono": "^4.7.5",
"dotenv": "^16.5.0",
"hono": "^4.8.2",
"js-cookie": "^3.0.5",
"jsbarcode": "^3.11.6",
"jsbarcode": "^3.12.1",
"jsonwebtoken": "^9.0.2",
"lucide-react": "^0.483.0",
"marked": "^15.0.8",
"lucide-react": "^0.522.0",
"marked": "^15.0.12",
"next-themes": "^0.4.6",
"npm-check-updates": "^17.1.16",
"react": "^19.0.0",
"npm-check-updates": "^18.0.1",
"react": "^19.1.0",
"react-barcode": "^1.6.1",
"react-day-picker": "^8.10.1",
"react-dom": "^19.0.0",
"react-day-picker": "^9.7.0",
"react-dom": "^19.1.0",
"react-grid-layout": "^1.5.1",
"react-hook-form": "^7.54.2",
"react-resizable-panels": "^2.1.7",
"react-hook-form": "^7.58.1",
"react-resizable-panels": "^3.0.3",
"recharts": "^2.15.2",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.15",
"sonner": "^2.0.5",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.10",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2",
"zustand": "^5.0.3"
"zod": "^3.25.67",
"zustand": "^5.0.5"
},
"devDependencies": {
"@eslint/js": "^9.23.0",
"@tanstack/router-devtools": "^1.114.27",
"@tanstack/router-plugin": "^1.114.27",
"@types/react": "^19.0.12",
"@types/react-dom": "^19.0.4",
"@eslint/js": "^9.29.0",
"@tanstack/router-devtools": "^1.121.34",
"@tanstack/router-plugin": "^1.121.34",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/react-grid-layout": "^1.3.5",
"@vitejs/plugin-react-swc": "^3.8.1",
"eslint": "^9.23.0",
"@vitejs/plugin-react-swc": "^3.10.2",
"eslint": "^9.29.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^16.0.0",
"typescript": "~5.8.2",
"typescript-eslint": "^8.27.0",
"vite": "^6.2.2"
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.2.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.35.0",
"vite": "^6.3.5"
},
"overrides": {
"react-is": "^19.0.0-rc-69d4b800-20241021"
}
}

View File

@@ -23,6 +23,7 @@ import { Button } from "@/components/ui/button";
import { getSettings } from "@/utils/querys/settings";
import { toast } from "sonner";
import axios from "axios";
import { useEffect } from "react";
//import { useState } from "react";
export type Servers = {
@@ -46,14 +47,35 @@ export default function ServerPage() {
getServers(token ?? "")
);
const adminModule = modules.filter((n) => n.name === "admin");
const userLevel =
user?.roles?.filter((r) => r.module_id === adminModule[0].module_id) ||
[];
// const adminModule = modules.filter((n) => n.name === "admin");
// const userLevel =
// user?.roles?.filter((r) => r.module_id === adminModule[0].module_id) ||
// [];
if (!adminModule[0]?.roles?.includes(userLevel[0]?.role)) {
router.navigate({ to: "/" });
}
// if (!adminModule[0]?.roles?.includes(userLevel[0]?.role)) {
// console.log("Something failed");
// //router.navigate({ to: "/" });
// }
useEffect(() => {
if (!user || modules.length === 0) return;
const adminModule = modules.find((n) => n.name === "admin");
if (!adminModule) {
console.log("no module loaded");
//router.navigate({ to: "/" });
return;
}
const userLevel =
user?.roles?.filter((r) => r.module_id === adminModule.module_id) ||
[];
if (!adminModule.roles?.includes(userLevel[0]?.role)) {
console.log("Something failed");
//router.navigate({ to: "/" });
}
}, [modules, user, router]);
if (isError) {
return <div>{JSON.stringify(error)}</div>;

View File

@@ -1,12 +1,19 @@
import {LstCard} from "@/components/extendedUI/LstCard";
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
import {useSessionStore} from "@/lib/store/sessionStore";
import {useModuleStore} from "@/lib/store/useModuleStore";
import {useQuery} from "@tanstack/react-query";
import {useRouter} from "@tanstack/react-router";
import {ChangeSetting} from "./SettingForm";
import {getSettings} from "@/utils/querys/settings";
import {Skeleton} from "@/components/ui/skeleton";
import { LstCard } from "@/components/extendedUI/LstCard";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { useSessionStore } from "@/lib/store/sessionStore";
import { useModuleStore } from "@/lib/store/useModuleStore";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "@tanstack/react-router";
import { ChangeSetting } from "./SettingForm";
import { getSettings } from "@/utils/querys/settings";
import { Skeleton } from "@/components/ui/skeleton";
export type Settings = {
settings_id?: string;
@@ -16,18 +23,22 @@ export type Settings = {
};
export default function SettingsPage() {
const {user, token} = useSessionStore();
const {modules} = useModuleStore();
const { user, token } = useSessionStore();
const { modules } = useModuleStore();
const router = useRouter();
const adminModule = modules.filter((n) => n.name === "admin");
const userLevel = user?.roles.filter((r) => r.module_id === adminModule[0].module_id) || [];
const userLevel =
user?.roles.filter((r) => r.module_id === adminModule[0].module_id) ||
[];
if (!adminModule[0].roles.includes(userLevel[0]?.role)) {
router.navigate({to: "/"});
router.navigate({ to: "/" });
}
const {data, isError, error, isLoading} = useQuery(getSettings(token ?? ""));
const { data, isError, error, isLoading } = useQuery(
getSettings(token ?? "")
);
// if (isLoading) {
// return <div>Loading.....</div>;
@@ -51,32 +62,38 @@ export default function SettingsPage() {
<>
<TableBody>
{Array(10)
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
.fill(0)
.map((_, i) => (
<TableRow key={i}>
<TableCell className="font-medium">
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
<TableCell>
<Skeleton className="h-4" />
</TableCell>
</TableRow>
))}
</TableBody>
</>
) : (
<TableBody>
{data?.map((setting: Settings) => (
<TableRow key={setting.settings_id}>
<TableCell className="font-medium">{setting.name}</TableCell>
<TableCell className="font-medium">{setting.value}</TableCell>
<TableCell className="font-medium">{setting.description}</TableCell>
<TableCell className="font-medium">
{setting.name}
</TableCell>
<TableCell className="font-medium">
{setting.value}
</TableCell>
<TableCell className="font-medium">
{setting.description}
</TableCell>
<TableCell className="font-medium">
<ChangeSetting setting={setting} />
</TableCell>

View File

@@ -1,25 +1,15 @@
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 { useAppForm } from "@/utils/formStuff";
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({
const form = useAppForm({
defaultValues: {
name: existing[0]?.name || card.name,
rowType: existing[0]?.type ?? card.rowType,
@@ -62,7 +52,7 @@ export default function Cards(card: any) {
}}
className="flex flex-row"
>
<form.Field
<form.AppField
name="active"
// validators={{
// // We can choose between form-wide and field-specific validators
@@ -95,83 +85,16 @@ export default function Cards(card: any) {
/>
{!card.inventory && (
<>
<form.Field
<form.AppField
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}
value={field.state.value}
type="number"
onChange={(e) =>
field.handleChange(
e.target.value
)
}
/>
</div>
);
}}
children={(field) => (
<field.InputField
label="Age"
inputType="number"
required={true}
/>
)}
/>
{/* <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">

View File

@@ -27,9 +27,10 @@ export default function DashBoard() {
</div>
);
} else {
//console.log(name.split("-")[0], a);
return (
<div key={a.name} className="col-span-3">
<Component age={a.age} type={a.rowType} />
<Component data={a} />
</div>
);
}

View File

@@ -106,6 +106,17 @@ export default function PreformReturn() {
{...register1("lotNum")}
/>
</div>
<div className="m-2">
<Label htmlFor="lotNum">
Select type of material coming back.
</Label>
<Input
className="mt-2"
//defaultValue="634"
type="number"
{...register1("lotNum")}
/>
</div>
<Button
className="m-2"

View File

@@ -0,0 +1,164 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { useAppForm } from "@/utils/formStuff";
import { getMachineConnected } from "@/utils/querys/logistics/machineConnected";
import { getMachineNotConnected } from "@/utils/querys/logistics/notConnected";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
export function AttachSilo(props: any) {
const [open, setOpen] = useState(false);
const { data, isError, isLoading, refetch } = useQuery(
getMachineNotConnected({
siloID: props.silo.LocationID,
connectionType: "detached",
})
);
const { refetch: attached } = useQuery(
getMachineConnected({
siloID: props.silo.LocationID,
connectionType: "connected",
})
);
const form = useAppForm({
defaultValues: {
laneId: props.silo.LocationID,
productionLotId: "",
machineId: "",
},
onSubmit: async ({ value }) => {
console.log(value);
try {
const res = await axios.post(
"/api/logistics/attachsilo",
value
);
if (res.data.success) {
toast.success(res.data.message);
refetch();
attached();
form.reset();
setOpen(!open);
} else {
console.log(res.data);
toast.error(res.data.message);
refetch();
form.reset();
setOpen(!open);
}
} catch (error) {
console.log(error);
toast.error(
"There was an error attaching the silo please try again, if persist please enter a helpdesk ticket."
);
}
},
});
if (isError)
return (
<div>
<p>There was an error loading data</p>
</div>
);
if (isLoading)
return (
<div>
<p>Loading....</p>
</div>
);
// convert the array that comes over to label and value
const tranMachine = data.map((i: any) => ({
value: i.machineId.toString(),
label: i.name,
}));
return (
<Dialog open={open}>
<DialogTrigger asChild>
<Button variant="outline" onClick={() => setOpen(!open)}>
Attach Silo
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<DialogHeader>
<DialogTitle>
Attach silo for: {props.silo.Description}
</DialogTitle>
<DialogDescription>
Enter the new lotnumber, select the machine you
would like to attach.
</DialogDescription>
</DialogHeader>
<div>
<p className="text-sm">
NOTE: If the machine you are trying to attach is not
showing in the drop down this means it is already
attached to this silo.
</p>
</div>
<div className="mt-3">
<form.AppField
name="productionLotId"
children={(field) => (
<field.InputField
label="Lot Number"
inputType="number"
required={true}
/>
)}
/>
</div>
<div className="mt-2">
<form.AppField
name="machineId"
children={(field) => (
<field.SelectField
label="Select Machine"
options={tranMachine}
/>
)}
/>
</div>
<DialogFooter className="mt-2">
<DialogClose asChild>
<Button
variant="outline"
onClick={() => setOpen(!open)}
>
Cancel
</Button>
</DialogClose>
<form.AppForm>
<form.SubmitButton>Attach</form.SubmitButton>
</form.AppForm>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,145 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { useAppForm } from "@/utils/formStuff";
import { getMachineConnected } from "@/utils/querys/logistics/machineConnected";
import { getMachineNotConnected } from "@/utils/querys/logistics/notConnected";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
export function DetachSilo(props: any) {
const [open, setOpen] = useState(false);
const { data, isError, isLoading, refetch } = useQuery(
getMachineConnected({
siloID: props.silo.LocationID,
connectionType: "connected",
})
);
const { refetch: notConnected } = useQuery(
getMachineNotConnected({
siloID: props.silo.LocationID,
connectionType: "detached",
})
);
const form = useAppForm({
defaultValues: {
laneId: props.silo.LocationID,
machineId: 0,
},
onSubmit: async ({ value }) => {
try {
const res = await axios.post(
"/api/logistics/detachsilo",
value
);
if (res.status === 200) {
toast.success(res.data.message);
refetch();
notConnected();
form.reset();
setOpen(!open);
} else {
console.log(res.data);
toast.error(res.data.message);
refetch();
form.reset();
setOpen(!open);
}
} catch (error) {
console.log(error);
toast.error(
"There was an error detaching the silo please try again, if persist please enter a helpdesk ticket."
);
}
},
});
if (isError)
return (
<div>
<p>There was an error loading data</p>
</div>
);
if (isLoading)
return (
<div>
<p>Loading....</p>
</div>
);
// convert the array that comes over to label and value
const tranMachine = data.map((i: any) => ({
value: i.machineId.toString(),
label: i.name,
}));
return (
<Dialog open={open}>
<DialogTrigger asChild>
<Button
variant="outline"
onClick={() => setOpen(!open)}
disabled={data.length === 0}
>
Detach Silo
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<DialogHeader>
<DialogTitle>
Attach silo for: {props.silo.Description}
</DialogTitle>
<DialogDescription>
Select the machine you would like to detach.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4">
<div className="grid gap-3">
<div className="mt-2">
<form.AppField
name="machineId"
children={(field) => (
<field.SelectField
label="Select Machine"
options={tranMachine}
/>
)}
/>
</div>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<form.AppForm>
<form.SubmitButton>Detach</form.SubmitButton>
</form.AppForm>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -19,6 +19,8 @@ import { CircleAlert } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import ChartData from "./ChartData";
import { AttachSilo } from "./AttachSilo";
import { DetachSilo } from "./DetachSilo";
export default function SiloCard(data: any) {
const token = localStorage.getItem("auth_token");
@@ -151,6 +153,7 @@ export default function SiloCard(data: any) {
/>
<Button
className="ml-1"
variant="outline"
type="submit"
onClick={
form.handleSubmit
@@ -188,13 +191,17 @@ export default function SiloCard(data: any) {
<div className="grow max-w-[600px]">
<ChartData laneId={silo.LocationID} />
<div className="flex justify-end m-1">
<Link
to={"/siloAdjustments/$hist"}
params={{ hist: silo.LocationID }}
>
Historical Data
</Link>
<div className="flex justify-end m-1 gap-3">
<AttachSilo silo={silo} />
<DetachSilo silo={silo} />
<Button variant="outline">
<Link
to={"/siloAdjustments/$hist"}
params={{ hist: silo.LocationID }}
>
Historical Data
</Link>
</Button>
</div>
</div>
</div>

View File

@@ -9,8 +9,12 @@ import { useQuery } from "@tanstack/react-query";
//import { toast } from "sonner";
export default function INVCheckCard(props: any) {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getinventoryCheck(props));
const { age, rowType } = props.data;
//console.log(props.data);
const { data, isError, isLoading } = useQuery(
getinventoryCheck({ age: age })
);
if (isLoading) return <div>Loading inventory data...</div>;
if (isError) {
@@ -23,11 +27,11 @@ export default function INVCheckCard(props: any) {
let laneData: any = data;
if (props.type != "") {
laneData = laneData.filter(
(l: any) => l.rowType === props.type.toUpperCase()
(l: any) => l.rowType === rowType.toUpperCase()
);
// age
laneData = laneData.filter((l: any) => l.DaysSinceLast >= props.age);
laneData = laneData.filter((l: any) => l.DaysSinceLast >= age);
}
// const handleCloseCard = () => {
@@ -36,5 +40,5 @@ export default function INVCheckCard(props: any) {
// toast.success("card removed");
// };
return <InvTable columns={invColumns} data={laneData} info={props} />;
return <InvTable columns={invColumns} data={laneData} info={props.data} />;
}

View File

@@ -15,6 +15,7 @@ import {
export default function OCPPage() {
const { settings } = useSettingStore();
if (settings.length === 0) return;
let server = settings.filter((n) => n.name === "server");
return (
@@ -73,7 +74,7 @@ export default function OCPPage() {
<WrapperManualTrigger />
</ResizablePanel>
)}
{server[0].value === "localhost" && (
{server[0]?.value === "localhost" && (
<ResizablePanel className="max-h-[300px]">
<WrapperManualTrigger />
</ResizablePanel>

View File

@@ -20,6 +20,7 @@ export const SessionProvider = ({
useEffect(() => {
fetchModules();
fetchSettings();
console.log("settings grab ran");
fetchUserRoles();
fetchSubModules();
}, []);

View File

@@ -1,73 +1,208 @@
import * as React from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { DayPicker } from "react-day-picker"
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
} from "lucide-react"
import { DayButton, DayPicker, getDefaultClassNames } from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { Button, buttonVariants } from "@/components/ui/button"
function Calendar({
className,
classNames,
showOutsideDays = true,
captionLayout = "label",
buttonVariant = "ghost",
formatters,
components,
...props
}: React.ComponentProps<typeof DayPicker>) {
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
}) {
const defaultClassNames = getDefaultClassNames()
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
className={cn(
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className
)}
captionLayout={captionLayout}
formatters={{
formatMonthDropdown: (date) =>
date.toLocaleString("default", { month: "short" }),
...formatters,
}}
classNames={{
months: "flex flex-col sm:flex-row gap-2",
month: "flex flex-col gap-4",
caption: "flex justify-center pt-1 relative items-center w-full",
caption_label: "text-sm font-medium",
nav: "flex items-center gap-1",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100"
root: cn("w-fit", defaultClassNames.root),
months: cn(
"flex gap-4 flex-col md:flex-row relative",
defaultClassNames.months
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-x-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
nav: cn(
"flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
defaultClassNames.nav
),
button_previous: cn(
buttonVariants({ variant: buttonVariant }),
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
defaultClassNames.button_previous
),
button_next: cn(
buttonVariants({ variant: buttonVariant }),
"size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
defaultClassNames.button_next
),
month_caption: cn(
"flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
defaultClassNames.month_caption
),
dropdowns: cn(
"w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
defaultClassNames.dropdowns
),
dropdown_root: cn(
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
defaultClassNames.dropdown_root
),
dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),
caption_label: cn(
"select-none font-medium",
captionLayout === "label"
? "text-sm"
: "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
defaultClassNames.caption_label
),
table: "w-full border-collapse",
weekdays: cn("flex", defaultClassNames.weekdays),
weekday: cn(
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
defaultClassNames.weekday
),
week: cn("flex w-full mt-2", defaultClassNames.week),
week_number_header: cn(
"select-none w-(--cell-size)",
defaultClassNames.week_number_header
),
week_number: cn(
"text-[0.8rem] select-none text-muted-foreground",
defaultClassNames.week_number
),
day: cn(
buttonVariants({ variant: "ghost" }),
"size-8 p-0 font-normal aria-selected:opacity-100"
"relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
defaultClassNames.day
),
day_range_start:
"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
day_range_end:
"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
range_start: cn(
"rounded-l-md bg-accent",
defaultClassNames.range_start
),
range_middle: cn("rounded-none", defaultClassNames.range_middle),
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
today: cn(
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
defaultClassNames.today
),
outside: cn(
"text-muted-foreground aria-selected:text-muted-foreground",
defaultClassNames.outside
),
disabled: cn(
"text-muted-foreground opacity-50",
defaultClassNames.disabled
),
hidden: cn("invisible", defaultClassNames.hidden),
...classNames,
}}
components={{
IconLeft: ({ className, ...props }) => (
<ChevronLeft className={cn("size-4", className)} {...props} />
),
IconRight: ({ className, ...props }) => (
<ChevronRight className={cn("size-4", className)} {...props} />
),
Root: ({ className, rootRef, ...props }) => {
return (
<div
data-slot="calendar"
ref={rootRef}
className={cn(className)}
{...props}
/>
)
},
Chevron: ({ className, orientation, ...props }) => {
if (orientation === "left") {
return (
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
)
}
if (orientation === "right") {
return (
<ChevronRightIcon
className={cn("size-4", className)}
{...props}
/>
)
}
return (
<ChevronDownIcon className={cn("size-4", className)} {...props} />
)
},
DayButton: CalendarDayButton,
WeekNumber: ({ children, ...props }) => {
return (
<td {...props}>
<div className="flex size-(--cell-size) items-center justify-center text-center">
{children}
</div>
</td>
)
},
...components,
}}
{...props}
/>
)
}
export { Calendar }
function CalendarDayButton({
className,
day,
modifiers,
...props
}: React.ComponentProps<typeof DayButton>) {
const defaultClassNames = getDefaultClassNames()
const ref = React.useRef<HTMLButtonElement>(null)
React.useEffect(() => {
if (modifiers.focused) ref.current?.focus()
}, [modifiers.focused])
return (
<Button
ref={ref}
variant="ghost"
size="icon"
data-day={day.date.toLocaleDateString()}
data-selected-single={
modifiers.selected &&
!modifiers.range_start &&
!modifiers.range_end &&
!modifiers.range_middle
}
data-range-start={modifiers.range_start}
data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle}
className={cn(
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
defaultClassNames.day,
className
)}
{...props}
/>
)
}
export { Calendar, CalendarDayButton }

View File

@@ -19,7 +19,10 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn("flex flex-col gap-1.5 px-6", className)}
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
)}
{...props}
/>
)
@@ -45,6 +48,19 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
)
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
)
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
@@ -59,10 +75,18 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6", className)}
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
)
}
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}

View File

@@ -28,7 +28,7 @@ function PopoverContent({
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-hidden",
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}

View File

@@ -1,9 +1,8 @@
import axios from "axios";
import {create} from "zustand";
import { create } from "zustand";
interface SettingState {
settings: any[];
fetchSettings: () => Promise<void>;
setSettings: (settings: any[]) => void;
}
@@ -13,17 +12,17 @@ interface FetchModulesResponse {
export const useSettingStore = create<SettingState>()((set) => ({
settings: [],
setSettings: (settings) => set({settings}),
setSettings: (settings) => set({ settings }),
fetchSettings: async () => {
try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const response = await axios.get(`/api/server/settings`, {});
const data: FetchModulesResponse = response.data; //await response.json();
//console.log(data);
set({settings: data.data});
set({ settings: data.data });
} catch (error) {
console.error("Failed to fetch settings:", error);
set({settings: []});
set({ settings: [] });
}
},
}));

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import ConsumeMaterial from "@/components/logistics/materialHelper/consumption/ConsumeMaterial";
import PreformReturn from "@/components/logistics/materialHelper/consumption/PreformReturn";
import PreformReturn from "@/components/logistics/materialHelper/consumption/MaterialReturn";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute(

View File

@@ -0,0 +1,15 @@
import { createFormHook, createFormHookContexts } from "@tanstack/react-form";
import { InputField } from "./options/InputField";
import { SubmitButton } from "./options/submitButton";
import { SelectField } from "./options/selectorField";
import { CheckboxField } from "./options/checkbox";
export const { fieldContext, useFieldContext, formContext, useFormContext } =
createFormHookContexts();
export const { useAppForm } = createFormHook({
fieldComponents: { InputField, SelectField, CheckboxField },
formComponents: { SubmitButton },
fieldContext,
formContext,
});

View File

@@ -0,0 +1,16 @@
import { AnyFieldMeta } from "@tanstack/react-form";
import { ZodError } from "zod";
type FieldErrorsProps = {
meta: AnyFieldMeta;
};
export const FieldErrors = ({ meta }: FieldErrorsProps) => {
if (!meta.isTouched) return null;
return meta.errors.map(({ message }: ZodError, index) => (
<p key={index} className="text-sm font-medium text-destructive">
{message}
</p>
));
};

View File

@@ -1,32 +1,28 @@
//import { Input } from "@/components/ui/input";
//import { Label } from "@radix-ui/react-dropdown-menu";
import { Label } from "@/components/ui/label";
import { useFieldContext } from "..";
import { Input } from "@/components/ui/input";
import { FieldErrors } from "./FieldErrors";
// export const FormInput = (form: any, label: string) => {
// // <form.Field
// // name="username"
// // 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">
// // <Label htmlFor="username">{label}</Label>
// // <Input
// // name={field.name}
// // value={field.state.value}
// // onBlur={field.handleBlur}
// // //type="number"
// // onChange={(e) => field.handleChange(e.target.value)}
// // />
// // {field.state.meta.errors.length ? (
// // <em>{field.state.meta.errors.join(",")}</em>
// // ) : null}
// // </div>
// // );
// // }}
// // />;
// };
type InputFieldProps = {
label: string;
inputType: string;
required: boolean;
};
export const InputField = ({ label, inputType, required }: InputFieldProps) => {
const field = useFieldContext<any>();
return (
<div className="grid gap-3">
<Label htmlFor={field.name}>{label}</Label>
<Input
id={field.name}
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
onBlur={field.handleBlur}
type={inputType}
required={required}
/>
<FieldErrors meta={field.state.meta} />
</div>
);
};

View File

@@ -0,0 +1,34 @@
import { Checkbox } from "@radix-ui/react-checkbox";
import { useFieldContext } from "..";
import { Label } from "@/components/ui/label";
import { FieldErrors } from "./FieldErrors";
type CheckboxFieldProps = {
label: string;
description?: string;
};
export const CheckboxField = ({ label }: CheckboxFieldProps) => {
const field = useFieldContext<boolean>();
return (
<div>
<div className="m-2 p-2 flex flex-row">
<div>
<Label htmlFor="active">
<span>{label}</span>
</Label>
</div>
<Checkbox
id={field.name}
checked={field.state.value}
onCheckedChange={(checked) => {
field.handleChange(checked === true);
}}
onBlur={field.handleBlur}
/>
</div>
<FieldErrors meta={field.state.meta} />
</div>
);
};

View File

@@ -0,0 +1,57 @@
import { Label } from "@/components/ui/label";
import { useFieldContext } from "..";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { FieldErrors } from "./FieldErrors";
type SelectOption = {
value: string;
label: string;
};
type SelectFieldProps = {
label: string;
options: SelectOption[];
placeholder?: string;
};
export const SelectField = ({
label,
options,
placeholder,
}: SelectFieldProps) => {
const field = useFieldContext<string>();
return (
<div className="grid gap-3">
<div className="grid gap-3">
<Label htmlFor={field.name}>{label}</Label>
<Select
value={field.state.value}
onValueChange={(value) => field.handleChange(value)}
>
<SelectTrigger
id={field.name}
onBlur={field.handleBlur}
className="w-[380px]"
>
<SelectValue placeholder={placeholder} />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<FieldErrors meta={field.state.meta} />
</div>
);
};

View File

@@ -0,0 +1,24 @@
import { useStore } from "@tanstack/react-form";
import { useFormContext } from "..";
import { Button } from "@/components/ui/button";
type SubmitButtonProps = {
children: React.ReactNode;
};
export const SubmitButton = ({ children }: SubmitButtonProps) => {
const form = useFormContext();
const [isSubmitting] = useStore(form.store, (state) => [
state.isSubmitting,
state.canSubmit,
]);
return (
<div className="">
<Button type="submit" disabled={isSubmitting}>
{children}
</Button>
</div>
);
};

View File

@@ -13,7 +13,7 @@ export function getinventoryCheck(data: any) {
}
const fetchStockSilo = async (info: any) => {
console.log(info);
//console.log("What tpye of info:", info);
const { data } = await axios.post(`/api/logistics/cyclecountcheck`, {
age: info.age ? parseInt(info.age) : null,
type: "",

View File

@@ -0,0 +1,23 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getMachineConnected(siloCon: any) {
return queryOptions({
queryKey: [`siloConnectionAttached-${siloCon.siloID}`],
queryFn: () => fetchStockSilo(siloCon),
//enabled:
//staleTime: 1000,
//refetchInterval: 60 * 1000,
refetchOnWindowFocus: true,
});
}
const fetchStockSilo = async (siloCon: any) => {
const { data } = await axios.post(`/api/logistics/siloconnection`, {
siloID: siloCon.siloID,
connectionType: siloCon.connectionType,
});
// 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,23 @@
import { queryOptions } from "@tanstack/react-query";
import axios from "axios";
export function getMachineNotConnected(siloCon: any) {
return queryOptions({
queryKey: [`siloConnectionNotConnected-${siloCon.siloID}`],
queryFn: () => fetchStockSilo(siloCon),
//enabled:
//staleTime: 1000,
//refetchInterval: 60 * 1000,
refetchOnWindowFocus: true,
});
}
const fetchStockSilo = async (siloCon: any) => {
const { data } = await axios.post(`/api/logistics/siloconnection`, {
siloID: siloCon.siloID,
connectionType: siloCon.connectionType,
});
// if we are not localhost ignore the devDir setting.
//const url: string = window.location.host.split(":")[0];
return data.data ?? [];
};

1639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -36,7 +36,7 @@
}
},
"admConfig": {
"build": 426,
"build": 429,
"oldBuild": "backend-0.1.3.zip"
},
"devDependencies": {
@@ -45,52 +45,52 @@
"@types/fs-extra": "^11.0.4",
"@types/js-cookie": "^3.0.6",
"@types/mssql": "^9.1.7",
"@types/node": "^22.13.11",
"@types/node": "^24.0.3",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.17",
"@types/pg": "^8.11.11",
"@types/ws": "^8.18.0",
"@types/pg": "^8.15.4",
"@types/ws": "^8.18.1",
"concurrently": "^9.1.2",
"cz-conventional-changelog": "^3.3.0",
"standard-version": "^9.5.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
"tsx": "^4.20.3",
"typescript": "^5.8.3"
},
"dependencies": {
"@dotenvx/dotenvx": "^1.39.0",
"@hono/node-server": "^1.14.0",
"@hono/zod-openapi": "^0.19.2",
"@scalar/hono-api-reference": "^0.7.2",
"@tanstack/react-form": "^1.2.1",
"@tanstack/react-table": "^8.21.2",
"@types/jsonwebtoken": "^9.0.9",
"@dotenvx/dotenvx": "^1.45.1",
"@hono/node-server": "^1.14.4",
"@hono/zod-openapi": "^0.19.8",
"@scalar/hono-api-reference": "^0.9.5",
"@tanstack/react-form": "^1.12.3",
"@tanstack/react-table": "^8.21.3",
"@types/jsonwebtoken": "^9.0.10",
"@types/nodemailer-express-handlebars": "^4.0.5",
"adm-zip": "^0.5.16",
"axios": "^1.8.4",
"axios": "^1.10.0",
"bcryptjs": "^3.0.2",
"croner": "^9.0.0",
"croner": "^9.1.0",
"date-fns": "^4.1.0",
"date-fns-tz": "^3.2.0",
"drizzle-kit": "^0.30.5",
"drizzle-orm": "^0.41.0",
"drizzle-zod": "^0.7.0",
"drizzle-kit": "^0.31.1",
"drizzle-orm": "^0.44.2",
"drizzle-zod": "^0.8.2",
"excel-date-to-js": "^1.1.5",
"fast-xml-parser": "^5.0.9",
"fast-xml-parser": "^5.2.5",
"fs-extra": "^11.3.0",
"jsonwebtoken": "^9.0.2",
"mssql": "^11.0.1",
"nodemailer": "^6.10.0",
"nodemailer": "^7.0.3",
"nodemailer-express-handlebars": "^7.0.0",
"pg": "^8.14.1",
"pino": "^9.6.0",
"pg": "^8.16.2",
"pino": "^9.7.0",
"pino-abstract-transport": "^2.0.0",
"pino-pretty": "^13.0.0",
"postgres": "^3.4.5",
"react-resizable-panels": "^2.1.7",
"postgres": "^3.4.7",
"react-resizable-panels": "^3.0.3",
"rimraf": "^6.0.1",
"st-ethernet-ip": "^2.7.3",
"ws": "^8.18.1",
"st-ethernet-ip": "^2.7.5",
"ws": "^8.18.2",
"xlsx": "^0.18.5",
"zod": "^3.24.2"
"zod": "^3.25.67"
}
}

View File

@@ -0,0 +1,92 @@
import axios from "axios";
import { prodEndpointCreation } from "./createUrl.js";
import { tryCatch } from "./tryCatch.js";
import { createLog } from "../services/logger/logger.js";
type bodyData = any;
type Data = {
endpoint: string;
data: bodyData[];
};
export const runProdApi = async (data: Data) => {
/**
* Detachs a silo
*/
let url = await prodEndpointCreation(data.endpoint);
const { data: d, error } = await tryCatch(
axios.post(url, data.data[0], {
headers: {
"X-API-Key": process.env.TEC_API_KEY || "",
"Content-Type": "application/json",
},
})
);
let e = error as any;
if (e) {
//console.log(e.response);
if (e.status === 401) {
createLog(
"error",
"lst",
"logistics",
`Not autorized: ${JSON.stringify(e.response?.data)}`
);
const data = {
success: false,
message: `Not autorized: ${JSON.stringify(e.response?.data)}`,
data: {
status: e.response?.status,
statusText: e.response?.statusText,
data: e.response?.data,
},
};
return data;
} else {
createLog(
"error",
"lst",
"logistics",
`There was an error processing the endpoint: ${JSON.stringify(
e.response?.data
)}`
);
return {
success: false,
message: `There was an error processing the endpoint: ${JSON.stringify(
e.response?.data
)}`,
data: {
status: e.response?.status,
statusText: e.response?.statusText,
data: e.response?.data,
},
};
}
}
if (d?.status !== 200) {
return {
success: false,
message: "Error processing endpoint",
data: {
status: d?.status,
statusText: d?.statusText,
data: d?.data,
},
};
} else {
return {
success: true,
message: "Endpoint was processed",
data: {
status: d.status,
statusText: d.statusText,
data: d.data,
},
};
}
};

View File

@@ -41,15 +41,21 @@ export const postAdjustment = async (data: any, prod: any) => {
const { data: silo, error } = await tryCatch(
axios.post(url, siloAdjustment, {
headers: { Authorization: `Basic ${prod}` },
headers: {
"X-API-Key": process.env.TEC_API_KEY || "",
"Content-Type": "application/json",
},
})
);
let e = error as any;
if (e) {
console.log(e.response);
if (e.status === 401) {
const data = {
success: false,
message: "Incorrect alpla prod password.",
message: `There was error posting the data: ${JSON.stringify(
e.response?.data
)}`,
data: {
status: e.response?.status,
statusText: e.response?.statusText,

View File

@@ -0,0 +1,43 @@
import { runProdApi } from "../../../../globalUtils/runProdApi.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
export const attachSilo = async (data: any) => {
/**
* Detachs a silo
*/
const detachData = {
endpoint: "/public/v1.0/IssueMaterial/AssignSiloToMachine",
data: [
{
laneId: data.laneId,
machineId: data.machineId,
productionLotId: data.productionLotId,
},
],
};
const { data: d, error } = await tryCatch(runProdApi(detachData));
if (error) {
return {
success: false,
message: "Error processing attachingSilo data",
data: error,
};
}
if (!d.success) {
return {
success: false,
message: "Error processing silo attach data",
data: d.message,
};
}
return {
success: true,
message: "silo attach was completed",
data: d.data,
};
};

View File

@@ -0,0 +1,42 @@
import { runProdApi } from "../../../../globalUtils/runProdApi.js";
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
export const detachSilo = async (data: any) => {
/**
* Detachs a silo
*/
const detachData = {
endpoint: "/public/v1.0/IssueMaterial/DetachSiloFromMachine",
data: [
{
laneId: data.laneId,
machineId: data.machineId,
},
],
};
const { data: d, error } = await tryCatch(runProdApi(detachData));
if (error) {
return {
success: false,
message: "Error processing detach data",
data: error,
};
}
if (!d.success) {
return {
success: false,
message: "Error processing detach data",
data: d.message,
};
}
return {
success: true,
message: "Detach was completed",
data: d.data,
};
};

View File

@@ -0,0 +1,63 @@
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
import { createLog } from "../../../logger/logger.js";
import { query } from "../../../sqlServer/prodSqlServer.js";
import {
connectedToMachine,
notconnectedToMachine,
} from "../../../sqlServer/querys/silo/connectionCheck.js";
type Data = {
siloID: string;
connectionType: string;
};
export const siloConnectionType = async (data: Data) => {
/**
* Will return the machines that are attached or detached based on the silo and connection type
*/
if (!data) {
return {
success: false,
message: "Missing mandatory data",
data: [{ error: "Missing siloId or ConnectionType" }],
};
}
// change the silo id to the correct one
let newQuery = "";
if (data.connectionType === "connected") {
newQuery = connectedToMachine.replace("[siloID]", data.siloID);
} else {
newQuery = notconnectedToMachine.replace("[siloID]", data.siloID);
}
/**
* get the silo data
*/
const { data: s, error } = (await tryCatch(
query(newQuery, "Silo connection check")
)) as any;
if (error) {
createLog(
"error",
"lst",
"logistics",
`There was an error getting the silo connection data: ${JSON.stringify(
error
)}`
);
return {
success: false,
message: "There was an error getting the silo connection data.",
data: error,
};
}
return {
success: true,
message: `silo ${data.connectionType} data`,
data: s.data,
};
};

View File

@@ -21,6 +21,9 @@ import intervalChecks from "./route/getActiveLogistics.js";
import getActiveLanes from "./route/getActiveLanes.js";
import removeAsNonReable from "./route/removeAsNonReusable.js";
import getSSCC from "./route/getSSCCNumber.js";
import getConnectionType from "./route/getSiloConnectionData.js";
import detachSilo from "./route/detachSilo.js";
import attachSilo from "./route/attachSilo.js";
const app = new OpenAPIHono();
@@ -33,6 +36,9 @@ const routes = [
postComment,
getStockSilo,
getSiloAdjustments,
getConnectionType,
detachSilo,
attachSilo,
//lanes
getCycleCountCheck,
//warehouse

View File

@@ -0,0 +1,65 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { attachSilo } from "../controller/siloAttachments/attachSilo.js";
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 all the silo connection based on connection type",
method: "post",
path: "/attachsilo",
// 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: "/attachSilo" });
const { data: body, error: bodyError } = await tryCatch(c.req.json());
if (bodyError) {
return {
success: false,
message: "Missing mandatory data",
data: [{ error: "Missing Data" }],
};
}
let b = body as any;
const { data: silo, error } = await tryCatch(attachSilo(b));
if (error) {
return c.json({
success: false,
message: "Error detaching silo.",
data: error,
});
}
return c.json({
success: silo.success,
message: silo.message,
data: silo.data,
});
}
);
export default app;

View File

@@ -0,0 +1,67 @@
import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi";
import { responses } from "../../../globalUtils/routeDefs/responses.js";
import { tryCatch } from "../../../globalUtils/tryCatch.js";
import { apiHit } from "../../../globalUtils/apiHits.js";
import { siloConnectionType } from "../controller/siloAttachments/siloConnectionData.js";
import { detachSilo } from "../controller/siloAttachments/detachSilo.js";
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 all the silo connection based on connection type",
method: "post",
path: "/detachsilo",
// 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: "/attachSilo" });
const { data: body, error: bodyError } = await tryCatch(c.req.json());
if (bodyError) {
return {
success: false,
message: "Missing mandatory data",
data: [{ error: "Missing Data" }],
};
}
let b = body as any;
const { data: silo, error } = await tryCatch(detachSilo(b));
if (error) {
return c.json({
success: false,
message: "Error detaching silo.",
data: error,
});
}
return c.json({
success: silo.success,
message: silo.message,
data: silo.data,
});
}
);
export default app;

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 { apiHit } from "../../../globalUtils/apiHits.js";
import { siloConnectionType } from "../controller/siloAttachments/siloConnectionData.js";
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 all the silo connection based on connection type",
method: "post",
path: "/siloconnection",
// 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: "/siloconnection" });
const { data: body, error: bodyError } = await tryCatch(c.req.json());
if (bodyError) {
return {
success: false,
message: "Missing mandatory data",
data: [{ error: "Missing Data" }],
};
}
let b = body as any;
const { data: silo, error } = await tryCatch(siloConnectionType(b));
if (error) {
return c.json({
success: false,
message: "Error getting silo connection data.",
data: error,
});
}
return c.json({
success: silo.success,
message: silo.message,
data: silo.data,
});
}
);
export default app;

View File

@@ -224,7 +224,7 @@
"active": true,
"serverLoc": "E:\\LST\\lstv2",
"oldVersion": "E:\\LST\\lst_backend",
"shippingHours": "[{\"early\": \"07:00\", \"late\": \"11:00\"}]",
"shippingHours": "[{\"early\": \"13:00\", \"late\": \"15:00\"}]",
"tiPostTime": "[{\"from\": \"24\", \"to\": \"24\"}]",
"otherSettings": [{ "specialInstructions": "" }]
},

View File

@@ -0,0 +1,42 @@
export const notconnectedToMachine = `
select distinct HumanReadableId as machineId
,Location as location
,name
--,[SiloHumanReadableId]
from [test1_AlplaPROD2.0_Read].[masterData].[Machine] (nolock) m
left join
[test1_AlplaPROD2.0_Read].[issueMaterial].[SiloAssignment] (nolock) s
on s.MachineId = m.id
where m.id not in (
SELECT
[MachineId]
FROM [test1_AlplaPROD2.0_Read].[issueMaterial].[SiloAssignment]
where [SiloHumanReadableId] = [siloID]
)
and name not like '%REWORK%'
`;
export const connectedToMachine = `
SELECT
[SiloHumanReadableId]
,[SiloDescription]
,[MaterialHumanReadableId]
,[MaterialDescription]
,[ConnectionDate]
,m.HumanReadableId as machineId
,m.Location as location
,m.Name as name
FROM [test1_AlplaPROD2.0_Read].[issueMaterial].[SiloAssignment] s
left join
[test1_AlplaPROD2.0_Read].[masterData].[Machine] m
on m.id = s.MachineId
where [SiloHumanReadableId] = [siloID]
`;