From cae9b41bb93b08abd984d207a415a5e684c99634 Mon Sep 17 00:00:00 2001 From: mattriggle05 Date: Mon, 1 Jun 2026 15:16:13 -0500 Subject: [PATCH 1/7] Update types with new fields from API --- types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/types.ts b/types.ts index 3f6a5a3..26ff7b0 100644 --- a/types.ts +++ b/types.ts @@ -6,6 +6,8 @@ export type FormStatusEnum = "PENDING" | "APPROVED" | "DENIED"; export type EntityManagerEnum = "APPLICATION" | "MANIFEST" | "MORGRIDGE_ACTIVE_DIRECTORY"; +export type GroupTypeEnum = "SUBMIT_NODE" + export interface TokenGet { id: number; created_by: number; @@ -65,16 +67,20 @@ export interface UserSubmitGet { export interface Group { id: number; name: string; + description: string | null; point_of_contact: User | null; unix_gid: number | null; has_groupdir: boolean | null; + type: GroupTypeEnum | null; } export interface GroupCreateUpdate { name: string; + description?: string | null; point_of_contact?: number | null; unix_gid?: number | null; has_groupdir?: boolean | null; + type?: GroupTypeEnum | null; } export interface Project { @@ -241,9 +247,11 @@ export interface UserGroupView { created_at: string | null; updated_at: string | null; name: string; + description: string | null; point_of_contact: User | null; unix_gid: number | null; has_groupdir: boolean; + type: GroupTypeEnum | null; } // Returned by GET /groups/{group_id}/users From 791ad6996d4fe618a093715aeb663b85becfec15 Mon Sep 17 00:00:00 2001 From: mattriggle05 Date: Tue, 2 Jun 2026 12:18:51 -0500 Subject: [PATCH 2/7] Hide submit node section on user form --- src/components/Forms/UserForm/UserForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Forms/UserForm/UserForm.tsx b/src/components/Forms/UserForm/UserForm.tsx index c20517f..9c6ec36 100644 --- a/src/components/Forms/UserForm/UserForm.tsx +++ b/src/components/Forms/UserForm/UserForm.tsx @@ -365,8 +365,8 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi - - + + {/* Submit Nodes @@ -466,7 +466,7 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi - + */} ); From 9aa72b8a4c7abce70f0c16b0ed3896276d12d165 Mon Sep 17 00:00:00 2001 From: mattriggle05 Date: Tue, 2 Jun 2026 14:45:04 -0500 Subject: [PATCH 3/7] Add group type to tables --- src/app/groups/page.tsx | 4 +++- src/components/UserGroupTable/UserGroupTable.tsx | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/groups/page.tsx b/src/app/groups/page.tsx index 037ebcd..2be9083 100644 --- a/src/app/groups/page.tsx +++ b/src/app/groups/page.tsx @@ -3,7 +3,7 @@ import GenericTableView from "@/src/components/GenericTableView/GenericTableView"; import { createCellRenderer } from "@/src/utils/cellRenderers"; -const headers = ["id", "Group Name", "Point of Contact", "GID", "Has Group Directory?"]; +const headers = ["id", "Group Name", "Type", "Point of Contact", "GID", "Has Group Directory?"]; const cellRenderer = createCellRenderer({ editPath: "/groups/edit" }); function Page() { @@ -14,6 +14,7 @@ function Page() { cellRenderer={cellRenderer} sortableColumns={{ "Group Name": "name", + "Type": "type", "Point of Contact": "point_of_contact", GID: "unix_gid", }} @@ -27,6 +28,7 @@ function Page() { const data = result.data.map((group) => [ group.id, group.name, + group.type ?? "", group.point_of_contact?.name ?? group.point_of_contact?.email1 ?? "", group.unix_gid ?? "", group.has_groupdir ? "Yes" : "No", diff --git a/src/components/UserGroupTable/UserGroupTable.tsx b/src/components/UserGroupTable/UserGroupTable.tsx index 719a883..0bee2d3 100644 --- a/src/components/UserGroupTable/UserGroupTable.tsx +++ b/src/components/UserGroupTable/UserGroupTable.tsx @@ -18,6 +18,7 @@ const UserProjectTable = ({ userId, adminView = false }: UserProjectTableProps) Name + Type Point Of Contact Unix GID Managed By @@ -34,6 +35,7 @@ const UserProjectTable = ({ userId, adminView = false }: UserProjectTableProps) {group.name} {adminView && ( )} + {group.type ?? ""} {group.point_of_contact?.name ?? group.point_of_contact?.email1 ?? ""} {group.unix_gid} From b4a4143ca1b743b4056a0df02ac84aa7cfbbc043 Mon Sep 17 00:00:00 2001 From: mattriggle05 Date: Thu, 4 Jun 2026 12:21:07 -0500 Subject: [PATCH 4/7] Update email list generator to use submit node groups --- src/app/email/page.tsx | 6 +-- .../SubmitNodeAutocomplete.tsx | 45 +++++++++++++++---- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/app/email/page.tsx b/src/app/email/page.tsx index e220e0c..70e7e68 100644 --- a/src/app/email/page.tsx +++ b/src/app/email/page.tsx @@ -4,7 +4,7 @@ import { AuthGuard } from "@/src/components/AuthGuard"; import { apiFetch } from "@/src/components/AuthProvider"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import SubmitNodeAutocomplete from "@/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete"; -import type { JoinedProjectView, Project, SubmitNode, User } from "@/types"; +import type { Group, JoinedProjectView, Project, SubmitNode, User } from "@/types"; import CheckIcon from "@mui/icons-material/Check"; import ContentCopyIcon from "@mui/icons-material/ContentCopy"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; @@ -83,7 +83,7 @@ async function fetchEmails(filters: CommittedFilters): Promise { if (!res.ok) throw new Error(await res.text()); const data: User[] = await res.json(); - const users = data.filter((u) => u.submit_nodes?.some((sn) => sn.submit_node_id === submitNodeId)); + const users = data.filter((u) => u.groups?.some((g) => g.group_id === submitNodeId)); return toEmails(users); } @@ -101,7 +101,7 @@ function Page() { const [status, setStatus] = useState("all"); const [role, setRole] = useState("any"); const [selectedProject, setSelectedProject] = useState(null); - const [selectedSubmitNode, setSelectedSubmitNode] = useState(null); + const [selectedSubmitNode, setSelectedSubmitNode] = useState(null); const [committedFilters, setCommittedFilters] = useState(null); const [copied, setCopied] = useState(false); diff --git a/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx b/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx index 8645ea4..6b6fd9f 100644 --- a/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx +++ b/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx @@ -1,22 +1,49 @@ import { GenericAutocomplete } from "@/src/components/GenericAutocomplete/GenericAutocomplete"; -import type { SubmitNode } from "@/types"; +import type { Group, SubmitNode } from "@/types"; + +// interface SubmitNodeAutocompleteProps { +// value?: Partial; +// onSelect: (submitNode: SubmitNode | null) => void; +// defaultFilter?: Record; +// required?: boolean; +// disabled?: boolean; +// } + +// const SubmitNodeAutocomplete = ({ value, onSelect, defaultFilter, required, disabled }: SubmitNodeAutocompleteProps) => { +// return ( +// +// endpoint="/submit_nodes" +// label="Select Submit Node" +// value={value} +// onSelect={onSelect} +// defaultFilter={defaultFilter} +// getOptionLabel={(option) => option.name} +// searchField="name" +// required={required} +// disabled={disabled} +// /> +// ); +// }; + +// export default SubmitNodeAutocomplete; interface SubmitNodeAutocompleteProps { - value?: Partial; - onSelect: (submitNode: SubmitNode | null) => void; - defaultFilter?: Record; + value?: Partial; + onSelect: (submitNode: Group | null) => void; required?: boolean; disabled?: boolean; } -const SubmitNodeAutocomplete = ({ value, onSelect, defaultFilter, required, disabled }: SubmitNodeAutocompleteProps) => { +const SubmitNodeAutocomplete = ({ value, onSelect, required, disabled }: SubmitNodeAutocompleteProps) => { return ( - - endpoint="/submit_nodes" + + endpoint="/groups" label="Select Submit Node" value={value} onSelect={onSelect} - defaultFilter={defaultFilter} + defaultFilter={{ + "type": "eq.SUBMIT_NODE" + }} getOptionLabel={(option) => option.name} searchField="name" required={required} @@ -25,4 +52,4 @@ const SubmitNodeAutocomplete = ({ value, onSelect, defaultFilter, required, disa ); }; -export default SubmitNodeAutocomplete; +export default SubmitNodeAutocomplete; \ No newline at end of file From 8e6f24415cf9a9db332b6d01b02f7b05b9d8bb99 Mon Sep 17 00:00:00 2001 From: mattriggle05 Date: Thu, 4 Jun 2026 15:04:02 -0500 Subject: [PATCH 5/7] Update user form approval to send group ids over submit nodes --- .../UserApplicationEditForm.tsx | 32 +++++++++---------- types.ts | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx b/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx index 8649fc7..14bbb9c 100644 --- a/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx +++ b/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx @@ -4,7 +4,7 @@ import type { ApiError } from "@/src/components/Forms/UserForm/UserForm"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import SubmitNodeAutocomplete from "@/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete"; import { apiFetch } from "@/src/components/AuthProvider"; -import type { FormStatusEnum, PositionEnum, Project, SubmitNode, User, UserForm, UserFormPatch } from "@/types"; +import type { FormStatusEnum, Group, PositionEnum, Project, SubmitNode, User, UserForm, UserFormPatch } from "@/types"; import { Alert, AlertTitle, Box, @@ -75,8 +75,8 @@ export function UserApplicationEditForm({ const [step, setStep] = useState("initial"); const [project, setProject] = useState(null); const [userPosition, setUserPosition] = useState(initialValues?.position ?? ""); - const [selectedSubmitNode, setSelectedSubmitNode] = useState(null); - const [submitNodes, setSubmitNodes] = useState([]); + const [selectedSubmitNodeGroup, setSelectedSubmitNodeGroup] = useState(null); + const [submitNodeGroups, setSubmitNodeGroups] = useState([]); const [email, setEmail] = useState(initialValues?.email ?? ""); const content = initialValues?.content; const piDisplay = initialValues?.pi_name ?? `User ID ${initialValues?.pi_id ?? "-"}`; @@ -90,8 +90,8 @@ export function UserApplicationEditForm({ const userWasPreviouslyActive = useMemo(() => { return Boolean( (applicant?.groups?.length ?? 0) || - (applicant?.projects?.length ?? 0) || - (applicant?.submit_nodes?.length ?? 0) + (applicant?.projects?.length ?? 0) + // (applicant?.submit_nodes?.length ?? 0) ); }, [applicant]); @@ -99,9 +99,9 @@ export function UserApplicationEditForm({ if (needsEmail && !email.trim()) return "Enter the user's email address before approving."; if (!project) return "Select a project before approving."; if (userPosition === "") return "Choose a project position before approving."; - if (submitNodes.length === 0) return "Add at least one submit node before approving."; + if (submitNodeGroups.length === 0) return "Add at least one submit node before approving."; return null; - }, [needsEmail, email, project, userPosition, submitNodes.length]); + }, [needsEmail, email, project, userPosition, submitNodeGroups.length]); const handleDeny = async () => { await onSubmit({ status: "DENIED" }); @@ -124,15 +124,15 @@ export function UserApplicationEditForm({ }; const handleAddSubmitNode = () => { - if (!selectedSubmitNode || submitNodes.some((node) => node.id === selectedSubmitNode.id)) { + if (!selectedSubmitNodeGroup || submitNodeGroups.some((node) => node.id === selectedSubmitNodeGroup.id)) { return; } - setSubmitNodes((current) => [...current, selectedSubmitNode]); - setSelectedSubmitNode(null); + setSubmitNodeGroups((current) => [...current, selectedSubmitNodeGroup]); + setSelectedSubmitNodeGroup(null); }; const handleRemoveSubmitNode = (id: number) => { - setSubmitNodes((current) => current.filter((node) => node.id !== id)); + setSubmitNodeGroups((current) => current.filter((node) => node.id !== id)); }; const handleSubmit = async (event: React.FormEvent) => { @@ -144,7 +144,7 @@ export function UserApplicationEditForm({ email: needsEmail ? email.trim() || undefined : undefined, project_id: project?.id ?? 0, user_position: userPosition as PositionEnum, - submit_nodes: submitNodes.map((node) => ({ submit_node_id: node.id })), + submit_node_group_ids: submitNodeGroups.map((group) => (group.id)), }); }; @@ -325,8 +325,8 @@ export function UserApplicationEditForm({ @@ -334,13 +334,13 @@ export function UserApplicationEditForm({ type="button" variant="outlined" onClick={handleAddSubmitNode} - disabled={!selectedSubmitNode || isSubmitting} + disabled={!selectedSubmitNodeGroup || isSubmitting} > Add Submit Node - {submitNodes.map((node) => ( + {submitNodeGroups.map((node) => ( Date: Mon, 8 Jun 2026 15:07:08 -0500 Subject: [PATCH 6/7] Commnet out references to submit nodes --- src/app/email/page.tsx | 2 +- src/components/AuthProvider.tsx | 21 ++-- .../UserApplicationEditForm.tsx | 2 +- src/components/Forms/UserForm/UserForm.tsx | 113 ++++++++++-------- .../SubmitNodeAutocomplete.tsx | 2 +- types.ts | 52 ++++---- 6 files changed, 105 insertions(+), 87 deletions(-) diff --git a/src/app/email/page.tsx b/src/app/email/page.tsx index 70e7e68..503e4f8 100644 --- a/src/app/email/page.tsx +++ b/src/app/email/page.tsx @@ -4,7 +4,7 @@ import { AuthGuard } from "@/src/components/AuthGuard"; import { apiFetch } from "@/src/components/AuthProvider"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import SubmitNodeAutocomplete from "@/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete"; -import type { Group, JoinedProjectView, Project, SubmitNode, User } from "@/types"; +import type { Group, JoinedProjectView, Project, User } from "@/types"; // TODO: Remove this — removed SubmitNode (submit nodes → SUBMIT_NODE groups) import CheckIcon from "@mui/icons-material/Check"; import ContentCopyIcon from "@mui/icons-material/ContentCopy"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; diff --git a/src/components/AuthProvider.tsx b/src/components/AuthProvider.tsx index 51eeca5..c781595 100644 --- a/src/components/AuthProvider.tsx +++ b/src/components/AuthProvider.tsx @@ -12,7 +12,8 @@ import type { Project, ProjectCreateUpdate, RouteGet, - SubmitNode, + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // SubmitNode, TokenGet, TokenGetFull, TokenPost, @@ -72,8 +73,9 @@ export type ApiClient = { // PI Projects getPiProjects: (params?: PaginationParams) => Promise>; + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups; /submit_nodes endpoint removed // Submit Nodes - getSubmitNodes: (params?: PaginationParams) => Promise>; + // getSubmitNodes: (params?: PaginationParams) => Promise>; // Tokens getTokens: (params?: PaginationParams) => Promise>; @@ -387,14 +389,15 @@ export function AuthClientProvider({ children }: { children: React.ReactNode }) return { data, totalCount }; }, []), + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups; /submit_nodes endpoint removed // Submit Nodes - getSubmitNodes: useCallback(async (params?: PaginationParams): Promise> => { - const response = await apiFetch(`/submit_nodes${buildQuery(params)}`); - if (!response.ok) throw new Error(`Failed to get submit nodes: ${response.statusText}`); - const totalCount = parseInt(response.headers.get("X-Total-Count") || "0", 10); - const data = await response.json(); - return { data, totalCount }; - }, []), + // getSubmitNodes: useCallback(async (params?: PaginationParams): Promise> => { + // const response = await apiFetch(`/submit_nodes${buildQuery(params)}`); + // if (!response.ok) throw new Error(`Failed to get submit nodes: ${response.statusText}`); + // const totalCount = parseInt(response.headers.get("X-Total-Count") || "0", 10); + // const data = await response.json(); + // return { data, totalCount }; + // }, []), // Tokens getTokens: useCallback(async (params?: PaginationParams): Promise> => { diff --git a/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx b/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx index 14bbb9c..b09ddfa 100644 --- a/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx +++ b/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx @@ -4,7 +4,7 @@ import type { ApiError } from "@/src/components/Forms/UserForm/UserForm"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import SubmitNodeAutocomplete from "@/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete"; import { apiFetch } from "@/src/components/AuthProvider"; -import type { FormStatusEnum, Group, PositionEnum, Project, SubmitNode, User, UserForm, UserFormPatch } from "@/types"; +import type { FormStatusEnum, Group, PositionEnum, Project, User, UserForm, UserFormPatch } from "@/types"; // TODO: Remove this — removed SubmitNode (submit nodes → SUBMIT_NODE groups) import { Alert, AlertTitle, Box, diff --git a/src/components/Forms/UserForm/UserForm.tsx b/src/components/Forms/UserForm/UserForm.tsx index 9c6ec36..61afe52 100644 --- a/src/components/Forms/UserForm/UserForm.tsx +++ b/src/components/Forms/UserForm/UserForm.tsx @@ -1,11 +1,12 @@ "use client"; -import { apiFetch } from "@/src/components/AuthProvider"; +// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups (only used by the removed /submit_nodes fetch) +// import { apiFetch } from "@/src/components/AuthProvider"; import FormErrorAlert from "@/src/components/FormErrorAlert/FormErrorAlert"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import { ApiError } from "@/src/utils/formErrors"; import type { PositionEnum, RoleEnum, UserCreate, UserUpdate } from "@/types"; -import { Project, SubmitNode, UserSubmitGet, UserSubmitNodeCreate } from "@/types"; +import { Project } from "@/types"; // TODO: Remove this — removed SubmitNode, UserSubmitGet, UserSubmitNodeCreate (submit nodes → SUBMIT_NODE groups) import { Box, Button, @@ -26,7 +27,8 @@ import { Typography, } from "@mui/material"; import React, { useState } from "react"; -import useSWR from "swr"; +// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups (only used by the removed /submit_nodes fetch) +// import useSWR from "swr"; export type UserFormMode = "create" | "edit"; @@ -46,7 +48,8 @@ export interface UserFormValues { position: PositionEnum | ""; primary_project_id: string; // string in form, converted to number primary_project_role: RoleEnum | ""; - submit_nodes: number[]; // store selected submit_node IDs for easier diffing + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // submit_nodes: number[]; // store selected submit_node IDs for easier diffing } export interface UserFormProps { @@ -81,7 +84,8 @@ function normalizeInitialValues(initial?: Partial): Use ? String(initial.primary_project_id) : "", primary_project_role: initial?.primary_project_role ?? "", - submit_nodes: (initial?.submit_nodes as UserSubmitNodeCreate[] | undefined)?.map((x) => x.submit_node_id) ?? [], + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // submit_nodes: (initial?.submit_nodes as UserSubmitNodeCreate[] | undefined)?.map((x) => x.submit_node_id) ?? [], }; } @@ -91,14 +95,15 @@ const normalizeComparable = (value: unknown) => { return value === "" ? null : value; }; -const arraysEqual = (a: number[] | undefined, b: number[] | undefined) => { - if (!a && !b) return true; - if (!a || !b) return false; - if (a.length !== b.length) return false; - const sortedA = [...a].sort(); - const sortedB = [...b].sort(); - return sortedA.every((val, idx) => val === sortedB[idx]); -}; +// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups (only used for the submit_nodes diff) +// const arraysEqual = (a: number[] | undefined, b: number[] | undefined) => { +// if (!a && !b) return true; +// if (!a || !b) return false; +// if (a.length !== b.length) return false; +// const sortedA = [...a].sort(); +// const sortedB = [...b].sort(); +// return sortedA.every((val, idx) => val === sortedB[idx]); +// }; // Field name mappings for error display const FIELD_NAME_MAP: Record = { @@ -112,43 +117,46 @@ const FIELD_NAME_MAP: Record = { position: "Position", primary_project_id: "Primary Project", primary_project_role: "Primary Project Role", - submit_nodes: "Submit Nodes", + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // submit_nodes: "Submit Nodes", }; export const UserForm: React.FC = ({ mode, initialValues, onSubmit, isSubmitting = false, error, adminView }) => { const [values, setValues] = useState(() => normalizeInitialValues(initialValues)); - const [selectedSubmitNodeId, setSelectedSubmitNodeId] = useState(""); + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // const [selectedSubmitNodeId, setSelectedSubmitNodeId] = useState(""); // Check if selected node is already assigned - const userSubmitNodeIds = - mode === "edit" - ? ((initialValues?.submit_nodes as UserSubmitGet[] | undefined)?.map((n) => n.submit_node_id) ?? []) - : values.submit_nodes; - const isNodeAssigned = selectedSubmitNodeId ? userSubmitNodeIds.includes(selectedSubmitNodeId as number) : false; + // const userSubmitNodeIds = + // mode === "edit" + // ? ((initialValues?.submit_nodes as UserSubmitGet[] | undefined)?.map((n) => n.submit_node_id) ?? []) + // : values.submit_nodes; + // const isNodeAssigned = selectedSubmitNodeId ? userSubmitNodeIds.includes(selectedSubmitNodeId as number) : false; const handleChange = (field: keyof UserFormValues, value: string | boolean | number[]) => { setValues((prev) => ({ ...prev, [field]: value })); }; - const handleAddSubmitNode = () => { - if (!selectedSubmitNodeId) return; - - // Add to local state - changes will be saved when form is submitted - handleChange("submit_nodes", [...values.submit_nodes, selectedSubmitNodeId as number]); - setSelectedSubmitNodeId(""); - }; - - const handleRemoveSubmitNode = () => { - if (!selectedSubmitNodeId) return; - - // Remove from local state - changes will be saved when form is submitted - const nodeId = selectedSubmitNodeId as number; - handleChange( - "submit_nodes", - values.submit_nodes.filter((id) => id !== nodeId), - ); - setSelectedSubmitNodeId(""); - }; + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // const handleAddSubmitNode = () => { + // if (!selectedSubmitNodeId) return; + // + // // Add to local state - changes will be saved when form is submitted + // handleChange("submit_nodes", [...values.submit_nodes, selectedSubmitNodeId as number]); + // setSelectedSubmitNodeId(""); + // }; + // + // const handleRemoveSubmitNode = () => { + // if (!selectedSubmitNodeId) return; + // + // // Remove from local state - changes will be saved when form is submitted + // const nodeId = selectedSubmitNodeId as number; + // handleChange( + // "submit_nodes", + // values.submit_nodes.filter((id) => id !== nodeId), + // ); + // setSelectedSubmitNodeId(""); + // }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -167,9 +175,10 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi position: values.position || null, primary_project_id: Number(values.primary_project_id), primary_project_role: values.primary_project_role as RoleEnum, - ...(values.submit_nodes?.length - ? { submit_nodes: values.submit_nodes.map((id) => ({ submit_node_id: id })) } - : {}), + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // ...(values.submit_nodes?.length + // ? { submit_nodes: values.submit_nodes.map((id) => ({ submit_node_id: id })) } + // : {}), }; await onSubmit(payload); @@ -204,20 +213,22 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi ); maybeSet("position", (values.position || null) as UserUpdate["position"], initial.position); + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups // Handle submit_nodes diff for edit: compare ID arrays, and only set if changed - const initialSubmitNodeIds = - (initial.submit_nodes as UserSubmitNodeCreate[] | undefined)?.map((x) => x.submit_node_id) ?? []; - if (!arraysEqual(values.submit_nodes, initialSubmitNodeIds)) { - updatePayload.submit_nodes = values.submit_nodes.map((id) => ({ submit_node_id: id })); - } + // const initialSubmitNodeIds = + // (initial.submit_nodes as UserSubmitNodeCreate[] | undefined)?.map((x) => x.submit_node_id) ?? []; + // if (!arraysEqual(values.submit_nodes, initialSubmitNodeIds)) { + // updatePayload.submit_nodes = values.submit_nodes.map((id) => ({ submit_node_id: id })); + // } await onSubmit(updatePayload); }; - const { data: submitNodes } = useSWR("/submit_nodes", async () => { - const response = await apiFetch("/submit_nodes"); - return response.json(); - }); + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups; /submit_nodes endpoint removed + // const { data: submitNodes } = useSWR("/submit_nodes", async () => { + // const response = await apiFetch("/submit_nodes"); + // return response.json(); + // }); return ( diff --git a/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx b/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx index 6b6fd9f..253856e 100644 --- a/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx +++ b/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx @@ -1,5 +1,5 @@ import { GenericAutocomplete } from "@/src/components/GenericAutocomplete/GenericAutocomplete"; -import type { Group, SubmitNode } from "@/types"; +import type { Group } from "@/types"; // TODO: Remove this — removed SubmitNode (submit nodes → SUBMIT_NODE groups) // interface SubmitNodeAutocompleteProps { // value?: Partial; diff --git a/types.ts b/types.ts index feccab6..53861dc 100644 --- a/types.ts +++ b/types.ts @@ -42,27 +42,28 @@ export interface RouteGet { route: string; } -export interface SubmitNode { - id: number; - name: string; -} - -export interface UserSubmitNodeCreate { - submit_node_id: number; -} - -export interface UserSubmitGet { - id: number; - submit_node_id: number; - submit_node_name: string; - user_id: number; - disk_quota: number | null; - hpc_diskquota: number | null; - hpc_inodequota: number | null; - hpc_joblimit: number | null; - hpc_corelimit: number | null; - hpc_fairshare: number | null; -} +// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups +// export interface SubmitNode { +// id: number; +// name: string; +// } + +// export interface UserSubmitNodeCreate { +// submit_node_id: number; +// } + +// export interface UserSubmitGet { +// id: number; +// submit_node_id: number; +// submit_node_name: string; +// user_id: number; +// disk_quota: number | null; +// hpc_diskquota: number | null; +// hpc_inodequota: number | null; +// hpc_joblimit: number | null; +// hpc_corelimit: number | null; +// hpc_fairshare: number | null; +// } export interface Group { id: number; @@ -139,7 +140,8 @@ export interface User { position: string; created_at: string; updated_at: string; - submit_nodes?: UserSubmitGet[]; + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // submit_nodes?: UserSubmitGet[]; notes?: Note[]; projects?: JoinedProjectView[]; groups?: UserGroupView[]; @@ -162,7 +164,8 @@ export interface UserCreate { password?: string | null; primary_project_id: number; primary_project_role: RoleEnum; - submit_nodes?: UserSubmitNodeCreate[] | null; + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // submit_nodes?: UserSubmitNodeCreate[] | null; } export interface UserUpdate { @@ -179,7 +182,8 @@ export interface UserUpdate { unix_uid?: number | null; position?: PositionEnum | null; password?: string | null; - submit_nodes?: UserSubmitNodeCreate[] | null; + // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups + // submit_nodes?: UserSubmitNodeCreate[] | null; } export interface Note { From f885b6c05ec439c66c908bef707ccce2e400a268 Mon Sep 17 00:00:00 2001 From: mattriggle05 Date: Tue, 16 Jun 2026 16:25:36 -0500 Subject: [PATCH 7/7] Remove commented out unused code --- scripts/dev/nginx.conf | 2 +- src/app/email/page.tsx | 2 +- src/components/AuthProvider.tsx | 16 -- .../UserApplicationEditForm.tsx | 3 +- src/components/Forms/UserForm/UserForm.tsx | 183 +----------------- .../SubmitNodeAutocomplete.tsx | 28 +-- types.ts | 29 --- 7 files changed, 7 insertions(+), 256 deletions(-) diff --git a/scripts/dev/nginx.conf b/scripts/dev/nginx.conf index f9fb7ef..b711565 100644 --- a/scripts/dev/nginx.conf +++ b/scripts/dev/nginx.conf @@ -53,7 +53,7 @@ http { # Disable buffering if you have large payloads proxy_request_buffering off; - proxy_pass https://userapp.chtcdev.chtc.io; + proxy_pass http://host.docker.internal:8080/; } location / { diff --git a/src/app/email/page.tsx b/src/app/email/page.tsx index 503e4f8..b0e623e 100644 --- a/src/app/email/page.tsx +++ b/src/app/email/page.tsx @@ -4,7 +4,7 @@ import { AuthGuard } from "@/src/components/AuthGuard"; import { apiFetch } from "@/src/components/AuthProvider"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import SubmitNodeAutocomplete from "@/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete"; -import type { Group, JoinedProjectView, Project, User } from "@/types"; // TODO: Remove this — removed SubmitNode (submit nodes → SUBMIT_NODE groups) +import type { Group, JoinedProjectView, Project, User } from "@/types"; import CheckIcon from "@mui/icons-material/Check"; import ContentCopyIcon from "@mui/icons-material/ContentCopy"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; diff --git a/src/components/AuthProvider.tsx b/src/components/AuthProvider.tsx index c781595..c8750f6 100644 --- a/src/components/AuthProvider.tsx +++ b/src/components/AuthProvider.tsx @@ -12,8 +12,6 @@ import type { Project, ProjectCreateUpdate, RouteGet, - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // SubmitNode, TokenGet, TokenGetFull, TokenPost, @@ -73,10 +71,6 @@ export type ApiClient = { // PI Projects getPiProjects: (params?: PaginationParams) => Promise>; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups; /submit_nodes endpoint removed - // Submit Nodes - // getSubmitNodes: (params?: PaginationParams) => Promise>; - // Tokens getTokens: (params?: PaginationParams) => Promise>; getToken: (tokenId: number) => Promise; @@ -389,16 +383,6 @@ export function AuthClientProvider({ children }: { children: React.ReactNode }) return { data, totalCount }; }, []), - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups; /submit_nodes endpoint removed - // Submit Nodes - // getSubmitNodes: useCallback(async (params?: PaginationParams): Promise> => { - // const response = await apiFetch(`/submit_nodes${buildQuery(params)}`); - // if (!response.ok) throw new Error(`Failed to get submit nodes: ${response.statusText}`); - // const totalCount = parseInt(response.headers.get("X-Total-Count") || "0", 10); - // const data = await response.json(); - // return { data, totalCount }; - // }, []), - // Tokens getTokens: useCallback(async (params?: PaginationParams): Promise> => { const response = await apiFetch(`/tokens${buildQuery(params)}`); diff --git a/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx b/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx index b09ddfa..029d335 100644 --- a/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx +++ b/src/components/Forms/UserApplicationForm/UserApplicationEditForm.tsx @@ -4,7 +4,7 @@ import type { ApiError } from "@/src/components/Forms/UserForm/UserForm"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import SubmitNodeAutocomplete from "@/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete"; import { apiFetch } from "@/src/components/AuthProvider"; -import type { FormStatusEnum, Group, PositionEnum, Project, User, UserForm, UserFormPatch } from "@/types"; // TODO: Remove this — removed SubmitNode (submit nodes → SUBMIT_NODE groups) +import type { FormStatusEnum, Group, PositionEnum, Project, User, UserForm, UserFormPatch } from "@/types"; import { Alert, AlertTitle, Box, @@ -91,7 +91,6 @@ export function UserApplicationEditForm({ return Boolean( (applicant?.groups?.length ?? 0) || (applicant?.projects?.length ?? 0) - // (applicant?.submit_nodes?.length ?? 0) ); }, [applicant]); diff --git a/src/components/Forms/UserForm/UserForm.tsx b/src/components/Forms/UserForm/UserForm.tsx index 61afe52..4cd5928 100644 --- a/src/components/Forms/UserForm/UserForm.tsx +++ b/src/components/Forms/UserForm/UserForm.tsx @@ -1,12 +1,10 @@ "use client"; -// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups (only used by the removed /submit_nodes fetch) -// import { apiFetch } from "@/src/components/AuthProvider"; import FormErrorAlert from "@/src/components/FormErrorAlert/FormErrorAlert"; import ProjectAutocomplete from "@/src/components/ProjectAutocomplete/ProjectAutocomplete"; import { ApiError } from "@/src/utils/formErrors"; import type { PositionEnum, RoleEnum, UserCreate, UserUpdate } from "@/types"; -import { Project } from "@/types"; // TODO: Remove this — removed SubmitNode, UserSubmitGet, UserSubmitNodeCreate (submit nodes → SUBMIT_NODE groups) +import { Project } from "@/types"; import { Box, Button, @@ -18,17 +16,10 @@ import { MenuItem, Select, Stack, - Table, - TableBody, - TableCell, - TableHead, - TableRow, TextField, Typography, } from "@mui/material"; import React, { useState } from "react"; -// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups (only used by the removed /submit_nodes fetch) -// import useSWR from "swr"; export type UserFormMode = "create" | "edit"; @@ -48,8 +39,6 @@ export interface UserFormValues { position: PositionEnum | ""; primary_project_id: string; // string in form, converted to number primary_project_role: RoleEnum | ""; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // submit_nodes: number[]; // store selected submit_node IDs for easier diffing } export interface UserFormProps { @@ -64,7 +53,7 @@ export interface UserFormProps { onSubmit: (payload: UserCreate | Partial) => Promise | void; isSubmitting?: boolean; error?: string | ApiError | null; - adminView?: boolean; // whether to show admin-only fields in submit node table + adminView?: boolean; // whether to show admin-only fields } function normalizeInitialValues(initial?: Partial): UserFormValues { @@ -84,8 +73,6 @@ function normalizeInitialValues(initial?: Partial): Use ? String(initial.primary_project_id) : "", primary_project_role: initial?.primary_project_role ?? "", - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // submit_nodes: (initial?.submit_nodes as UserSubmitNodeCreate[] | undefined)?.map((x) => x.submit_node_id) ?? [], }; } @@ -95,16 +82,6 @@ const normalizeComparable = (value: unknown) => { return value === "" ? null : value; }; -// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups (only used for the submit_nodes diff) -// const arraysEqual = (a: number[] | undefined, b: number[] | undefined) => { -// if (!a && !b) return true; -// if (!a || !b) return false; -// if (a.length !== b.length) return false; -// const sortedA = [...a].sort(); -// const sortedB = [...b].sort(); -// return sortedA.every((val, idx) => val === sortedB[idx]); -// }; - // Field name mappings for error display const FIELD_NAME_MAP: Record = { name: "Name", @@ -117,47 +94,15 @@ const FIELD_NAME_MAP: Record = { position: "Position", primary_project_id: "Primary Project", primary_project_role: "Primary Project Role", - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // submit_nodes: "Submit Nodes", }; export const UserForm: React.FC = ({ mode, initialValues, onSubmit, isSubmitting = false, error, adminView }) => { const [values, setValues] = useState(() => normalizeInitialValues(initialValues)); - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // const [selectedSubmitNodeId, setSelectedSubmitNodeId] = useState(""); - - // Check if selected node is already assigned - // const userSubmitNodeIds = - // mode === "edit" - // ? ((initialValues?.submit_nodes as UserSubmitGet[] | undefined)?.map((n) => n.submit_node_id) ?? []) - // : values.submit_nodes; - // const isNodeAssigned = selectedSubmitNodeId ? userSubmitNodeIds.includes(selectedSubmitNodeId as number) : false; - const handleChange = (field: keyof UserFormValues, value: string | boolean | number[]) => { + const handleChange = (field: keyof UserFormValues, value: string | boolean) => { setValues((prev) => ({ ...prev, [field]: value })); }; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // const handleAddSubmitNode = () => { - // if (!selectedSubmitNodeId) return; - // - // // Add to local state - changes will be saved when form is submitted - // handleChange("submit_nodes", [...values.submit_nodes, selectedSubmitNodeId as number]); - // setSelectedSubmitNodeId(""); - // }; - // - // const handleRemoveSubmitNode = () => { - // if (!selectedSubmitNodeId) return; - // - // // Remove from local state - changes will be saved when form is submitted - // const nodeId = selectedSubmitNodeId as number; - // handleChange( - // "submit_nodes", - // values.submit_nodes.filter((id) => id !== nodeId), - // ); - // setSelectedSubmitNodeId(""); - // }; - const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -175,10 +120,6 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi position: values.position || null, primary_project_id: Number(values.primary_project_id), primary_project_role: values.primary_project_role as RoleEnum, - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // ...(values.submit_nodes?.length - // ? { submit_nodes: values.submit_nodes.map((id) => ({ submit_node_id: id })) } - // : {}), }; await onSubmit(payload); @@ -213,23 +154,9 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi ); maybeSet("position", (values.position || null) as UserUpdate["position"], initial.position); - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // Handle submit_nodes diff for edit: compare ID arrays, and only set if changed - // const initialSubmitNodeIds = - // (initial.submit_nodes as UserSubmitNodeCreate[] | undefined)?.map((x) => x.submit_node_id) ?? []; - // if (!arraysEqual(values.submit_nodes, initialSubmitNodeIds)) { - // updatePayload.submit_nodes = values.submit_nodes.map((id) => ({ submit_node_id: id })); - // } - await onSubmit(updatePayload); }; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups; /submit_nodes endpoint removed - // const { data: submitNodes } = useSWR("/submit_nodes", async () => { - // const response = await apiFetch("/submit_nodes"); - // return response.json(); - // }); - return ( @@ -376,111 +303,7 @@ export const UserForm: React.FC = ({ mode, initialValues, onSubmi - - {/* - - Submit Nodes - - {adminView && ( - - - Select Submit Node - - - - - - )} - - - - Name - {mode === "edit" && ( - <> - Disk Quota - HPC Disk - HPC Inode - Job Limit - Core Limit - Fairshare - - )} - - - - {values.submit_nodes.length > 0 ? ( - values.submit_nodes.map((nodeId) => { - const node = submitNodes?.find((n) => n.id === nodeId); - if (!node) return null; - - if (mode === "edit") { - // For edit mode, try to get quota info from initialValues - const initialNode = (initialValues?.submit_nodes as UserSubmitGet[] | undefined)?.find( - (n) => n.submit_node_id === nodeId, - ); - return ( - - {node.name} - {initialNode?.disk_quota ?? ""} - {initialNode?.hpc_diskquota ?? ""} - {initialNode?.hpc_inodequota ?? ""} - {initialNode?.hpc_joblimit ?? ""} - {initialNode?.hpc_corelimit ?? ""} - {initialNode?.hpc_fairshare ?? ""} - - ); - } else { - // For create mode, just show the name - return ( - - {node.name} - - ); - } - }) - ) : ( - - - No submit nodes selected - - - )} - -
-
-
*/} ); }; - -export default UserForm; diff --git a/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx b/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx index 253856e..3e89b96 100644 --- a/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx +++ b/src/components/SubmitNodeAutocomplete/SubmitNodeAutocomplete.tsx @@ -1,31 +1,5 @@ import { GenericAutocomplete } from "@/src/components/GenericAutocomplete/GenericAutocomplete"; -import type { Group } from "@/types"; // TODO: Remove this — removed SubmitNode (submit nodes → SUBMIT_NODE groups) - -// interface SubmitNodeAutocompleteProps { -// value?: Partial; -// onSelect: (submitNode: SubmitNode | null) => void; -// defaultFilter?: Record; -// required?: boolean; -// disabled?: boolean; -// } - -// const SubmitNodeAutocomplete = ({ value, onSelect, defaultFilter, required, disabled }: SubmitNodeAutocompleteProps) => { -// return ( -// -// endpoint="/submit_nodes" -// label="Select Submit Node" -// value={value} -// onSelect={onSelect} -// defaultFilter={defaultFilter} -// getOptionLabel={(option) => option.name} -// searchField="name" -// required={required} -// disabled={disabled} -// /> -// ); -// }; - -// export default SubmitNodeAutocomplete; +import type { Group } from "@/types"; interface SubmitNodeAutocompleteProps { value?: Partial; diff --git a/types.ts b/types.ts index 53861dc..7c14776 100644 --- a/types.ts +++ b/types.ts @@ -42,29 +42,6 @@ export interface RouteGet { route: string; } -// TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups -// export interface SubmitNode { -// id: number; -// name: string; -// } - -// export interface UserSubmitNodeCreate { -// submit_node_id: number; -// } - -// export interface UserSubmitGet { -// id: number; -// submit_node_id: number; -// submit_node_name: string; -// user_id: number; -// disk_quota: number | null; -// hpc_diskquota: number | null; -// hpc_inodequota: number | null; -// hpc_joblimit: number | null; -// hpc_corelimit: number | null; -// hpc_fairshare: number | null; -// } - export interface Group { id: number; name: string; @@ -140,8 +117,6 @@ export interface User { position: string; created_at: string; updated_at: string; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // submit_nodes?: UserSubmitGet[]; notes?: Note[]; projects?: JoinedProjectView[]; groups?: UserGroupView[]; @@ -164,8 +139,6 @@ export interface UserCreate { password?: string | null; primary_project_id: number; primary_project_role: RoleEnum; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // submit_nodes?: UserSubmitNodeCreate[] | null; } export interface UserUpdate { @@ -182,8 +155,6 @@ export interface UserUpdate { unix_uid?: number | null; position?: PositionEnum | null; password?: string | null; - // TODO: Remove this — submit nodes replaced by SUBMIT_NODE groups - // submit_nodes?: UserSubmitNodeCreate[] | null; } export interface Note {