test(frontend): work on the frontend to have better admin

This commit is contained in:
2025-04-04 17:09:30 -05:00
parent e1973e4da6
commit ad5e77028d
11 changed files with 375 additions and 225 deletions

View File

@@ -0,0 +1,8 @@
export default function DataMartStats() {
return (
<div>
The stats for all the data mart querys out there and whos and when
they are last used to understand if we want to keep them or not
</div>
);
}

View File

@@ -0,0 +1,8 @@
export default function NotificationMGT() {
return (
<div>
Manage all notifications from here instad of going to the db,
locking some items that are auto updated on server restarts
</div>
);
}

View File

@@ -19,6 +19,11 @@ import { adminUrlCheck } from "@/utils/adminUrlCheck";
import RestartServer from "./RestartServer"; import RestartServer from "./RestartServer";
import StopServer from "./StopServer"; import StopServer from "./StopServer";
import StartServer from "./StartServer"; import StartServer from "./StartServer";
import { Button } from "@/components/ui/button";
import { getSettings } from "@/utils/querys/settings";
import { toast } from "sonner";
import axios from "axios";
//import { useState } from "react";
export type Servers = { export type Servers = {
server_id?: string; server_id?: string;
@@ -33,6 +38,7 @@ export type Servers = {
export default function ServerPage() { export default function ServerPage() {
const { user, token } = useSessionStore(); const { user, token } = useSessionStore();
const { modules } = useModuleStore(); const { modules } = useModuleStore();
//const [upgrading, setUpgrading] = useState(false);
const router = useRouter(); const router = useRouter();
const { data, isError, error, isLoading } = useQuery( const { data, isError, error, isLoading } = useQuery(
@@ -51,10 +57,45 @@ export default function ServerPage() {
if (isError) { if (isError) {
return <div>{JSON.stringify(error)}</div>; return <div>{JSON.stringify(error)}</div>;
} }
const { data: set } = useQuery(getSettings(token ?? ""));
const upgrade = async () => {
let devDir = set.filter((n: any) => n.name === "devDir");
toast.success("All Servers was just triggered.");
try {
const result = await axios.post(
`/api/server/update/localhost`,
{ devDir: devDir[0].value, all: true },
{
headers: { Authorization: `Bearer ${token}` },
}
);
if (result.data.success) {
toast.success(result.data.message);
}
if (!result.data.success) {
toast.success(result.data.message);
}
} catch (error: any) {
toast.error(
`There was an error updating the server: ${error.data.message}`
);
}
};
//console.log(data); //console.log(data);
return ( return (
<LstCard className="m-2 flex place-content-center w-dvh"> <LstCard className="m-2 flex place-content-center w-dvh">
<div className="flex justify-end m-2">
<Button
onClick={upgrade}
disabled={data?.some((d: any) => d.isUpgrading)}
>
Update All Servers
</Button>
</div>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>

View File

@@ -1,3 +1,4 @@
import { LstCard } from "@/components/extendedUI/LstCard";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
@@ -10,6 +11,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { DebugButton } from "@/utils/formStuff/debugButton"; import { DebugButton } from "@/utils/formStuff/debugButton";
import { userFormOptions } from "@/utils/formStuff/options/userformOptions"; import { userFormOptions } from "@/utils/formStuff/options/userformOptions";
import { generatePassword } from "@/utils/passwordGen"; import { generatePassword } from "@/utils/passwordGen";
@@ -18,10 +20,16 @@ import { useForm } from "@tanstack/react-form";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import axios from "axios"; import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import UserRoles from "./UserRoles";
import { CardHeader } from "@/components/ui/card";
export default function UserCard(data: any) { export default function UserCard(data: any) {
const token = localStorage.getItem("auth_token"); const token = localStorage.getItem("auth_token");
const { refetch } = useQuery(getUsers()); const { refetch } = useQuery(getUsers());
//console.log(modules);
//console.log(data.user);
//console.log(userRoles);
const form = useForm({ const form = useForm({
...userFormOptions(data.user), ...userFormOptions(data.user),
onSubmit: async ({ value }) => { onSubmit: async ({ value }) => {
@@ -53,7 +61,10 @@ export default function UserCard(data: any) {
}, },
}); });
return ( return (
<div> <div className="flex flex-row">
<div className="m-2">
<LstCard>
<CardHeader>User Profile</CardHeader>
<form <form
onSubmit={(e) => { onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
@@ -72,18 +83,26 @@ export default function UserCard(data: any) {
children={(field) => { children={(field) => {
return ( return (
<div className="m-2 min-w-48 max-w-96 p-2"> <div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor="username">Username</Label> <Label htmlFor="username">
Username
</Label>
<Input <Input
name={field.name} name={field.name}
value={field.state.value} value={field.state.value}
onBlur={field.handleBlur} onBlur={field.handleBlur}
//type="number" //type="number"
onChange={(e) => onChange={(e) =>
field.handleChange(e.target.value) field.handleChange(
e.target.value
)
} }
/> />
{field.state.meta.errors.length ? ( {field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(",")}</em> <em>
{field.state.meta.errors.join(
","
)}
</em>
) : null} ) : null}
</div> </div>
); );
@@ -108,11 +127,17 @@ export default function UserCard(data: any) {
onBlur={field.handleBlur} onBlur={field.handleBlur}
//type="number" //type="number"
onChange={(e) => onChange={(e) =>
field.handleChange(e.target.value) field.handleChange(
e.target.value
)
} }
/> />
{field.state.meta.errors.length ? ( {field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(",")}</em> <em>
{field.state.meta.errors.join(
","
)}
</em>
) : null} ) : null}
</div> </div>
); );
@@ -124,7 +149,9 @@ export default function UserCard(data: any) {
children={(field) => { children={(field) => {
return ( return (
<div className="m-2 min-w-48 max-w-96 p-2"> <div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor={field.name}>Select role</Label> <Label htmlFor={field.name}>
Select role
</Label>
<Select <Select
value={field.state.value} value={field.state.value}
onValueChange={field.handleChange} onValueChange={field.handleChange}
@@ -137,7 +164,9 @@ export default function UserCard(data: any) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectLabel>Roles</SelectLabel> <SelectLabel>
Roles
</SelectLabel>
<SelectItem value="viewer"> <SelectItem value="viewer">
Viewer Viewer
</SelectItem> </SelectItem>
@@ -163,7 +192,9 @@ export default function UserCard(data: any) {
onChangeAsyncDebounceMs: 500, onChangeAsyncDebounceMs: 500,
onChangeAsync: ({ value }) => { onChangeAsync: ({ value }) => {
if ( if (
window.location.pathname.includes("/users") && window.location.pathname.includes(
"/users"
) &&
value.length === 0 value.length === 0
) { ) {
return; return;
@@ -207,7 +238,9 @@ export default function UserCard(data: any) {
onBlur={field.handleBlur} onBlur={field.handleBlur}
//type="number" //type="number"
onChange={(e) => onChange={(e) =>
field.handleChange(e.target.value) field.handleChange(
e.target.value
)
} }
/> />
<Button <Button
@@ -220,19 +253,33 @@ export default function UserCard(data: any) {
> >
Random password Random password
</Button> </Button>
<DebugButton data={form.state.values} /> <DebugButton
data={form.state.values}
/>
</div> </div>
{field.state.meta.errors.length ? ( {field.state.meta.errors.length ? (
<em>{field.state.meta.errors.join(",")}</em> <em>
{field.state.meta.errors.join(
","
)}
</em>
) : null} ) : null}
</div> </div>
); );
}} }}
/> />
</form> </form>
</LstCard>
<div> <div>
<Button onClick={form.handleSubmit}>Save</Button> <Button onClick={form.handleSubmit}>Save</Button>
</div> </div>
</div> </div>
<div className="m-2">
<LstCard>
<CardHeader>User Module / Role Access</CardHeader>
<UserRoles user={data.user} />
</LstCard>
</div>
</div>
); );
} }

View File

@@ -0,0 +1,16 @@
import { Label } from "@/components/ui/label";
import { useModuleStore } from "@/lib/store/useModuleStore";
//import { Checkbox } from "@radix-ui/react-checkbox";
export default function UserRoles(user: any) {
const { modules } = useModuleStore();
console.log(user);
return (
<div>
{modules?.map((m: any) => {
console.log(m);
return <Label>{m.name}</Label>;
})}
</div>
);
}

View File

@@ -20,48 +20,64 @@ const items = [
active: true, active: true,
}, },
{ {
title: "Bulk orders", name: "Bulk orders",
url: "#", moduleName: "logistics",
description: "",
link: "#",
icon: Truck, icon: Truck,
role: ["systemAdmin"], role: ["systemAdmin"],
module: "logistics",
active: true, active: true,
subSubModule: [],
}, },
{ {
title: "Forecast", name: "Forecast",
url: "#", moduleName: "logistics",
description: "",
link: "#",
icon: Truck, icon: Truck,
role: ["systemAdmin"], role: ["systemAdmin"],
module: "logistics",
active: true, active: true,
subSubModule: [],
}, },
{ {
title: "Ocme cycle counts", name: "Ocme cycle counts",
url: "#", moduleName: "logistics",
description: "",
link: "#",
icon: Package, icon: Package,
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"], role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
module: "logistics",
active: false, active: false,
subSubModule: [],
}, },
{ {
title: "Material Helper", name: "Material Helper",
url: "/materialHelper/consumption", moduleName: "logistics",
description: "",
link: "/materialHelper/consumption",
icon: Package, icon: Package,
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"], role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
module: "logistics",
active: true, active: true,
subSubModule: [],
}, },
{ {
title: "Ocme Cyclecount", name: "Ocme Cyclecount",
url: "/cyclecount", moduleName: "logistics",
description: "",
link: "/cyclecount",
icon: Package, icon: Package,
role: ["technician", "supervisor", "manager", "admin", "systemAdmin"], role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
module: "logistics",
active: true, active: true,
subSubModule: [],
}, },
]; ];
export function LogisticsSideBar({user, moduleID}: {user: User | null; moduleID: string}) { export function LogisticsSideBar({
user,
moduleID,
}: {
user: User | null;
moduleID: string;
}) {
return ( return (
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>Logistics</SidebarGroupLabel> <SidebarGroupLabel>Logistics</SidebarGroupLabel>
@@ -70,7 +86,8 @@ export function LogisticsSideBar({user, moduleID}: {user: User | null; moduleID:
{items.map((item) => ( {items.map((item) => (
<SidebarMenuItem key={item.title}> <SidebarMenuItem key={item.title}>
<> <>
{hasPageAccess(user, item.role, moduleID) && item.active && ( {hasPageAccess(user, item.role, moduleID) &&
item.active && (
<SidebarMenuButton asChild> <SidebarMenuButton asChild>
<a href={item.url}> <a href={item.url}>
<item.icon /> <item.icon />

View File

@@ -32,7 +32,7 @@ let lotColumns = [
label: "AvDescription", label: "AvDescription",
}, },
{ {
key: "LOT", key: "lot",
label: "LotNumber", label: "LotNumber",
}, },
{ {
@@ -208,10 +208,14 @@ export default function Lots() {
<TableCell className="font-medium"> <TableCell className="font-medium">
{lot.Alias} {lot.Alias}
</TableCell> </TableCell>
<TableCell className="font-medium"> <TableCell
{lot.LOT} className={`font-medium ${lot.ProlinkLot != lot.lot ? "text-red-500" : ""}`}
>
{lot.lot}
</TableCell> </TableCell>
<TableCell className="font-medium"> <TableCell
className={`font-medium ${lot.ProlinkLot != lot.lot ? "text-red-500" : ""}`}
>
{lot.ProlinkLot} {lot.ProlinkLot}
</TableCell> </TableCell>
<TableCell className="font-medium"> <TableCell className="font-medium">

View File

@@ -2,19 +2,27 @@ import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
import { useModuleStore } from "../../lib/store/useModuleStore"; import { useModuleStore } from "../../lib/store/useModuleStore";
import { useEffect } from "react"; import { useEffect } from "react";
import { useSettingStore } from "@/lib/store/useSettings"; import { useSettingStore } from "@/lib/store/useSettings";
//import {useGetUserRoles} from "@/lib/store/useGetRoles"; import { useGetUserRoles } from "@/lib/store/useGetRoles";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
export const SessionProvider = ({children}: {children: React.ReactNode}) => { export const SessionProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { fetchModules } = useModuleStore(); const { fetchModules } = useModuleStore();
const { fetchSettings } = useSettingStore(); const { fetchSettings } = useSettingStore();
//const {fetchUserRoles} = useGetUserRoles(); const { fetchUserRoles } = useGetUserRoles();
useEffect(() => { useEffect(() => {
fetchModules(); fetchModules();
fetchSettings(); fetchSettings();
//fetchUserRoles(); fetchUserRoles();
}, []); }, []);
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>; return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}; };

View File

@@ -12,7 +12,7 @@ function Checkbox({
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
data-slot="checkbox" data-slot="checkbox"
className={cn( className={cn(
"peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className className
)} )}
{...props} {...props}

View File

@@ -1,5 +1,4 @@
import { create } from "zustand"; import { create } from "zustand";
import {useSessionStore} from "./sessionStore";
import { Modules } from "@/types/modules"; import { Modules } from "@/types/modules";
import axios from "axios"; import axios from "axios";
@@ -19,8 +18,10 @@ export const useGetUserRoles = create<SettingState>()((set) => ({
fetchUserRoles: async () => { fetchUserRoles: async () => {
try { try {
//const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`); //const response = await axios.get<{data: Setting[]}>(`${process.env.NEXT_PUBLIC_URL}/api/settings/client`);
const {token} = useSessionStore(); const token = localStorage.getItem("auth_token");
const response = await axios.get("/api/auth/getuseraccess", {headers: {Authorization: `Bearer ${token}`}}); const response = await axios.get("/api/auth/getuseraccess", {
headers: { Authorization: `Bearer ${token}` },
});
const data: FetchModulesResponse = response.data; //await response.json(); const data: FetchModulesResponse = response.data; //await response.json();
//console.log(data); //console.log(data);
set({ userRoles: data.data }); set({ userRoles: data.data });

View File

@@ -1,7 +1,7 @@
export type LotType = { export type LotType = {
AV: number; AV: number;
Alias: string; Alias: string;
LOT: number; lot: number;
LabelOnlineID: number; LabelOnlineID: number;
MachineDescription: string; MachineDescription: string;
MachineID: number; MachineID: number;