feat(frontend): migrated old > new silo adjustments

moved the apps around so we can use 1 url for cors bs
This commit is contained in:
2025-10-25 17:22:51 -05:00
parent d46ef922f3
commit 425f8f5f71
179 changed files with 7511 additions and 2724 deletions

View File

@@ -1,23 +0,0 @@
import { ReactNode } from "react";
import { Card } from "../ui/card";
interface LstCardProps {
children?: ReactNode;
className?: string;
style?: React.CSSProperties;
}
export function LstCard({
children,
className = "",
style = {},
}: LstCardProps) {
return (
<Card
className={`border-solid border-1 border-[#00659c] ${className}`}
style={style}
>
{children}
</Card>
);
}

View File

@@ -1,55 +0,0 @@
import { useSessionStore } from "../../lib/store/sessionStore";
import { useModuleStore } from "../../lib/store/useModuleStore";
import { moduleActive } from "../../utils/moduleActive";
import { hasAccess } from "../../utils/userAccess";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarTrigger,
} from "../ui/sidebar";
import { AdminSideBar } from "./side-components/admin";
import { EomSideBar } from "./side-components/eom";
import { ForkliftSideBar } from "./side-components/forklift";
import { Header } from "./side-components/header";
import { LogisticsSideBar } from "./side-components/logistics";
import { ProductionSideBar } from "./side-components/production";
import { QualitySideBar } from "./side-components/quality";
export function AppSidebar() {
const { user } = useSessionStore();
const { modules } = useModuleStore();
return (
<Sidebar collapsible="icon">
<SidebarContent>
<Header />
{moduleActive("production") && (
<ProductionSideBar
user={user}
moduleID={
modules.filter((n) => n.name === "production")[0]
.module_id as string
}
/>
)}
{moduleActive("logistics") && hasAccess(user, "logistics") && (
<LogisticsSideBar user={user} />
)}
{moduleActive("forklift") && hasAccess(user, "forklift") && (
<ForkliftSideBar />
)}
{moduleActive("eom") && hasAccess(user, "eom") && <EomSideBar />}
{moduleActive("quality") && hasAccess(user, "quality") && (
<QualitySideBar />
)}
{moduleActive("admin") && hasAccess(user || [], "admin") && (
<AdminSideBar />
)}
</SidebarContent>
<SidebarFooter>
<SidebarTrigger />
</SidebarFooter>
</Sidebar>
);
}

View File

@@ -1,26 +0,0 @@
import {Moon, Sun} from "lucide-react";
import {Button} from "../ui/button";
import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "../ui/dropdown-menu";
import {useTheme} from "../layout/theme-provider";
export function ModeToggle() {
const {setTheme} = useTheme();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@@ -1,212 +0,0 @@
import {
AlignJustify,
Atom,
Logs,
Minus,
Plus,
Server,
Settings,
ShieldCheck,
Users,
Webhook,
} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "../../ui/sidebar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "../../ui/collapsible";
import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
import { useSettingStore } from "@/lib/store/useSettings";
const iconMap: any = {
ShieldCheck: ShieldCheck,
AlignJustify: AlignJustify,
Settings: Settings,
Atom: Atom,
Logs: Logs,
Users: Users,
Webhook: Webhook,
Server: Server,
};
export function AdminSideBar() {
const { subModules } = useSubModuleStore();
const { settings } = useSettingStore();
const plantToken = settings.filter((n) => n.name === "plantToken");
const items = subModules.filter((m) => m.moduleName === "admin");
return (
<SidebarGroup>
<SidebarGroupLabel>Admin section</SidebarGroupLabel>
<SidebarGroupContent>
{items.map((item: any, index) => {
const Icon = iconMap[item.icon] || AlignJustify;
// drop down menu setup
return (
<SidebarMenu key={item.name}>
{item.link === "#" ? (
<Collapsible
key={item.name}
defaultOpen={index === 1}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<Icon />
{item.name}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.subSubModule?.length > 0 ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.subSubModule.map(
(i: any) => {
const SubIcon =
iconMap[
i.icon
] ||
AlignJustify;
return (
<SidebarMenuSubItem
key={i.name}
>
{i.isActive && (
<SidebarMenuSubButton
asChild
>
<a
href={
i.name ===
"Swagger"
? `https://${plantToken[0].value}prod.alpla.net/application/swagger/index.html`
: i.link
}
target={
i.newWindow
? "_blank"
: "_self"
}
>
<SubIcon />
<span>
{
i.name
}
</span>
</a>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem>
);
}
)}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
) : (
<SidebarMenu>
{items.map((item) => {
if (item.link === "#") return;
return (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton asChild>
<a href={item.link}>
<Icon />
<span>{item.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
);
})}
</SidebarMenu>
)}
</SidebarMenu>
);
})}
</SidebarGroupContent>
</SidebarGroup>
);
}
{
/* <SidebarMenu>
{data.navMain.map((item, index) => (
<Collapsible
key={item.title}
defaultOpen={index === 1}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<item.icon />
{item.title}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem
key={item.title}
>
{item.isActive && (
<SidebarMenuSubButton
asChild
>
<a
href={item.url}
target={
item.newWindow
? "_blank"
: "_self"
}
>
<item.icon />
<span>
{item.title}
</span>
</a>
</SidebarMenuSubButton>
)}
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu> */
}

View File

@@ -1,39 +0,0 @@
import {FileText} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
const items = [
{
title: "End Of Month",
url: "/eom",
icon: FileText,
},
];
export function EomSideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>End of month</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -1,106 +0,0 @@
import {Forklift, Hourglass, Minus, Plus, Signature} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "../../ui/sidebar";
import {Collapsible, CollapsibleTrigger} from "../../ui/collapsible";
import {CollapsibleContent} from "@radix-ui/react-collapsible";
const items = [
{
title: "Gemone",
url: "#",
icon: Forklift,
isActive: false,
},
];
const data = {
navMain: [
{
title: "Forklift Management",
url: "#",
icon: Forklift,
items: [
{
title: "All Forklifts",
url: "#",
icon: Forklift,
},
{
title: "Leasing data",
url: "#",
isActive: false,
icon: Signature,
},
{
title: "Forklift Hours",
url: "#",
isActive: false,
icon: Hourglass,
},
],
},
],
};
export function ForkliftSideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>Forklift Section</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{data.navMain.map((item, index) => (
<Collapsible key={item.title} defaultOpen={index === 1} className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<item.icon />
{item.title}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem key={item.title}>
<SidebarMenuSubButton asChild isActive={item.isActive}>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -1,36 +0,0 @@
import { Link } from "@tanstack/react-router";
import {
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
export function Header() {
return (
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<Link to="/">
<SidebarMenuButton size="lg" asChild>
<div className="flex flex-row">
<img
src={"imgs/dkLst.png"}
alt="Description"
className="size-8"
/>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">
Logistics Support Tool
</span>
<span className="">v2.0.0</span>
</div>
</div>
</SidebarMenuButton>
</Link>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
);
}

View File

@@ -1,53 +0,0 @@
import { Barcode, Command, Cylinder, Package, Truck } from "lucide-react";
import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
import { User } from "@/types/users";
import { hasPageAccess } from "@/utils/userAccess";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
const iconMap: any = {
Package: Package,
Truck: Truck,
Cylinder: Cylinder,
Barcode: Barcode,
Command: Command,
};
export function LogisticsSideBar({ user }: { user: User | null }) {
const { subModules } = useSubModuleStore();
const items = subModules.filter((m) => m.moduleName === "logistics");
return (
<SidebarGroup>
<SidebarGroupLabel>Logistics</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => {
const Icon = iconMap[item.icon];
return (
<SidebarMenuItem key={item.submodule_id}>
<>
{hasPageAccess(user, item.roles, item.name) && (
<SidebarMenuButton asChild>
<a href={item.link}>
<Icon />
<span>{item.name}</span>
</a>
</SidebarMenuButton>
)}
</>
</SidebarMenuItem>
);
})}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -1,64 +0,0 @@
import { Printer, Tag } from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
import { hasPageAccess } from "@/utils/userAccess";
import { User } from "@/types/users";
export function ProductionSideBar({
user,
moduleID,
}: {
user: User | null;
moduleID: string;
}) {
const url: string = window.location.host.split(":")[0];
const items = [
{
title: "One Click Print",
url: "/ocp",
icon: Printer,
role: ["viewer"],
module: "ocp",
active: true,
},
{
title: "Rfid Readers",
url: "/rfid",
icon: Tag,
role: ["viewer"],
module: "production",
active:
url === "usday1vms006" || url === "localhost" ? true : false,
},
];
return (
<SidebarGroup>
<SidebarGroupLabel>Production</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<>
{hasPageAccess(user, item.role, moduleID) &&
item.active && (
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
)}
</>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -1,39 +0,0 @@
import {Printer} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "../../ui/sidebar";
const items = [
{
title: "Qaulity Request",
url: "#",
icon: Printer,
},
];
export function QualitySideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>Quality</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
);
}

View File

@@ -1,67 +0,0 @@
import {createContext, useContext, useEffect, useState} from "react";
type Theme = "dark" | "light" | "system";
type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
};
type ThemeProviderState = {
theme: Theme;
setTheme: (theme: Theme) => void;
};
const initialState: ThemeProviderState = {
theme: "system",
setTheme: () => null,
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || defaultTheme);
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
root.classList.add(systemTheme);
return;
}
root.classList.add(theme);
}, [theme]);
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider");
return context;
};

View File

@@ -1,163 +0,0 @@
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 }) => {
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

@@ -1,113 +0,0 @@
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import { CardContent } from "@/components/ui/card";
import {
ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart";
import { useQuery } from "@tanstack/react-query";
import { getAdjustments } from "@/utils/querys/logistics/siloAdjustments/getAdjustments";
import { LstCard } from "@/components/extendedUI/LstCard";
import { format } from "date-fns";
export default function ChartData(props: any) {
const { data, isError, isLoading } = useQuery(getAdjustments());
const chartConfig = {
stock: {
label: "Stock",
color: "rgb(255, 99, 132)",
},
actual: {
label: "Actual",
color: "rgb(53, 162, 235)",
},
} satisfies ChartConfig;
if (isLoading) return <div>Loading chart data</div>;
if (isError) return <div>Error in loading chart data</div>;
let adjustments: any = data.filter(
(l: any) => l.locationID === props.laneId
);
adjustments = adjustments.splice(0, 10).map((s: any) => {
return {
date: format(s.dateAdjusted.replace("Z", ""), "M/d/yyyy hh:mm"),
stock: s.currentStockLevel,
actual: s.newLevel,
};
});
return (
<LstCard className="w-[425px] h-[250px] m-1">
<CardContent>
{adjustments.length === 0 ? (
<span>No silo data has been entered for this silo.</span>
) : (
<ChartContainer config={chartConfig}>
<AreaChart
accessibilityLayer
data={adjustments}
margin={{
left: 35,
right: 45,
bottom: 50,
}}
>
<CartesianGrid vertical={false} />
<XAxis
dataKey="date"
tickLine={false}
axisLine={false}
tickMargin={14}
angle={-45}
textAnchor="end"
dy={10}
tickFormatter={(value) =>
format(value, "M/d/yyyy")
}
/>
<ChartTooltip
cursor={false}
content={
<ChartTooltipContent indicator="dot" />
}
/>
<Area
dataKey="stock"
type="natural"
fill="rgba(53, 162, 235, 0.5)"
fillOpacity={0.4}
stroke="rgba(53, 162, 235, 0.5)"
stackId="a"
/>
<Area
dataKey="actual"
type="natural"
fill="rgba(255, 99, 132, 0.5)"
fillOpacity={0.4}
stroke="rgba(255, 99, 132, 0.5)"
stackId="a"
/>
</AreaChart>
</ChartContainer>
)}
</CardContent>
{/* <CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none">
Trending up by 5.2% this month{" "}
<TrendingUp className="h-4 w-4" />
</div>
<div className="flex items-center gap-2 leading-none text-muted-foreground">
January - June 2024
</div>
</div>
</div>
</CardFooter> */}
</LstCard>
);
}

View File

@@ -1,120 +0,0 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { Button } from "@/components/ui/button";
import { CardContent, CardFooter, CardHeader } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { useForm } from "@tanstack/react-form";
import { useRouter } from "@tanstack/react-router";
import axios from "axios";
import { useState } from "react";
import { toast } from "sonner";
export default function Comment(data: any) {
const token = localStorage.getItem("auth_token");
const [isSubmitting, setIsSubmitting] = useState(false);
const router = useRouter();
const form = useForm({
defaultValues: {
comment: "",
},
onSubmit: async ({ value }) => {
setIsSubmitting(true);
try {
const res = await axios.post(
`/api/logistics/postcomment/${data.id.split("&")[0]}`,
{
comment: value.comment,
key: data.id.split("&")[1],
},
{ headers: { Authorization: `Bearer ${token}` } }
);
if (res.data.success) {
toast.success(res.data.message);
form.reset();
router.navigate({ to: "/siloAdjustments" });
}
if (!res.data.success) {
toast.error(res.data.message);
form.reset();
}
} catch (error) {
console.log(error);
toast.error(`There was an error posting your comment.`);
}
setIsSubmitting(false);
},
});
return (
<div className="">
<LstCard>
<CardHeader>
Please enter your comment for the silo adjust
</CardHeader>
<CardContent>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field
name="comment"
validators={{
// We can choose between form-wide and field-specific validators
onChange: ({ value }) =>
value.length > 10
? undefined
: "Comment must be longer than 10 characters.",
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label
htmlFor="comment"
className="mb-2"
>
Comment
</Label>
<Textarea
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>
);
}}
/>
</form>
</CardContent>
<CardFooter>
<div className="flex justify-end">
<Button
onClick={form.handleSubmit}
disabled={isSubmitting}
>
Submit
</Button>
</div>
</CardFooter>
</LstCard>
</div>
);
}

View File

@@ -1,151 +0,0 @@
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) {
console.log(res.data.data);
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"
onClick={() => setOpen(!open)}
>
Cancel
</Button>
</DialogClose>
<form.AppForm>
<form.SubmitButton>Detach</form.SubmitButton>
</form.AppForm>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -1,26 +0,0 @@
import { getAdjustments } from "@/utils/querys/logistics/siloAdjustments/getAdjustments";
import { columns } from "@/utils/tableData/siloAdjustmentHist/siloAdjHistColumns";
import { SiloTable } from "@/utils/tableData/siloAdjustmentHist/siloData";
import { useQuery } from "@tanstack/react-query";
export default function HistoricalData(props: any) {
const { data, isError, isLoading } = useQuery(getAdjustments());
if (isLoading) return <div>Loading adjustmnet data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the adjustments.</p>
</div>
);
}
//console.log(data[0].locationID, parseInt(props.laneId));
const adjustments: any = data.filter(
(l: any) => l.locationID === parseInt(props.laneId)
);
return (
<div className="container mx-auto py-10">
<SiloTable columns={columns} data={adjustments} />
</div>
);
}

View File

@@ -1,227 +0,0 @@
import { useForm } from "@tanstack/react-form";
import { useQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import axios from "axios";
import { format } from "date-fns";
import { CircleAlert } from "lucide-react";
import { useState } from "react";
import { toast } from "sonner";
import { LstCard } from "@/components/extendedUI/LstCard";
import { Button } from "@/components/ui/button";
import { CardHeader } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useSessionStore } from "@/lib/store/sessionStore";
import { useGetUserRoles } from "@/lib/store/useGetRoles";
import { useModuleStore } from "@/lib/store/useModuleStore";
import { getStockSilo } from "@/utils/querys/logistics/siloAdjustments/getStockSilo";
import { AttachSilo } from "./AttachSilo";
import ChartData from "./ChartData";
import { DetachSilo } from "./DetachSilo";
export default function SiloCard(data: any) {
const [submitting, setSubmitting] = useState(false);
const { refetch } = useQuery(getStockSilo());
const { user } = useSessionStore();
const { userRoles } = useGetUserRoles();
const { modules } = useModuleStore();
const silo = data.silo;
// roles that can do the silo adjustments
const roles = ["systemAdmin", "technician", "admin", "manager"];
const module = modules.filter((n) => n.name === "logistics");
const accessRoles = userRoles.filter(
(n: any) => n.module === module[0]?.name,
) as any;
const form = useForm({
defaultValues: {
newLevel: "",
},
onSubmit: async ({ value }) => {
setSubmitting(true);
const dataToSubmit = {
quantity: parseFloat(value.newLevel),
warehouseId: silo.WarehouseID,
laneId: silo.LocationID,
};
try {
const res = await axios.post(
"/api/logistics/createsiloadjustment",
dataToSubmit,
{ withCredentials: true },
);
//console.log(res.data);
if (res.data.success) {
toast.success(res.data.message);
refetch();
form.reset();
}
if (!res.data.success && res.data.data?.status === 400) {
if (res.data.data.status === 400) {
toast.error(res.data.data.data.errors[0].message);
}
} else if (!res.data.success) {
toast.error(res.data.message);
}
setSubmitting(false);
} catch (error: any) {
//console.log(error);
if (error.status === 401) {
toast.error(error.response.statusText);
setSubmitting(false);
}
}
},
});
return (
<LstCard>
<div className="flex flex-row">
<LstCard className="grow m-1 max-w-[400px]">
<CardHeader>{silo.Description}</CardHeader>
<div className="m-1">
<hr className="m-2" />
<span>Current Stock: </span>
{silo.Stock_Total}
<hr className="m-2" />
<span>Last date adjusted </span>
{format(silo.LastAdjustment, "M/dd/yyyy")}
<hr className="m-2" />
</div>
<div>
{silo.Stock_Total === 0 ? (
<div className="flex justify-center flex-col">
<span>
The silo is currently empty you will not be able to do an
adjustment until you have received material in.
</span>
<hr />
<ul>
<li>
-Someone click "Take inventory on a empty location" in
stock.
</li>
<li>
-Silo virtualy ran empty due to production over consumption.
</li>
<li>
-Someone forgot to move a railcar compartment over to this
location.
</li>
</ul>
</div>
) : (
<>
{user && roles.includes(accessRoles[0]?.role) && (
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<form.Field
name="newLevel"
validators={{
// We can choose between form-wide and field-specific validators
onChange: ({ value }) =>
value.length > 1
? undefined
: "You must enter a value greate than 1",
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<div className="flex flex-row">
<Label htmlFor="newLevel">New level</Label>
<div>
<Disclaimer />
</div>
</div>
<div className="flex flex-row">
<Input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
type="decimal"
onChange={(e) =>
field.handleChange(e.target.value)
}
/>
<Button
className="ml-1"
variant="outline"
type="submit"
onClick={form.handleSubmit}
disabled={submitting}
>
{submitting ? (
<span className="w-24">Submitting...</span>
) : (
<span className="w-24">Submit</span>
)}
</Button>
</div>
{field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(",")}</em>
) : null}
</div>
);
}}
/>
</form>
)}
</>
)}
</div>
</LstCard>
<div className="grow max-w-[600px]">
<ChartData laneId={silo.LocationID} />
<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>
</LstCard>
);
}
const Disclaimer = () => {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<CircleAlert className="ml-1 w-[14px]" />
</TooltipTrigger>
<TooltipContent className="max-w-48">
<p className="text-pretty">
If you have had this page open for a period of time before
submitting your data, there is a chance that the stock levels will
be different from the ones you see above
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
};

View File

@@ -1,30 +0,0 @@
import { getStockSilo } from "@/utils/querys/logistics/siloAdjustments/getStockSilo";
import { useQuery } from "@tanstack/react-query";
import SiloCard from "./SiloCard";
export default function SiloPage() {
const { data, isError, error, isLoading } = useQuery(getStockSilo());
if (isLoading) return;
if (isError) return;
if (error)
return (
<div>
{" "}
There was an error getting the silos please notify your admin if
this continues to be an issue
</div>
);
return (
<div className="flex flex-wrap">
{data?.map((s: any) => (
<div key={s.LocationID} className="grow m-2 max-w-[800px]">
<SiloCard silo={s} />
</div>
))}
</div>
);
}

View File

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

View File

@@ -1,44 +0,0 @@
//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) {
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) {
return (
<div>
<p>There was an error getting the inv.</p>
</div>
);
}
let laneData: any = data;
if (props.type != "") {
laneData = laneData.filter(
(l: any) => l.rowType === rowType.toUpperCase()
);
// age
laneData = laneData.filter((l: any) => l.DaysSinceLast >= age);
}
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return <InvTable columns={invColumns} data={laneData} info={props.data} />;
}

View File

@@ -1,61 +0,0 @@
//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";
export default function PPOO() {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getPPOO());
if (isLoading) return <div>Loading adjustmnet data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the adjustments.</p>
</div>
);
}
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return (
<PPOOTable
columns={columns}
data={data}
//style={style}
/>
);
// return (
// <div style={style}>
// <LstCard style={style}>
// <Suspense fallback={<p>Loading PPOO...</p>}>
// <div className={`flex justify-center`}>
// <p
// className={`drag-handle w-fit`}
// style={{ cursor: "move", padding: "5px" }}
// >
// PPOO
// </p>
// <button onClick={handleCloseCard}>
// <CircleX />
// </button>
// </div>
// <PPOOTable
// columns={columns}
// data={data}
// //style={style}
// />
// </Suspense>
// </LstCard>
// </div>
// );
}

View File

@@ -1,33 +0,0 @@
//import { LstCard } from "@/components/extendedUI/LstCard";
import { getOpenOrders } from "@/utils/querys/logistics/getOpenOrders";
import { openOrderColumns } from "@/utils/tableData/openorders/ooColumns";
import { OpenOrderTable } from "@/utils/tableData/openorders/ooData";
import { useQuery } from "@tanstack/react-query";
//import { CircleX } from "lucide-react";
//import { Suspense } from "react";
//import { toast } from "sonner";
export default function OpenOrders() {
//{ style = {} }
const { data, isError, isLoading } = useQuery(getOpenOrders());
if (isLoading) return <div>Loading openOrder data...</div>;
if (isError) {
return (
<div>
<p>There was an error getting the openorders.</p>
</div>
);
}
let openOrders: any = data;
// const handleCloseCard = () => {
// //removeCard("PPOO");
// toast.success("card removed");
// };
return <OpenOrderTable columns={openOrderColumns} data={openOrders} />;
}

View File

@@ -0,0 +1,202 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { redirect, useNavigate, useRouter } from "@tanstack/react-router";
import axios from "axios";
import { createAuthClient } from "better-auth/client";
import { usernameClient } from "better-auth/client/plugins";
import { useEffect } from "react";
import { create } from "zustand";
// ---- TYPES ----
export type Session = typeof authClient.$Infer.Session | null;
// Zustand store type
type SessionState = {
session: Session;
setSession: (session: Session) => void;
clearSession: () => void;
};
export type UserRoles = {
userRoleId: string;
userId: string;
module: string;
role: "systemAdmin" | "admin" | "manager" | "user" | "viewer";
};
type UserRoleState = {
userRoles: UserRoles[] | null;
fetchRoles: () => Promise<void>;
clearRoles: () => void;
};
// ---- ZUSTAND STORE ----
export const useAuth = create<SessionState>((set) => ({
session: null,
setSession: (session) => set({ session }),
clearSession: () => set({ session: null }),
}));
export const useUserRoles = create<UserRoleState>((set) => ({
userRoles: null,
fetchRoles: async () => {
try {
const res = await axios.get(`/lst/api/user/roles`);
const roles = res.data;
set({ userRoles: roles.data });
} catch (err) {
console.error("Error fetching roles:", err);
set({ userRoles: null });
}
},
clearRoles: () => set({ userRoles: null }),
}));
export function userAccess(
moduleName: string | null,
roles: UserRoles["role"] | UserRoles["role"][],
): boolean {
const { userRoles } = useUserRoles();
if (!userRoles) return false;
const roleArray = Array.isArray(roles) ? roles : [roles];
return userRoles.some(
(m) =>
(moduleName ? m.module === moduleName : true) &&
roleArray.includes(m.role),
);
}
export async function checkUserAccess({
allowedRoles,
moduleName,
}: {
allowedRoles: UserRoles["role"][];
moduleName?: string;
//location: { pathname: string; search: string };
}) {
try {
// fetch roles from your API (credentials required)
const res = await axios.get("/lst/api/user/roles", {
withCredentials: true,
});
const roles = res.data.data as UserRoles[];
const hasAccess = roles.some(
(r) =>
(moduleName ? r.module === moduleName : true) &&
allowedRoles.includes(r.role),
);
if (!hasAccess) {
throw redirect({
to: "/",
search: { from: location.pathname + location.search },
});
}
// return roles so the route component can use them if needed
return roles;
} catch {
throw redirect({
to: "/login",
search: { redirect: location.pathname + location.search },
});
}
}
// ---- BETTER AUTH CLIENT ----
export const authClient = createAuthClient({
baseURL: `${window.location.origin}/lst/api/auth`,
plugins: [usernameClient()],
fetchOptions: {
credentials: "include", // <--- required!
},
callbacks: {
callbacks: {
onUpdate: (res: any) => {
// res has strong type
// res.data is `Session | null`
useAuth.getState().setSession(res?.data ?? null);
},
onSignIn: (res: any) => {
console.log("Setting session to ", res?.data);
useAuth.getState().setSession(res?.data ?? null);
},
onSignOut: () => {
useAuth.getState().clearSession();
},
},
},
});
// ---- AUTH API HELPERS ----
export async function signin(data: { username: string; password: string }) {
const res = await authClient.signIn.username(data);
if (res.error) throw res.error;
await authClient.getSession();
return res.data;
}
export const useLogout = () => {
const { clearSession } = useAuth();
const { clearRoles } = useUserRoles();
const navigate = useNavigate();
const router = useRouter();
const logout = async () => {
await authClient.signOut();
router.invalidate();
router.clearCache();
clearSession();
clearRoles();
navigate({ to: "/" });
window.location.reload();
};
return logout;
};
export async function getSession() {
const res = await authClient.getSession({
query: { disableCookieCache: true },
});
console.log(res);
if (res.error) return null;
return res.data;
}
// ---- REACT QUERY INTEGRATION ----
export function useSession() {
const { setSession, clearSession } = useAuth();
const qc = useQueryClient();
const query = useQuery({
queryKey: ["session"],
queryFn: getSession,
refetchInterval: 60_000,
refetchOnWindowFocus: true,
});
//console.log("Auth Check", query.data);
// react to data change
useEffect(() => {
if (query.data !== undefined) {
setSession(query.data);
}
}, [query.data, setSession]);
// react to error
useEffect(() => {
if (query.error) {
clearSession();
qc.removeQueries({ queryKey: ["session"] });
}
}, [query.error, qc, clearSession]);
return query;
}

View File

@@ -0,0 +1,6 @@
import axios from "axios";
export const api = axios.create({
baseURL: "/lst",
withCredentials: true, // ✅ always send credentials (cookies)
});

View File

@@ -30,17 +30,14 @@ import { Route as AdminNotificationMGTRouteImport } from './routes/_admin/notifi
import { Route as AdminModulesRouteImport } from './routes/_admin/modules'
import { Route as userPasswordChangeRouteImport } from './routes/(user)/passwordChange'
import { Route as ocmeCyclecountIndexRouteImport } from './routes/(ocme)/cyclecount/index'
import { Route as logisticsSiloAdjustmentsIndexRouteImport } from './routes/(logistics)/siloAdjustments/index'
import { Route as logisticsOpenOrdersIndexRouteImport } from './routes/(logistics)/openOrders/index'
import { Route as logisticsMaterialHelperIndexRouteImport } from './routes/(logistics)/materialHelper/index'
import { Route as logisticsHelperCommandsIndexRouteImport } from './routes/(logistics)/helperCommands/index'
import { Route as logisticsDmIndexRouteImport } from './routes/(logistics)/dm/index'
import { Route as logisticsBarcodegenIndexRouteImport } from './routes/(logistics)/barcodegen/index'
import { Route as EomArticleAvRouteImport } from './routes/_eom/article/$av'
import { Route as logisticsSiloAdjustmentsHistRouteImport } from './routes/(logistics)/siloAdjustments/$hist'
import { Route as logisticsMaterialHelperSiloLinkIndexRouteImport } from './routes/(logistics)/materialHelper/siloLink/index'
import { Route as logisticsMaterialHelperConsumptionIndexRouteImport } from './routes/(logistics)/materialHelper/consumption/index'
import { Route as logisticsSiloAdjustmentsCommentCommentRouteImport } from './routes/(logistics)/siloAdjustments/comment/$comment'
const RegisterRoute = RegisterRouteImport.update({
id: '/register',
@@ -144,12 +141,6 @@ const ocmeCyclecountIndexRoute = ocmeCyclecountIndexRouteImport.update({
path: '/cyclecount/',
getParentRoute: () => rootRouteImport,
} as any)
const logisticsSiloAdjustmentsIndexRoute =
logisticsSiloAdjustmentsIndexRouteImport.update({
id: '/(logistics)/siloAdjustments/',
path: '/siloAdjustments/',
getParentRoute: () => rootRouteImport,
} as any)
const logisticsOpenOrdersIndexRoute =
logisticsOpenOrdersIndexRouteImport.update({
id: '/(logistics)/openOrders/',
@@ -184,12 +175,6 @@ const EomArticleAvRoute = EomArticleAvRouteImport.update({
path: '/article/$av',
getParentRoute: () => EomRoute,
} as any)
const logisticsSiloAdjustmentsHistRoute =
logisticsSiloAdjustmentsHistRouteImport.update({
id: '/(logistics)/siloAdjustments/$hist',
path: '/siloAdjustments/$hist',
getParentRoute: () => rootRouteImport,
} as any)
const logisticsMaterialHelperSiloLinkIndexRoute =
logisticsMaterialHelperSiloLinkIndexRouteImport.update({
id: '/(logistics)/materialHelper/siloLink/',
@@ -202,12 +187,6 @@ const logisticsMaterialHelperConsumptionIndexRoute =
path: '/materialHelper/consumption/',
getParentRoute: () => rootRouteImport,
} as any)
const logisticsSiloAdjustmentsCommentCommentRoute =
logisticsSiloAdjustmentsCommentCommentRouteImport.update({
id: '/(logistics)/siloAdjustments/comment/$comment',
path: '/siloAdjustments/comment/$comment',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
@@ -227,16 +206,13 @@ export interface FileRoutesByFullPath {
'/eom': typeof EomEomRoute
'/ocp': typeof OcpIndexRoute
'/rfid': typeof RfidIndexRoute
'/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
'/article/$av': typeof EomArticleAvRoute
'/barcodegen': typeof logisticsBarcodegenIndexRoute
'/dm': typeof logisticsDmIndexRoute
'/helperCommands': typeof logisticsHelperCommandsIndexRoute
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
'/openOrders': typeof logisticsOpenOrdersIndexRoute
'/siloAdjustments': typeof logisticsSiloAdjustmentsIndexRoute
'/cyclecount': typeof ocmeCyclecountIndexRoute
'/siloAdjustments/comment/$comment': typeof logisticsSiloAdjustmentsCommentCommentRoute
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -258,16 +234,13 @@ export interface FileRoutesByTo {
'/eom': typeof EomEomRoute
'/ocp': typeof OcpIndexRoute
'/rfid': typeof RfidIndexRoute
'/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
'/article/$av': typeof EomArticleAvRoute
'/barcodegen': typeof logisticsBarcodegenIndexRoute
'/dm': typeof logisticsDmIndexRoute
'/helperCommands': typeof logisticsHelperCommandsIndexRoute
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
'/openOrders': typeof logisticsOpenOrdersIndexRoute
'/siloAdjustments': typeof logisticsSiloAdjustmentsIndexRoute
'/cyclecount': typeof ocmeCyclecountIndexRoute
'/siloAdjustments/comment/$comment': typeof logisticsSiloAdjustmentsCommentCommentRoute
'/materialHelper/consumption': typeof logisticsMaterialHelperConsumptionIndexRoute
'/materialHelper/siloLink': typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -293,16 +266,13 @@ export interface FileRoutesById {
'/_eom/eom': typeof EomEomRoute
'/ocp/': typeof OcpIndexRoute
'/rfid/': typeof RfidIndexRoute
'/(logistics)/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
'/_eom/article/$av': typeof EomArticleAvRoute
'/(logistics)/barcodegen/': typeof logisticsBarcodegenIndexRoute
'/(logistics)/dm/': typeof logisticsDmIndexRoute
'/(logistics)/helperCommands/': typeof logisticsHelperCommandsIndexRoute
'/(logistics)/materialHelper/': typeof logisticsMaterialHelperIndexRoute
'/(logistics)/openOrders/': typeof logisticsOpenOrdersIndexRoute
'/(logistics)/siloAdjustments/': typeof logisticsSiloAdjustmentsIndexRoute
'/(ocme)/cyclecount/': typeof ocmeCyclecountIndexRoute
'/(logistics)/siloAdjustments/comment/$comment': typeof logisticsSiloAdjustmentsCommentCommentRoute
'/(logistics)/materialHelper/consumption/': typeof logisticsMaterialHelperConsumptionIndexRoute
'/(logistics)/materialHelper/siloLink/': typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -326,16 +296,13 @@ export interface FileRouteTypes {
| '/eom'
| '/ocp'
| '/rfid'
| '/siloAdjustments/$hist'
| '/article/$av'
| '/barcodegen'
| '/dm'
| '/helperCommands'
| '/materialHelper'
| '/openOrders'
| '/siloAdjustments'
| '/cyclecount'
| '/siloAdjustments/comment/$comment'
| '/materialHelper/consumption'
| '/materialHelper/siloLink'
fileRoutesByTo: FileRoutesByTo
@@ -357,16 +324,13 @@ export interface FileRouteTypes {
| '/eom'
| '/ocp'
| '/rfid'
| '/siloAdjustments/$hist'
| '/article/$av'
| '/barcodegen'
| '/dm'
| '/helperCommands'
| '/materialHelper'
| '/openOrders'
| '/siloAdjustments'
| '/cyclecount'
| '/siloAdjustments/comment/$comment'
| '/materialHelper/consumption'
| '/materialHelper/siloLink'
id:
@@ -391,16 +355,13 @@ export interface FileRouteTypes {
| '/_eom/eom'
| '/ocp/'
| '/rfid/'
| '/(logistics)/siloAdjustments/$hist'
| '/_eom/article/$av'
| '/(logistics)/barcodegen/'
| '/(logistics)/dm/'
| '/(logistics)/helperCommands/'
| '/(logistics)/materialHelper/'
| '/(logistics)/openOrders/'
| '/(logistics)/siloAdjustments/'
| '/(ocme)/cyclecount/'
| '/(logistics)/siloAdjustments/comment/$comment'
| '/(logistics)/materialHelper/consumption/'
| '/(logistics)/materialHelper/siloLink/'
fileRoutesById: FileRoutesById
@@ -417,15 +378,12 @@ export interface RootRouteChildren {
userPasswordChangeRoute: typeof userPasswordChangeRoute
OcpIndexRoute: typeof OcpIndexRoute
RfidIndexRoute: typeof RfidIndexRoute
logisticsSiloAdjustmentsHistRoute: typeof logisticsSiloAdjustmentsHistRoute
logisticsBarcodegenIndexRoute: typeof logisticsBarcodegenIndexRoute
logisticsDmIndexRoute: typeof logisticsDmIndexRoute
logisticsHelperCommandsIndexRoute: typeof logisticsHelperCommandsIndexRoute
logisticsMaterialHelperIndexRoute: typeof logisticsMaterialHelperIndexRoute
logisticsOpenOrdersIndexRoute: typeof logisticsOpenOrdersIndexRoute
logisticsSiloAdjustmentsIndexRoute: typeof logisticsSiloAdjustmentsIndexRoute
ocmeCyclecountIndexRoute: typeof ocmeCyclecountIndexRoute
logisticsSiloAdjustmentsCommentCommentRoute: typeof logisticsSiloAdjustmentsCommentCommentRoute
logisticsMaterialHelperConsumptionIndexRoute: typeof logisticsMaterialHelperConsumptionIndexRoute
logisticsMaterialHelperSiloLinkIndexRoute: typeof logisticsMaterialHelperSiloLinkIndexRoute
}
@@ -579,13 +537,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ocmeCyclecountIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/(logistics)/siloAdjustments/': {
id: '/(logistics)/siloAdjustments/'
path: '/siloAdjustments'
fullPath: '/siloAdjustments'
preLoaderRoute: typeof logisticsSiloAdjustmentsIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/(logistics)/openOrders/': {
id: '/(logistics)/openOrders/'
path: '/openOrders'
@@ -628,13 +579,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof EomArticleAvRouteImport
parentRoute: typeof EomRoute
}
'/(logistics)/siloAdjustments/$hist': {
id: '/(logistics)/siloAdjustments/$hist'
path: '/siloAdjustments/$hist'
fullPath: '/siloAdjustments/$hist'
preLoaderRoute: typeof logisticsSiloAdjustmentsHistRouteImport
parentRoute: typeof rootRouteImport
}
'/(logistics)/materialHelper/siloLink/': {
id: '/(logistics)/materialHelper/siloLink/'
path: '/materialHelper/siloLink'
@@ -649,13 +593,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof logisticsMaterialHelperConsumptionIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/(logistics)/siloAdjustments/comment/$comment': {
id: '/(logistics)/siloAdjustments/comment/$comment'
path: '/siloAdjustments/comment/$comment'
fullPath: '/siloAdjustments/comment/$comment'
preLoaderRoute: typeof logisticsSiloAdjustmentsCommentCommentRouteImport
parentRoute: typeof rootRouteImport
}
}
}
@@ -715,16 +652,12 @@ const rootRouteChildren: RootRouteChildren = {
userPasswordChangeRoute: userPasswordChangeRoute,
OcpIndexRoute: OcpIndexRoute,
RfidIndexRoute: RfidIndexRoute,
logisticsSiloAdjustmentsHistRoute: logisticsSiloAdjustmentsHistRoute,
logisticsBarcodegenIndexRoute: logisticsBarcodegenIndexRoute,
logisticsDmIndexRoute: logisticsDmIndexRoute,
logisticsHelperCommandsIndexRoute: logisticsHelperCommandsIndexRoute,
logisticsMaterialHelperIndexRoute: logisticsMaterialHelperIndexRoute,
logisticsOpenOrdersIndexRoute: logisticsOpenOrdersIndexRoute,
logisticsSiloAdjustmentsIndexRoute: logisticsSiloAdjustmentsIndexRoute,
ocmeCyclecountIndexRoute: ocmeCyclecountIndexRoute,
logisticsSiloAdjustmentsCommentCommentRoute:
logisticsSiloAdjustmentsCommentCommentRoute,
logisticsMaterialHelperConsumptionIndexRoute:
logisticsMaterialHelperConsumptionIndexRoute,
logisticsMaterialHelperSiloLinkIndexRoute:

View File

@@ -1,32 +0,0 @@
import HistoricalData from "@/components/logistics/siloAdjustments/HistoricalData";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/(logistics)/siloAdjustments/$hist")({
component: RouteComponent,
beforeLoad: async () => {
const auth = localStorage.getItem("auth_token");
if (!auth) {
throw redirect({
to: "/login",
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.pathname + location.search,
},
});
}
},
// In a loader
loader: ({ params }) => params.hist,
// Or in a component
});
function RouteComponent() {
const { hist } = Route.useParams();
return (
<div>
<HistoricalData laneId={hist} />
</div>
);
}

View File

@@ -1,34 +0,0 @@
import Comment from "@/components/logistics/siloAdjustments/Comment";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute(
"/(logistics)/siloAdjustments/comment/$comment"
)({
beforeLoad: async () => {
const auth = localStorage.getItem("auth_token");
if (!auth) {
throw redirect({
to: "/login",
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.pathname + location.search,
},
});
}
},
// In a loader
loader: ({ params }) => params.comment,
// Or in a component
component: RouteComponent,
});
function RouteComponent() {
const { comment } = Route.useParams();
return (
<div className="ml-20 mt-20">
<Comment id={comment} />
</div>
);
}

View File

@@ -1,28 +0,0 @@
import SiloPage from "@/components/logistics/siloAdjustments/SiloPage";
import { createFileRoute, redirect } from "@tanstack/react-router";
export const Route = createFileRoute("/(logistics)/siloAdjustments/")({
component: RouteComponent,
beforeLoad: async () => {
const auth = localStorage.getItem("auth_token");
if (!auth) {
throw redirect({
to: "/login",
search: {
// Use the current location to power a redirect after login
// (Do not use `router.state.resolvedLocation` as it can
// potentially lag behind the actual current location)
redirect: location.pathname + location.search,
},
});
}
},
});
function RouteComponent() {
return (
<div>
<SiloPage />
</div>
);
}

View File

@@ -1,10 +1,12 @@
{
"files": [],
"references": [{"path": "./tsconfig.app.json"}, {"path": "./tsconfig.node.json"}],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@@ -3,12 +3,13 @@
"version": "2.27.0",
"type": "module",
"scripts": {
"dev": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
"dev:both": "concurrently -n \"server,frontend\" -c \"#007755,#2f6da3\" \"npm run dev:server\" \"cd frontend && npm run dev\"",
"dev": "npm run dev:server",
"dev:server": "dotenvx run -f .env -- tsx watch server/index.ts",
"dev:frontend": "cd frontend && npm run dev",
"dev:dbgen": " drizzle-kit generate --config=drizzle-dev.config.ts",
"dev:dbmigrate": " drizzle-kit migrate --config=drizzle-dev.config.ts",
"build": "npm run build:server && npm run build:frontend",
"build": "npm run build:server",
"build:server": "rimraf dist && tsc --build && npm run copy:scripts && xcopy server\\services\\notifications\\utils\\views\\ dist\\server\\services\\notifications\\utils\\views\\ /E /I /Y ",
"build:frontend": "cd frontend && npm run build",
"build:iisNet": "rimraf dotnetwrapper\\bin && xcopy frontend\\dist dotnetwrapper\\wwwroot /E /I /Y && cd dotnetwrapper && dotnet publish lst-wrapper.csproj --configuration Release --output ../prodBuild",
@@ -21,7 +22,7 @@
"db:dev": "npm run build && npm run db:generate && npm run db:migrate",
"deploy": "standard-version --conventional-commits && npm run build",
"zipServer": "dotenvx run -f .env -- tsx server/scripts/zipUpBuild.ts \"C:\\Users\\matthes01\\Documents\\lstv2\"",
"newBuild": "npm run build:server && npm run build:frontend",
"newBuild": "npm run build:server",
"copyToNew": "powershell -ExecutionPolicy Bypass -File server/scripts/copyToLst.ps1 -dir \"C:\\Users\\matthes01\\Documents\\lstv2\"",
"removeOld": "rimraf dist && rimraf frontend/dist",
"prodBuild": "npm run v1Build && npm run build && npm run zipServer && npm run dev",

View File

@@ -4,6 +4,7 @@ import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import { OpenAPIHono } from "@hono/zod-openapi";
import { cors } from "hono/cors";
import { html } from "hono/html";
import { logger } from "hono/logger";
import os from "os";
import auth from "./services/auth/authService.js";
@@ -152,9 +153,66 @@ app.route("/ocme/", ocme);
// the catch all api route
app.all("/api/*", (c) => c.json({ error: "API route not found" }, 404));
const newPageDoc = `
<!doctype html>
<h1>Hello!</h1>
`;
app.all("*", (c) => {
const testServers = ["test1", "test2", "test3"];
const server = serverIntialized.filter((n: any) => n.name === "plantToken");
let url = "";
if (testServers.includes(server[0].value)) {
const dbServer = serverIntialized.filter((n: any) => n.name === "dbServer");
url = `https://${dbServer[0].value}.alpla.net/lst/app/old${c.req.path}`;
} else {
url = `https://${server[0].value}prod.alpla.net/lst/app/old${c.req.path}`;
}
const target = url;
const waitSeconds = 10;
return c.html(html`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Lst Moved</title>
<style>
body{font-family:sans-serif;display:flex;flex-direction:column;
align-items:center;justify-content:center;height:100vh;}
button{margin-top:1rem;padding:0.6rem 1.2rem;font-size:1rem;cursor:pointer;}
</style>
</head>
<body>
<h1>Weve moved!</h1>
<p>With new security and updating to mobile version the app had to be moved. You will be redirected to.</p>
<p><a href="${target}">${target}</a></p>
<p>Redirecting in <span id="countdown">${waitSeconds}</span> seconds…</p>
<button id="goNow">Go now</button>
<script>
let s = ${waitSeconds};
const el = document.getElementById('countdown');
const btn = document.getElementById('goNow');
const timer = setInterval(()=>{
s--;
el.textContent = s;
if(s <= 0){ clearInterval(timer); window.location.href='${target}'; }
},1000);
btn.addEventListener('click',()=>{
clearInterval(timer); window.location.href='${target}';
});
</script>
</body>
</html>`);
});
// app.all("/*", (c) => {
// console.log(c.req.path);
// return c.html(html`${newPageDoc}`);
// });
// front end static files
app.use("/*", serveStatic({ root: "./frontend/dist" }));
app.use("*", serveStatic({ path: "./frontend/dist/index.html" }));
//app.use("/*", serveStatic({ root: "./frontend/dist" }));
//app.use("*", serveStatic({ path: "./frontend/dist/index.html" }));
// Handle app exit signals
process.on("SIGINT", async () => {

View File

@@ -13,7 +13,7 @@ const newSubModules = [
name: "RFID",
moduleName: "prodcution",
description: "RFID stuff",
link: "/rfid",
link: "/lst/app/old/rfid",
icon: "Tags",
active: true,
roles: [
@@ -30,7 +30,7 @@ const newSubModules = [
name: "siloAdjustments",
moduleName: "logistics",
description: "Do a silo adjustments",
link: "/siloAdjustments",
link: "/lst/app/old/siloAdjustments",
icon: "Cylinder",
active: false,
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
@@ -40,7 +40,7 @@ const newSubModules = [
name: "demandManagement",
moduleName: "logistics",
description: "Bulk order and Forecast imports",
link: "/dm",
link: "/lst/app/old/dm",
icon: "Truck",
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
active: false,
@@ -60,7 +60,7 @@ const newSubModules = [
name: "Material Helper",
moduleName: "logistics",
description: "",
link: "/materialHelper/consumption",
link: "/lst/app/old/materialHelper/consumption",
icon: "Package",
roles: [
"viewer",
@@ -77,7 +77,7 @@ const newSubModules = [
name: "Ocme Cyclecount",
moduleName: "logistics",
description: "",
link: "/cyclecount",
link: "/lst/app/old/cyclecount",
icon: "Package",
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
active: false,
@@ -87,7 +87,7 @@ const newSubModules = [
name: "Open Orders",
moduleName: "logistics",
description: "Open orders",
link: "/openOrders",
link: "/lst/app/old/openOrders",
icon: "Truck",
roles: [
"viewer",
@@ -104,7 +104,7 @@ const newSubModules = [
name: "Barcodes",
moduleName: "logistics",
description: "Barcodes, lanes and scanable",
link: "/barcodegen",
link: "/lst/app/old/barcodegen",
icon: "Barcode",
roles: [
"viewer",
@@ -121,7 +121,7 @@ const newSubModules = [
name: "Helper Commands",
moduleName: "logistics",
description: "Commands to assist when a scanner is not avalible",
link: "/helpercommands",
link: "/lst/app/old/helpercommands",
icon: "Command",
roles: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
active: true,
@@ -222,6 +222,7 @@ const newSubModules = [
},
];
export const areSubModulesIn = async () => {
await db.delete(subModules);
try {
for (let i = 0; i < newSubModules.length; i++) {
const subModuleUpdate = await db