diff --git a/src/api/functions/siglead.ts b/src/api/functions/siglead.ts new file mode 100644 index 0000000..b6cbaa3 --- /dev/null +++ b/src/api/functions/siglead.ts @@ -0,0 +1,114 @@ +import { + DynamoDBClient, + QueryCommand, + ScanCommand, +} from "@aws-sdk/client-dynamodb"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { OrganizationList } from "common/orgs.js"; +import { + SigDetailRecord, + SigMemberCount, + SigMemberRecord, +} from "common/types/siglead.js"; +import { transformSigLeadToURI } from "common/utils.js"; +import { string } from "zod"; + +export async function fetchMemberRecords( + sigid: string, + tableName: string, + dynamoClient: DynamoDBClient, +) { + const fetchSigMemberRecords = new QueryCommand({ + TableName: tableName, + KeyConditionExpression: "#sigid = :accessVal", + ExpressionAttributeNames: { + "#sigid": "sigGroupId", + }, + ExpressionAttributeValues: { + ":accessVal": { S: sigid }, + }, + ScanIndexForward: false, + }); + + const result = await dynamoClient.send(fetchSigMemberRecords); + + // Process the results + return (result.Items || []).map((item) => { + const unmarshalledItem = unmarshall(item); + return unmarshalledItem as SigMemberRecord; + }); +} + +export async function fetchSigDetail( + sigid: string, + tableName: string, + dynamoClient: DynamoDBClient, +) { + const fetchSigDetail = new QueryCommand({ + TableName: tableName, + KeyConditionExpression: "#sigid = :accessVal", + ExpressionAttributeNames: { + "#sigid": "sigid", + }, + ExpressionAttributeValues: { + ":accessVal": { S: sigid }, + }, + ScanIndexForward: false, + }); + + const result = await dynamoClient.send(fetchSigDetail); + + // Process the results + return (result.Items || [{}]).map((item) => { + const unmarshalledItem = unmarshall(item); + + // Strip '#' from access field + delete unmarshalledItem.leadGroupId; + delete unmarshalledItem.memberGroupId; + + return unmarshalledItem as SigDetailRecord; + })[0]; +} + +// select count(sigid) +// from table +// groupby sigid +export async function fetchSigCounts( + sigMemberTableName: string, + dynamoClient: DynamoDBClient, +) { + const scan = new ScanCommand({ + TableName: sigMemberTableName, + ProjectionExpression: "sigGroupId", + }); + + const result = await dynamoClient.send(scan); + + const ids2Name: Record = {}; + OrganizationList.forEach((org) => { + const sigid = transformSigLeadToURI(org); + ids2Name[sigid] = org; + }); + + const counts: Record = {}; + (result.Items || []).forEach((item) => { + const sigGroupId = item.sigGroupId?.S; + if (sigGroupId) { + counts[sigGroupId] = (counts[sigGroupId] || 0) + 1; + } + }); + + const joined: Record = {}; + Object.keys(counts).forEach((sigid) => { + joined[sigid] = [ids2Name[sigid], counts[sigid]]; + }); + + const countsArray: SigMemberCount[] = Object.entries(joined).map( + ([sigid, [signame, count]]) => ({ + sigid, + signame, + count, + }), + ); + return countsArray; +} diff --git a/src/api/index.ts b/src/api/index.ts index 5cd222b..845f845 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -25,6 +25,7 @@ import * as dotenv from "dotenv"; import iamRoutes from "./routes/iam.js"; import ticketsPlugin from "./routes/tickets.js"; import linkryRoutes from "./routes/linkry.js"; +import sigleadRoutes from "./routes/siglead.js"; import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts"; import NodeCache from "node-cache"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; @@ -287,6 +288,7 @@ async function init(prettyPrint: boolean = false) { api.register(iamRoutes, { prefix: "/iam" }); api.register(ticketsPlugin, { prefix: "/tickets" }); api.register(linkryRoutes, { prefix: "/linkry" }); + api.register(sigleadRoutes, { prefix: "/siglead" }); api.register(mobileWalletRoute, { prefix: "/mobileWallet" }); api.register(stripeRoutes, { prefix: "/stripe" }); api.register(roomRequestRoutes, { prefix: "/roomRequests" }); diff --git a/src/api/routes/siglead.ts b/src/api/routes/siglead.ts new file mode 100644 index 0000000..fca9577 --- /dev/null +++ b/src/api/routes/siglead.ts @@ -0,0 +1,134 @@ +import { FastifyPluginAsync } from "fastify"; +import { DatabaseFetchError } from "../../common/errors/index.js"; +import { genericConfig } from "../../common/config.js"; +import { + SigDetailRecord, + SigleadGetRequest, + SigMemberCount, + SigMemberRecord, +} from "common/types/siglead.js"; +import { + fetchMemberRecords, + fetchSigCounts, + fetchSigDetail, +} from "api/functions/siglead.js"; + +const sigleadRoutes: FastifyPluginAsync = async (fastify, _options) => { + const limitedRoutes: FastifyPluginAsync = async (fastify) => { + /*fastify.register(rateLimiter, { + limit: 30, + duration: 60, + rateLimitIdentifier: "linkry", + });*/ + + fastify.get( + "/sigmembers/:sigid", + { + onRequest: async (request, reply) => { + /*await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]);*/ + }, + }, + async (request, reply) => { + const { sigid } = request.params; + const tableName = genericConfig.SigleadDynamoSigMemberTableName; + + // First try-catch: Fetch owner records + let memberRecords: SigMemberRecord[]; + try { + memberRecords = await fetchMemberRecords( + sigid, + tableName, + fastify.dynamoClient, + ); + } catch (error) { + request.log.error( + `Failed to fetch member records: ${error instanceof Error ? error.toString() : "Unknown error"}`, + ); + throw new DatabaseFetchError({ + message: "Failed to fetch member records from Dynamo table.", + }); + } + + // Send the response + reply.code(200).send(memberRecords); + }, + ); + + fastify.get( + "/sigdetail/:sigid", + { + onRequest: async (request, reply) => { + /*await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]);*/ + }, + }, + async (request, reply) => { + const { sigid } = request.params; + const tableName = genericConfig.SigleadDynamoSigDetailTableName; + + // First try-catch: Fetch owner records + let sigDetail: SigDetailRecord; + try { + sigDetail = await fetchSigDetail( + sigid, + tableName, + fastify.dynamoClient, + ); + } catch (error) { + request.log.error( + `Failed to fetch sig detail record: ${error instanceof Error ? error.toString() : "Unknown error"}`, + ); + throw new DatabaseFetchError({ + message: "Failed to fetch sig detail record from Dynamo table.", + }); + } + + // Send the response + reply.code(200).send(sigDetail); + }, + ); + + // fetch sig count + fastify.get( + "/sigcount", + { + onRequest: async (request, reply) => { + /*await fastify.authorize(request, reply, [ + AppRoles.LINKS_MANAGER, + AppRoles.LINKS_ADMIN, + ]);*/ + }, + }, + async (request, reply) => { + // First try-catch: Fetch owner records + let sigMemCounts: SigMemberCount[]; + try { + sigMemCounts = await fetchSigCounts( + genericConfig.SigleadDynamoSigMemberTableName, + fastify.dynamoClient, + ); + } catch (error) { + request.log.error( + `Failed to fetch sig member counts record: ${error instanceof Error ? error.toString() : "Unknown error"}`, + ); + throw new DatabaseFetchError({ + message: + "Failed to fetch sig member counts record from Dynamo table.", + }); + } + + // Send the response + reply.code(200).send(sigMemCounts); + }, + ); + }; + + fastify.register(limitedRoutes); +}; + +export default sigleadRoutes; diff --git a/src/common/config.ts b/src/common/config.ts index 14937cf..43608b9 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -29,6 +29,8 @@ export type GenericConfigType = { EventsDynamoTableName: string; CacheDynamoTableName: string; LinkryDynamoTableName: string; + SigleadDynamoSigDetailTableName: string; + SigleadDynamoSigMemberTableName: string; StripeLinksDynamoTableName: string; ConfigSecretName: string; EntraSecretName: string; @@ -68,6 +70,8 @@ const genericConfig: GenericConfigType = { StripeLinksDynamoTableName: "infra-core-api-stripe-links", CacheDynamoTableName: "infra-core-api-cache", LinkryDynamoTableName: "infra-core-api-linkry", + SigleadDynamoSigDetailTableName: "infra-core-api-sig-details", + SigleadDynamoSigMemberTableName: "infra-core-api-sig-member-details", ConfigSecretName: "infra-core-api-config", EntraSecretName: "infra-core-api-entra", EntraReadOnlySecretName: "infra-core-api-ro-entra", diff --git a/src/common/orgs.ts b/src/common/orgs.ts index 61d570d..ee84d00 100644 --- a/src/common/orgs.ts +++ b/src/common/orgs.ts @@ -4,7 +4,7 @@ export const SIGList = [ "GameBuilders", "SIGAIDA", "SIGGRAPH", - "ICPC", + "SIGICPC", "SIGMobile", "SIGMusic", "GLUG", diff --git a/src/common/types/siglead.ts b/src/common/types/siglead.ts new file mode 100644 index 0000000..2822cd4 --- /dev/null +++ b/src/common/types/siglead.ts @@ -0,0 +1,24 @@ +export type SigDetailRecord = { + sigid: string; + signame: string; + description: string; + }; + + export type SigMemberRecord = { + sigGroupId: string; + email: string; + designation: string; + memberName: string; + }; + + export type SigleadGetRequest = { + Params: { sigid: string }; + Querystring: undefined; + Body: undefined; + }; + + export type SigMemberCount = { + sigid: string; + signame: string; + count: number; + }; \ No newline at end of file diff --git a/src/common/utils.ts b/src/common/utils.ts index 786c998..8959ecc 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -12,3 +12,47 @@ export function transformCommaSeperatedName(name: string) { } return name; } + +const notUnreservedCharsRegex = /[^a-zA-Z0-9\-._~]/g; +const reservedCharsRegex = /[:\/?#\[\]@!$&'()*+,;=]/g; +/** + * Transforms an organization name (sig lead) into a URI-friendly format. + * The function performs the following transformations: + * - Removes characters that are reserved or not unreserved. + * - Adds spaces between camel case words. + * - Converts reserved characters to spaces. + * - Converts all characters to lowercase and replaces all types of whitespace with hyphens. + * - Replaces any sequence of repeated hyphens with a single hyphen. + * - Refer to RFC 3986 https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 + * + * @param {string} org - The organization (sig lead) name to be transformed. + * @returns {string} - The transformed organization name, ready for use as a URL. + */ +export function transformSigLeadToURI(org: string) { + org = org + // change not reserved chars to spaces + .trim() + .replace(notUnreservedCharsRegex, " ") + .trim() + .replace(/\s/g, "-") + + // remove all that is reserved or not unreserved + .replace(reservedCharsRegex, "") + + // convert SIG -> sig for camel case + .replace(/SIG/g, "sig") + + // add hyphen for camel case + .replace(/([a-z])([A-Z])/g, "$1-$2") + + // lower + .toLowerCase() + + // add spaces between chars and numbers (seq2seq -> seq-2-seq) + .replace(/(?<=[a-z])([0-9]+)(?=[a-z])/g, "-$1-") + + // remove duplicate hyphens + .replace(/-{2,}/g, "-"); + + return org === "-" ? "" : org; +} diff --git a/src/ui/Router.tsx b/src/ui/Router.tsx index d786496..92d503c 100644 --- a/src/ui/Router.tsx +++ b/src/ui/Router.tsx @@ -5,6 +5,7 @@ import { RouterProvider, useLocation, } from "react-router-dom"; + import { AcmAppShell } from "./components/AppShell"; import { useAuth } from "./components/AuthContext"; import AuthCallback from "./components/AuthContext/AuthCallbackHandler.page"; @@ -24,6 +25,7 @@ import { ManageIamPage } from "./pages/iam/ManageIam.page"; import { ManageProfilePage } from "./pages/profile/ManageProfile.page"; import { ManageStripeLinksPage } from "./pages/stripe/ViewLinks.page"; import { ManageRoomRequestsPage } from "./pages/roomRequest/RoomRequestLanding.page"; +import { EditSigLeadsPage } from "./pages/siglead/EditSigLeads.page"; import { ManageSigLeadsPage } from "./pages/siglead/ManageSigLeads.page"; import { ViewSigLeadPage } from "./pages/siglead/ViewSigLead.page"; import { ViewRoomRequest } from "./pages/roomRequest/ViewRoomRequest.page"; @@ -193,6 +195,10 @@ const authenticatedRouter = createBrowserRouter([ path: "/siglead-management", element: , }, + { + path: "/siglead-management/edit", + element: , + }, { path: "/siglead-management/:sigId", element: , diff --git a/src/ui/pages/siglead/EditSigLeads.page.tsx b/src/ui/pages/siglead/EditSigLeads.page.tsx new file mode 100644 index 0000000..c5c03d3 --- /dev/null +++ b/src/ui/pages/siglead/EditSigLeads.page.tsx @@ -0,0 +1,116 @@ +import { Title, TextInput, Button, Container, Group } from "@mantine/core"; +import { useForm, zodResolver } from "@mantine/form"; +import { notifications } from "@mantine/notifications"; +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { z } from "zod"; +import { AuthGuard } from "@ui/components/AuthGuard"; +import { useApi } from "@ui/util/api"; +import { AppRoles } from "@common/roles"; +import { transformSigLeadToURI } from "@common/utils"; + +const baseSigSchema = z.object({ + signame: z + .string() + .min(1, "Title is required") + .regex( + /^[a-zA-Z0-9]+$/, + "Sig name should only contain alphanumeric characters", + ), + description: z.string().min(1, "Description is required"), +}); + +type SigPostRequest = z.infer; + +export const EditSigLeadsPage: React.FC = () => { + const [isSubmitting, setIsSubmitting] = useState(false); + const navigate = useNavigate(); + const api = useApi("core"); + + const form = useForm({ + validate: zodResolver(baseSigSchema), + initialValues: { + signame: "", + description: "", + }, + }); + + const checkSigId = async (signame: string) => { + try { + const sigid = transformSigLeadToURI(signame); + const result = await api.get(`/api/v1/siglead/sigdetail/${sigid}`); + return result.data; + } catch (error) { + console.error("Error validating if sigid already exists", error); + notifications.show({ + message: `Error validating if sigid already exists`, + }); + } + }; + + const handleSubmit = async (sigdetails: SigPostRequest) => { + try { + setIsSubmitting(true); + const found = await checkSigId(sigdetails.signame); + if (found) { + form.setErrors({ + signame: "This signame is reserved already.", + }); + setIsSubmitting(false); + return; + } + notifications.show({ + message: `This will eventually make to a post request with signame: + ${sigdetails.signame} and description: ${sigdetails.description} + `, + }); + //Post... + navigate("/siglead-management"); + } catch (error) { + setIsSubmitting(false); + console.error("Error creating sig:", error); + notifications.show({ + message: "Failed to create sig, please try again.", + }); + } + }; + + return ( + + + Registering a new Sig +
+ + + + + + + +
+
+ ); +}; diff --git a/src/ui/pages/siglead/ManageSigLeads.page.tsx b/src/ui/pages/siglead/ManageSigLeads.page.tsx index 3555e72..ced9bcc 100644 --- a/src/ui/pages/siglead/ManageSigLeads.page.tsx +++ b/src/ui/pages/siglead/ManageSigLeads.page.tsx @@ -1,14 +1,5 @@ -import { - Title, - Box, - TextInput, - Textarea, - Switch, - Select, - Button, - Loader, - Container, -} from "@mantine/core"; +import { Title, Button, Container, Group } from "@mantine/core"; +import { useDisclosure } from "@mantine/hooks"; import { DateTimePicker } from "@mantine/dates"; import { useForm, zodResolver } from "@mantine/form"; import { notifications } from "@mantine/notifications"; @@ -22,168 +13,53 @@ import { useApi } from "@ui/util/api"; import { OrganizationList as orgList } from "@common/orgs"; import { AppRoles } from "@common/roles"; import { ScreenComponent } from "./SigScreenComponents"; - -export function capitalizeFirstLetter(string: string) { - return string.charAt(0).toUpperCase() + string.slice(1); -} - -const repeatOptions = ["weekly", "biweekly"] as const; - -const baseBodySchema = z.object({ - title: z.string().min(1, "Title is required"), - description: z.string().min(1, "Description is required"), - start: z.date(), - end: z.optional(z.date()), - location: z.string().min(1, "Location is required"), - locationLink: z.optional(z.string().url("Invalid URL")), - host: z.string().min(1, "Host is required"), - featured: z.boolean().default(false), - paidEventId: z - .string() - .min(1, "Paid Event ID must be at least 1 character") - .optional(), -}); - -const requestBodySchema = baseBodySchema - .extend({ - repeats: z.optional(z.enum(repeatOptions)).nullable(), - repeatEnds: z.date().optional(), - }) - .refine((data) => (data.repeatEnds ? data.repeats !== undefined : true), { - message: "Repeat frequency is required when Repeat End is specified.", - }) - .refine((data) => !data.end || data.end >= data.start, { - message: "Event end date cannot be earlier than the start date.", - path: ["end"], - }) - .refine((data) => !data.repeatEnds || data.repeatEnds >= data.start, { - message: "Repeat end date cannot be earlier than the start date.", - path: ["repeatEnds"], - }); - -type EventPostRequest = z.infer; +import { transformSigLeadToURI } from "@common/utils"; +import { + SigDetailRecord, + SigleadGetRequest, + SigMemberCount, + SigMemberRecord, +} from "@common/types/siglead"; export const ManageSigLeadsPage: React.FC = () => { - const [isSubmitting, setIsSubmitting] = useState(false); + const [SigMemberCounts, setSigMemberCounts] = useState([]); const navigate = useNavigate(); const api = useApi("core"); - const { eventId } = useParams(); - - const isEditing = eventId !== undefined; - useEffect(() => { - if (!isEditing) { - return; - } - // Fetch event data and populate form - const getEvent = async () => { + const getMemberCounts = async () => { try { - const response = await api.get(`/api/v1/events/${eventId}`); - const eventData = response.data; - const formValues = { - title: eventData.title, - description: eventData.description, - start: new Date(eventData.start), - end: eventData.end ? new Date(eventData.end) : undefined, - location: eventData.location, - locationLink: eventData.locationLink, - host: eventData.host, - featured: eventData.featured, - repeats: eventData.repeats, - repeatEnds: eventData.repeatEnds - ? new Date(eventData.repeatEnds) - : undefined, - paidEventId: eventData.paidEventId, - }; - form.setValues(formValues); + const sigMemberCountsRequest = await api.get( + `/api/v1/siglead/sigcount`, + ); + setSigMemberCounts(sigMemberCountsRequest.data); } catch (error) { - console.error("Error fetching event data:", error); + console.error("Error fetching sig member counts:", error); notifications.show({ - message: "Failed to fetch event data, please try again.", + message: "Failed to fetch sig member counts, please try again.", }); } }; - getEvent(); - }, [eventId, isEditing]); - - const form = useForm({ - validate: zodResolver(requestBodySchema), - initialValues: { - title: "", - description: "", - start: new Date(), - end: new Date(new Date().valueOf() + 3.6e6), // 1 hr later - location: "ACM Room (Siebel CS 1104)", - locationLink: "https://maps.app.goo.gl/dwbBBBkfjkgj8gvA8", - host: "ACM", - featured: false, - repeats: undefined, - repeatEnds: undefined, - paidEventId: undefined, - }, - }); - - const checkPaidEventId = async (paidEventId: string) => { - try { - const merchEndpoint = - getRunEnvironmentConfig().ServiceConfiguration.merch.baseEndpoint; - const ticketEndpoint = - getRunEnvironmentConfig().ServiceConfiguration.tickets.baseEndpoint; - const paidEventHref = paidEventId.startsWith("merch:") - ? `${merchEndpoint}/api/v1/merch/details?itemid=${paidEventId.slice(6)}` - : `${ticketEndpoint}/api/v1/event/details?eventid=${paidEventId}`; - const response = await api.get(paidEventHref); - return Boolean(response.status < 299 && response.status >= 200); - } catch (error) { - console.error("Error validating paid event ID:", error); - return false; - } - }; - - const handleSubmit = async (values: EventPostRequest) => { - try { - setIsSubmitting(true); - const realValues = { - ...values, - start: dayjs(values.start).format("YYYY-MM-DD[T]HH:mm:00"), - end: values.end - ? dayjs(values.end).format("YYYY-MM-DD[T]HH:mm:00") - : undefined, - repeatEnds: - values.repeatEnds && values.repeats - ? dayjs(values.repeatEnds).format("YYYY-MM-DD[T]HH:mm:00") - : undefined, - repeats: values.repeats ? values.repeats : undefined, - }; - - const eventURL = isEditing - ? `/api/v1/events/${eventId}` - : "/api/v1/events"; - const response = await api.post(eventURL, realValues); - notifications.show({ - title: isEditing ? "Event updated!" : "Event created!", - message: isEditing - ? undefined - : `The event ID is "${response.data.id}".`, - }); - navigate("/events/manage"); - } catch (error) { - setIsSubmitting(false); - console.error("Error creating/editing event:", error); - notifications.show({ - message: "Failed to create/edit event, please try again.", - }); - } - }; + getMemberCounts(); + }, []); return ( - SigLead Management System - + + SigLead Management System + + + + {/* */} diff --git a/src/ui/pages/siglead/SigScreenComponents.tsx b/src/ui/pages/siglead/SigScreenComponents.tsx index 53d1c7d..c5d5e36 100644 --- a/src/ui/pages/siglead/SigScreenComponents.tsx +++ b/src/ui/pages/siglead/SigScreenComponents.tsx @@ -3,16 +3,20 @@ import { OrganizationList } from "@common/orgs"; import { NavLink, Paper } from "@mantine/core"; import { IconUsersGroup } from "@tabler/icons-react"; import { useLocation } from "react-router-dom"; +import { SigMemberCount } from "@common/types/siglead"; -const renderSigLink = (org: string, index: number) => { +const renderSigLink = (sigMemCount: SigMemberCount, index: number) => { const color = "light-dark(var(--mantine-color-black), var(--mantine-color-white))"; const size = "18px"; + const name = sigMemCount.signame; + const id = sigMemCount.sigid; + const count = sigMemCount.count; return ( { fontSize: `${size}`, }} > - MemberCount[{index}] + MemberCount: {count} } @@ -39,7 +43,11 @@ const renderSigLink = (org: string, index: number) => { ); }; -export const ScreenComponent: React.FC = () => { +type props = { + SigMemberCounts: SigMemberCount[]; +}; + +export const ScreenComponent: React.FC = ({ SigMemberCounts }) => { return ( <> { Organization Member Count - {OrganizationList.map(renderSigLink)} + {/* {OrganizationList.map(renderSigLink)} */} + {SigMemberCounts.map(renderSigLink)} ); }; diff --git a/src/ui/pages/siglead/ViewSigLead.page.tsx b/src/ui/pages/siglead/ViewSigLead.page.tsx index bc3b2c2..d752e11 100644 --- a/src/ui/pages/siglead/ViewSigLead.page.tsx +++ b/src/ui/pages/siglead/ViewSigLead.page.tsx @@ -1,12 +1,7 @@ import { Title, Box, - TextInput, - Textarea, - Switch, - Select, Button, - Loader, Container, Transition, useMantineColorScheme, @@ -14,17 +9,15 @@ import { Group, Stack, } from "@mantine/core"; -import { DateTimePicker } from "@mantine/dates"; -import { useForm, zodResolver } from "@mantine/form"; + import { notifications } from "@mantine/notifications"; -import dayjs from "dayjs"; import React, { useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { z } from "zod"; import { AuthGuard } from "@ui/components/AuthGuard"; -import { getRunEnvironmentConfig } from "@ui/config"; import { useApi } from "@ui/util/api"; import { AppRoles } from "@common/roles"; +import { IconUsersGroup } from "@tabler/icons-react"; const baseSigSchema = z.object({ sigid: z.string().min(1), @@ -44,7 +37,6 @@ type sigDetails = z.infer; type sigMemberDetails = z.infer; export const ViewSigLeadPage: React.FC = () => { - const [isSubmitting, setIsSubmitting] = useState(false); const navigate = useNavigate(); const api = useApi("core"); const { colorScheme } = useMantineColorScheme(); @@ -74,9 +66,14 @@ export const ViewSigLeadPage: React.FC = () => { // Fetch sig data and populate form / for now dummy data... const getSig = async () => { try { - /*const formValues = { - }; - form.setValues(formValues);*/ + const sigDetailsData = await api.get( + `/api/v1/siglead/sigdetail/${sigId}`, + ); + setSigDetails(sigDetailsData.data); + const sigMembersData = await api.get( + `/api/v1/siglead/sigmembers/${sigId}`, + ); + setSigMembers(sigMembersData.data); } catch (error) { console.error("Error fetching sig data:", error); notifications.show({ @@ -180,8 +177,9 @@ export const ViewSigLeadPage: React.FC = () => { - - +