feat(produser): added in the function to create a standard user based on there username

This commit is contained in:
2025-06-12 21:10:46 -05:00
parent 3283972809
commit 99ad79c662
13 changed files with 438 additions and 35 deletions

View File

@@ -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 (
<>
<p>There was an error loading the prod Perms</p>
</>
);
if (isLoading)
return (
<>
<p>Loading.....</p>
</>
);
return (
<div className="m-4">
<LstCard>
<CardHeader>
<p>Alpla Prod user create/update</p>
</CardHeader>
<div className="m-2">
<p>
Please enter the windows username. <br /> To check the
user head here{" "}
<a
target="_blank"
href="https://alplaservicedesk.service-now.com/sp?id=sc_cat_item&sys_id=0c266831c32f41107cba16c4e40131c0&sysparm_category=564428bdc3eb41107cba16c4e40131f2"
>
<u>Active Directory</u>
</a>
</p>
</div>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
{/* Windows username */}
<form.Field
name="username"
validators={{
// We can choose between form-wide and field-specific validators
onChange: ({ value }) =>
value.length > 3
? undefined
: "Username must be longer than 3 letters",
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor="username">Username</Label>
<Input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
//type="number"
onChange={(e) =>
field.handleChange(e.target.value)
}
/>
{field.state.meta.errors.length ? (
<em>
{field.state.meta.errors.join(",")}
</em>
) : null}
</div>
);
}}
/>
{/* Remark for the user */}
<form.Field
name="remark"
validators={{
// We can choose between form-wide and field-specific validators
onChange: ({ value }) =>
value.length > 3
? undefined
: "The remark should be longer than 3 letters",
}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor="remark">Remark</Label>
<Input
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
//type="number"
onChange={(e) =>
field.handleChange(e.target.value)
}
/>
{field.state.meta.errors.length ? (
<em>
{field.state.meta.errors.join(",")}
</em>
) : null}
</div>
);
}}
/>
{/* Select the type of role we will be granting/updating */}
<form.Field
name="role"
//listeners={{onChange: ({value})=>{}}}
children={(field) => {
return (
<div className="m-2 min-w-48 max-w-96 p-2">
<Label htmlFor={field.name}>
Select role
</Label>
<Select
value={field.state.value}
onValueChange={field.handleChange}
>
<SelectTrigger className="w-[180px]">
<SelectValue
id={field.name}
placeholder="Select Role"
/>
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Roles</SelectLabel>
{data.map((i: any) => {
return (
<SelectItem
key={i.prodPerm_id}
value={i.name}
>
{i.name}
</SelectItem>
);
})}
</SelectGroup>
</SelectContent>
</Select>
</div>
);
}}
/>
<div className="flex justify-end mr-3">
<Button onClick={form.handleSubmit}>Submit</Button>
</div>
</form>
</LstCard>
</div>
);
}

View File

@@ -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"

View File

@@ -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 (
<div>
<ProdUserCard />
</div>
);
}

View File

@@ -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;
};

View File

@@ -4,20 +4,22 @@ 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;
};

View File

@@ -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,
};
};

View File

@@ -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: [],
};
};

View File

@@ -34,7 +34,7 @@ app.openapi(
}
return c.json({
success: data.succes,
success: data.success,
message: data.message,
data: data.data,
});

View File

@@ -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;

View File

@@ -36,7 +36,7 @@ app.openapi(
}
return c.json({
success: data.succes,
success: data.success,
message: data.message,
data: data.data,
});

View File

@@ -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
];

View File

@@ -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",

View File

@@ -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/",