From 99ad79c662cfeddd78bc86792852e8e97b14e287 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Thu, 12 Jun 2025 21:10:46 -0500 Subject: [PATCH] feat(produser): added in the function to create a standard user based on there username --- .../admin/prodUser/ProdUserCard.tsx | 214 ++++++++++++++++++ frontend/src/routeTree.gen.ts | 27 +++ frontend/src/routes/_admin/prodUsers.tsx | 14 ++ .../utils/querys/prodUser/getProdPerms.tsx | 25 ++ frontend/src/utils/querys/settings.tsx | 22 +- .../prodUser/controller/getprodRoles.ts | 23 ++ .../services/prodUser/controller/produser.ts | 91 ++++++-- .../services/prodUser/routes/addProdRole.ts | 2 +- .../services/prodUser/routes/getProdRoles.ts | 36 +++ server/services/prodUser/routes/produser.ts | 2 +- server/services/prodUser/utils/prodRoles.ts | 2 +- server/services/server/utils/settingsCheck.ts | 8 + .../services/server/utils/subModuleCheck.ts | 7 + 13 files changed, 438 insertions(+), 35 deletions(-) create mode 100644 frontend/src/components/admin/prodUser/ProdUserCard.tsx create mode 100644 frontend/src/routes/_admin/prodUsers.tsx create mode 100644 frontend/src/utils/querys/prodUser/getProdPerms.tsx create mode 100644 server/services/prodUser/controller/getprodRoles.ts create mode 100644 server/services/prodUser/routes/getProdRoles.ts diff --git a/frontend/src/components/admin/prodUser/ProdUserCard.tsx b/frontend/src/components/admin/prodUser/ProdUserCard.tsx new file mode 100644 index 0000000..74841a0 --- /dev/null +++ b/frontend/src/components/admin/prodUser/ProdUserCard.tsx @@ -0,0 +1,214 @@ +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 { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useSessionStore } from "@/lib/store/sessionStore"; +import { getProdPerms } from "@/utils/querys/prodUser/getProdPerms"; + +import { useForm } from "@tanstack/react-form"; + +import { useQuery } from "@tanstack/react-query"; +import axios from "axios"; +import { toast } from "sonner"; + +export default function ProdUserCard() { + const { token } = useSessionStore(); + const { data, isError, isLoading } = useQuery(getProdPerms(token ?? "")); + + const form = useForm({ + defaultValues: { + username: "", + remark: "", + pcname: "", + role: "", + }, + + onSubmit: async ({ value }) => { + console.log(value); + + if (value.role === "") { + toast.error( + "Role is missing please select a role and try again." + ); + } + + try { + const res = await axios.post("/api/produser/produser", value, { + headers: { Authorization: `Bearer ${token}` }, + }); + + //console.log(res.data); + + if (!res.data.success) { + toast.error(res.data.data?.errors[0].message); + } + + if (res.data.success) { + toast.success(res.data.message); + form.reset(); + } + } catch (error) { + console.log(error); + } + }, + }); + + if (isError) + return ( + <> +

There was an error loading the prod Perms

+ + ); + if (isLoading) + return ( + <> +

Loading.....

+ + ); + return ( +
+ + +

Alpla Prod user create/update

+
+
+

+ Please enter the windows username.
To check the + user head here{" "} + + Active Directory + +

+
+
{ + e.preventDefault(); + e.stopPropagation(); + }} + > + {/* Windows username */} + + value.length > 3 + ? undefined + : "Username must be longer than 3 letters", + }} + children={(field) => { + return ( +
+ + + field.handleChange(e.target.value) + } + /> + {field.state.meta.errors.length ? ( + + {field.state.meta.errors.join(",")} + + ) : null} +
+ ); + }} + /> + {/* Remark for the user */} + + value.length > 3 + ? undefined + : "The remark should be longer than 3 letters", + }} + children={(field) => { + return ( +
+ + + field.handleChange(e.target.value) + } + /> + {field.state.meta.errors.length ? ( + + {field.state.meta.errors.join(",")} + + ) : null} +
+ ); + }} + /> + {/* Select the type of role we will be granting/updating */} + {}}} + children={(field) => { + return ( +
+ + +
+ ); + }} + /> +
+ +
+ +
+
+ ); +} diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index df9be59..a8cc97b 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -26,6 +26,7 @@ import { Route as AdminUsersImport } from './routes/_admin/users' import { Route as AdminSubModulesImport } from './routes/_admin/subModules' import { Route as AdminSettingsImport } from './routes/_admin/settings' import { Route as AdminServersImport } from './routes/_admin/servers' +import { Route as AdminProdUsersImport } from './routes/_admin/prodUsers' import { Route as AdminNotificationMGTImport } from './routes/_admin/notificationMGT' import { Route as AdminModulesImport } from './routes/_admin/modules' import { Route as userPasswordChangeImport } from './routes/(user)/passwordChange' @@ -131,6 +132,12 @@ const AdminServersRoute = AdminServersImport.update({ getParentRoute: () => AdminRoute, } as any) +const AdminProdUsersRoute = AdminProdUsersImport.update({ + id: '/prodUsers', + path: '/prodUsers', + getParentRoute: () => AdminRoute, +} as any) + const AdminNotificationMGTRoute = AdminNotificationMGTImport.update({ id: '/notificationMGT', path: '/notificationMGT', @@ -309,6 +316,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AdminNotificationMGTImport parentRoute: typeof AdminImport } + '/_admin/prodUsers': { + id: '/_admin/prodUsers' + path: '/prodUsers' + fullPath: '/prodUsers' + preLoaderRoute: typeof AdminProdUsersImport + parentRoute: typeof AdminImport + } '/_admin/servers': { id: '/_admin/servers' path: '/servers' @@ -450,6 +464,7 @@ declare module '@tanstack/react-router' { interface AdminRouteChildren { AdminModulesRoute: typeof AdminModulesRoute AdminNotificationMGTRoute: typeof AdminNotificationMGTRoute + AdminProdUsersRoute: typeof AdminProdUsersRoute AdminServersRoute: typeof AdminServersRoute AdminSettingsRoute: typeof AdminSettingsRoute AdminSubModulesRoute: typeof AdminSubModulesRoute @@ -459,6 +474,7 @@ interface AdminRouteChildren { const AdminRouteChildren: AdminRouteChildren = { AdminModulesRoute: AdminModulesRoute, AdminNotificationMGTRoute: AdminNotificationMGTRoute, + AdminProdUsersRoute: AdminProdUsersRoute, AdminServersRoute: AdminServersRoute, AdminSettingsRoute: AdminSettingsRoute, AdminSubModulesRoute: AdminSubModulesRoute, @@ -499,6 +515,7 @@ export interface FileRoutesByFullPath { '/passwordChange': typeof userPasswordChangeRoute '/modules': typeof AdminModulesRoute '/notificationMGT': typeof AdminNotificationMGTRoute + '/prodUsers': typeof AdminProdUsersRoute '/servers': typeof AdminServersRoute '/settings': typeof AdminSettingsRoute '/subModules': typeof AdminSubModulesRoute @@ -530,6 +547,7 @@ export interface FileRoutesByTo { '/passwordChange': typeof userPasswordChangeRoute '/modules': typeof AdminModulesRoute '/notificationMGT': typeof AdminNotificationMGTRoute + '/prodUsers': typeof AdminProdUsersRoute '/servers': typeof AdminServersRoute '/settings': typeof AdminSettingsRoute '/subModules': typeof AdminSubModulesRoute @@ -564,6 +582,7 @@ export interface FileRoutesById { '/(user)/passwordChange': typeof userPasswordChangeRoute '/_admin/modules': typeof AdminModulesRoute '/_admin/notificationMGT': typeof AdminNotificationMGTRoute + '/_admin/prodUsers': typeof AdminProdUsersRoute '/_admin/servers': typeof AdminServersRoute '/_admin/settings': typeof AdminSettingsRoute '/_admin/subModules': typeof AdminSubModulesRoute @@ -597,6 +616,7 @@ export interface FileRouteTypes { | '/passwordChange' | '/modules' | '/notificationMGT' + | '/prodUsers' | '/servers' | '/settings' | '/subModules' @@ -627,6 +647,7 @@ export interface FileRouteTypes { | '/passwordChange' | '/modules' | '/notificationMGT' + | '/prodUsers' | '/servers' | '/settings' | '/subModules' @@ -659,6 +680,7 @@ export interface FileRouteTypes { | '/(user)/passwordChange' | '/_admin/modules' | '/_admin/notificationMGT' + | '/_admin/prodUsers' | '/_admin/servers' | '/_admin/settings' | '/_admin/subModules' @@ -773,6 +795,7 @@ export const routeTree = rootRoute "children": [ "/_admin/modules", "/_admin/notificationMGT", + "/_admin/prodUsers", "/_admin/servers", "/_admin/settings", "/_admin/subModules", @@ -815,6 +838,10 @@ export const routeTree = rootRoute "filePath": "_admin/notificationMGT.tsx", "parent": "/_admin" }, + "/_admin/prodUsers": { + "filePath": "_admin/prodUsers.tsx", + "parent": "/_admin" + }, "/_admin/servers": { "filePath": "_admin/servers.tsx", "parent": "/_admin" diff --git a/frontend/src/routes/_admin/prodUsers.tsx b/frontend/src/routes/_admin/prodUsers.tsx new file mode 100644 index 0000000..6e33f7d --- /dev/null +++ b/frontend/src/routes/_admin/prodUsers.tsx @@ -0,0 +1,14 @@ +import ProdUserCard from "@/components/admin/prodUser/ProdUserCard"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/_admin/prodUsers")({ + component: RouteComponent, +}); + +function RouteComponent() { + return ( +
+ +
+ ); +} diff --git a/frontend/src/utils/querys/prodUser/getProdPerms.tsx b/frontend/src/utils/querys/prodUser/getProdPerms.tsx new file mode 100644 index 0000000..83d362b --- /dev/null +++ b/frontend/src/utils/querys/prodUser/getProdPerms.tsx @@ -0,0 +1,25 @@ +import { queryOptions } from "@tanstack/react-query"; +import axios from "axios"; + +export function getProdPerms(token: string) { + return queryOptions({ + queryKey: ["prodPerms"], + queryFn: () => fetch(token), + enabled: !!token, + staleTime: 1000, + refetchOnWindowFocus: true, + }); +} + +const fetch = async (token: string) => { + const { data } = await axios.get("/api/produser/prodrole", { + headers: { Authorization: `Bearer ${token}` }, + }); + // if we are not localhost ignore the devDir setting. + // const url: string = window.location.host.split(":")[0]; + // let settingsData = data.data; + // if (url != "localhost") { + // settingsData.filter((n: any) => n.name === "devDir"); + // } + return data.data; +}; diff --git a/frontend/src/utils/querys/settings.tsx b/frontend/src/utils/querys/settings.tsx index 2355b03..d8291d0 100644 --- a/frontend/src/utils/querys/settings.tsx +++ b/frontend/src/utils/querys/settings.tsx @@ -1,23 +1,25 @@ -import {queryOptions} from "@tanstack/react-query"; +import { queryOptions } from "@tanstack/react-query"; import axios from "axios"; export function getSettings(token: string) { return queryOptions({ queryKey: ["settings"], - queryFn: () => fetchSettings(token), + queryFn: () => fetch(token), enabled: !!token, staleTime: 1000, refetchOnWindowFocus: true, }); } -const fetchSettings = async (token: string) => { - const {data} = await axios.get("/api/server/settings", {headers: {Authorization: `Bearer ${token}`}}); +const fetch = async (token: string) => { + const { data } = await axios.get("/api/server/settings", { + headers: { Authorization: `Bearer ${token}` }, + }); // if we are not localhost ignore the devDir setting. - const url: string = window.location.host.split(":")[0]; - let settingsData = data.data; - if (url != "localhost") { - settingsData.filter((n: any) => n.name === "devDir"); - } - return settingsData; + // const url: string = window.location.host.split(":")[0]; + // let settingsData = data.data; + // if (url != "localhost") { + // settingsData.filter((n: any) => n.name === "devDir"); + // } + return data.data; }; diff --git a/server/services/prodUser/controller/getprodRoles.ts b/server/services/prodUser/controller/getprodRoles.ts new file mode 100644 index 0000000..209f6e5 --- /dev/null +++ b/server/services/prodUser/controller/getprodRoles.ts @@ -0,0 +1,23 @@ +import { db } from "../../../../database/dbclient.js"; +import { prodPermissions } from "../../../../database/schema/prodPermissions.js"; +import { tryCatch } from "../../../globalUtils/tryCatch.js"; + +export const getProdRoles = async () => { + const { data, error } = (await tryCatch( + db.select().from(prodPermissions) + )) as any; + + if (error) { + return { + success: false, + message: "Error getting prod permissions", + data: error, + }; + } + + return { + success: true, + message: "Current prod permissions", + data: data, + }; +}; diff --git a/server/services/prodUser/controller/produser.ts b/server/services/prodUser/controller/produser.ts index b203d38..fea8576 100644 --- a/server/services/prodUser/controller/produser.ts +++ b/server/services/prodUser/controller/produser.ts @@ -28,7 +28,7 @@ export const prodUser = async (data: any) => { if (pe) { console.log(pe); return { - succes: false, + success: false, message: "There was an error getting the base prod permissions", data: pe, }; @@ -39,7 +39,7 @@ export const prodUser = async (data: any) => { if (permRoleCheck.length === 0) { return { - succes: false, + success: false, message: `Role: ${data.role}, dose note exist please check the role you have selected and try again.`, data: [], }; @@ -55,6 +55,8 @@ export const prodUser = async (data: any) => { console.log(userError); } + //console.log(permRoleCheck); + if (usercheck?.data.length === 0) { // create the user const newUser: any = { @@ -69,9 +71,7 @@ export const prodUser = async (data: any) => { const { data: newU, error: newE } = (await tryCatch( axios.post(newurl, newUser, { headers: { - Authorization: `Basic ${btoa( - `matthes01:99Monsters200Scary!` - )}`, + "X-API-Key": process.env.TEC_API_KEY || "", "Content-Type": "application/json", }, }) @@ -80,42 +80,40 @@ export const prodUser = async (data: any) => { if (newE) { console.log(newE); return { - succes: false, + success: false, message: `${data.username} encountered an error creating..`, data: newE.response.data, }; } return { - succes: true, + success: true, message: `${data.username} was just created or updated.`, data: [], }; } else { // revoke and readd - const revokePerms: any = { - roles: - JSON.parse(usercheck.data[0].roles.replaceAll("\\", "\\\\")) || - [], - rolesLegacy: JSON.parse(usercheck.data[0].legacyRoles) || [], + roles: JSON.parse( + usercheck.data[0].roles.replaceAll("\\", "\\\\") + ) || ["Manufacturing\\IssueMaterial\\MaterialHandler"], + rolesLegacy: JSON.parse(usercheck.data[0].legacyRoles) || [3], }; const { data: newU, error: newE } = (await tryCatch( axios.patch(revoke, revokePerms, { headers: { - Authorization: `Basic ${btoa( - `matthes01:99Monsters200Scary!` - )}`, + "X-API-Key": process.env.TEC_API_KEY || "", "Content-Type": "application/json", }, }) )) as any; if (newE) { - console.log(newE.response.data); + console.log("Revoke failed with: ", newE.response.data); + return { - succes: false, + success: false, message: `${data.username} encountered an error updating..`, data: newE.response.data, }; @@ -130,9 +128,7 @@ export const prodUser = async (data: any) => { const { data: grant, error: grante } = (await tryCatch( axios.patch(grantUrl, grantRole, { headers: { - Authorization: `Basic ${btoa( - `matthes01:99Monsters200Scary!` - )}`, + "X-API-Key": process.env.TEC_API_KEY || "", "Content-Type": "application/json", }, }) @@ -141,7 +137,7 @@ export const prodUser = async (data: any) => { if (grante) { console.log(newE.response.data); return { - succes: false, + success: false, message: `${data.username} encountered an error updating..`, data: newE.response.data, }; @@ -149,8 +145,59 @@ export const prodUser = async (data: any) => { } return { - succes: true, + success: true, message: `${data.username} was just created or updated.`, data: [], }; }; + +const deleteUser = async (data: any, permRoleCheck: any) => { + const remove = await prodEndpointCreation( + `/public/v1.0/Administration/User/${data.username}` + ); + const newurl = await prodEndpointCreation( + `/public/v1.0/Administration/User` + ); + + const { data: removal, error: grante } = (await tryCatch( + axios.delete(remove, { + headers: { + "X-API-Key": process.env.TEC_API_KEY || "", + "Content-Type": "application/json", + }, + }) + )) as any; + + const newUser: any = { + userId: data.username, + remark: data.remark, + languageCode: "en", + active: true, + roles: permRoleCheck[0].roles, + rolesLegacy: permRoleCheck[0].rolesLegacy, + }; + + const { data: newU, error: newE } = (await tryCatch( + axios.post(newurl, newUser, { + headers: { + "X-API-Key": process.env.TEC_API_KEY || "", + "Content-Type": "application/json", + }, + }) + )) as any; + + if (newE) { + console.log(newE); + return { + success: false, + message: `${data.username} encountered an error creating..`, + data: newE.response.data, + }; + } + + return { + success: true, + message: `${data.username} was just created.`, + data: [], + }; +}; diff --git a/server/services/prodUser/routes/addProdRole.ts b/server/services/prodUser/routes/addProdRole.ts index 6ba8e1b..ccaf6d3 100644 --- a/server/services/prodUser/routes/addProdRole.ts +++ b/server/services/prodUser/routes/addProdRole.ts @@ -34,7 +34,7 @@ app.openapi( } return c.json({ - success: data.succes, + success: data.success, message: data.message, data: data.data, }); diff --git a/server/services/prodUser/routes/getProdRoles.ts b/server/services/prodUser/routes/getProdRoles.ts new file mode 100644 index 0000000..f29c29b --- /dev/null +++ b/server/services/prodUser/routes/getProdRoles.ts @@ -0,0 +1,36 @@ +// an external way to creating logs +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 { prodUser } from "../controller/produser.js"; +import { getProdRoles } from "../controller/getprodRoles.js"; + +const app = new OpenAPIHono({ strict: false }); + +app.openapi( + createRoute({ + tags: ["admin"], + summary: "Returns the current prod roles", + method: "get", + path: "/prodrole", + responses: responses(), + }), + async (c) => { + const { data, error } = await tryCatch(getProdRoles()); + apiHit(c, { endpoint: "/prodrole" }); + if (error) { + return c.json({ + success: false, + message: "Error getting new role.", + }); + } + + return c.json({ + success: data.success, + message: data.message, + data: data.data, + }); + } +); +export default app; diff --git a/server/services/prodUser/routes/produser.ts b/server/services/prodUser/routes/produser.ts index aa5f69e..9b1e672 100644 --- a/server/services/prodUser/routes/produser.ts +++ b/server/services/prodUser/routes/produser.ts @@ -36,7 +36,7 @@ app.openapi( } return c.json({ - success: data.succes, + success: data.success, message: data.message, data: data.data, }); diff --git a/server/services/prodUser/utils/prodRoles.ts b/server/services/prodUser/utils/prodRoles.ts index eb6a3ec..63ee588 100644 --- a/server/services/prodUser/utils/prodRoles.ts +++ b/server/services/prodUser/utils/prodRoles.ts @@ -56,7 +56,7 @@ const newProdRoles: any = [ "DemandManagement\\Forecast\\ProcessAdmin", "Manufacturing\\ProductionLabelling\\ProcessAdmin", ], - rolesLegacy: [4, 55, 145, 95, 45, 105, 65, 15, 125], + rolesLegacy: [3, 55, 145, 95, 45, 105, 65, 15, 125], }, // regional ]; diff --git a/server/services/server/utils/settingsCheck.ts b/server/services/server/utils/settingsCheck.ts index 245da17..ea3d9ce 100644 --- a/server/services/server/utils/settingsCheck.ts +++ b/server/services/server/utils/settingsCheck.ts @@ -236,6 +236,14 @@ const newSettings = [ serviceBelowsTo: "logistics", roleToChange: "admin", }, + { + name: "stagingReturnLocations", + value: `30125,31523`, + description: + "What are the staging location IDs we will use to select from. seperated by commas", + serviceBelowsTo: "logistics", + roleToChange: "admin", + }, // temp settings can be deleted at a later date once that code is removed { name: "siloAdjMigrations", diff --git a/server/services/server/utils/subModuleCheck.ts b/server/services/server/utils/subModuleCheck.ts index e3c1b10..5be29cc 100644 --- a/server/services/server/utils/subModuleCheck.ts +++ b/server/services/server/utils/subModuleCheck.ts @@ -172,6 +172,13 @@ const newSubModules = [ newWindow: false, isActive: true, }, + { + name: "Prod Perms", + link: "/produsers", + icon: "Users", + newWindow: false, + isActive: true, + }, { name: "UCD", link: "https://ucd.alpla.net:8443/",