add - base ux ui for web admin page, form works on group and teams selection only

This commit is contained in:
2026-09-02 12:44:38 -07:00
parent 43cc39fd62
commit 6c0664d05a
3 changed files with 254 additions and 13 deletions
+2
View File
@@ -19,6 +19,7 @@ import GlobalMap from "./pages/maps/GlobalMap.jsx";
import Admin from "./pages/admin/Admin.jsx";
import MyDashboard from "./pages/home/tabs/MyDashboard.jsx";
import Settings from "./pages/auth/Settings.jsx";
import WebAdmin from "./pages/auth/WebAdmin.jsx";
function AppFoundation() {
const recipe = useRecipe({ key: "dashboard" });
@@ -83,6 +84,7 @@ function App() {
</Route>
<Route path="/settings" element={<Settings />} />
<Route path="/web-admin" element={<WebAdmin />} />
<Route path="/global-map" element={<GlobalMap />} />
<Route path="/regional-map" element={<RegionalMap />} />
+18 -13
View File
@@ -23,25 +23,30 @@ export default function Settings() {
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Flex direction="column" w="100%" h="100%" p="15rem" pt="4rem" gap="5rem">
<Flex direction="column" w="100%" h="100%" p="15rem" pt="3rem" gap="5rem">
{/* CONTENT */}
<Flex direction="column" w="100%" h="100%" gap={4}>
<Text fontWeight="semibold" fontSize="xl">Personal Information</Text>
<Flex direction="column" w="100%" align="center" gap={4}>
{/* avatar */}
{/* header */}
<Text fontWeight="semibold" fontSize="xl">Settings</Text>
<Separator borderColor="gray.300"/>
{/* personal info */}
<Flex direction="column" w="100%" gap={4}>
<Text fontWeight="semibold" fontSize="lg">Personal Information</Text>
{/* profile picture & name */}
<Flex direction="row" w="100%" align="center" justify="space-between" gap={5}>
<Flex direction="row" w="100%" align="center" gap={5}>
<Image borderRadius="full" boxSize="100px" src="https://i.pravatar.cc/300?img=10" />
<Flex direction="column">
<Text fontWeight="semibold">Name</Text>
<Text pl={2}>{user.firstName || "first name"}, {user.lastName || "last name"}</Text>
</Flex>
<Flex direction="column">
<Text fontWeight="semibold">Name</Text>
<Text pl={2}>{user.firstName || "first name"}, {user.lastName || "last name"}</Text>
</Flex>
</Flex>
<Button variant="subtle" size="sm">Update photo</Button>
</Flex>
{/* personal info */}
<Flex direction="row" w="100%" gap={5}>
<Field.Root>
<Field.Label fontWeight="semibold">Username</Field.Label>
@@ -79,9 +84,9 @@ export default function Settings() {
<Separator borderColor="gray.300"/>
{/* permissions */}
{/* permission requests */}
<Flex direction="column" gap={2}>
<Text fontWeight="semibold" fontSize="xl">Permissions</Text>
<Text fontWeight="semibold" fontSize="lg">Permissions</Text>
<Flex direction="row" w="100%" align="center" justify="space-between" pl={2}>
<Text>Request access to IVANS</Text>
<Button variant="subtle" size="sm" onClick={ivansRequest}>IVANS Request</Button>
@@ -96,7 +101,7 @@ export default function Settings() {
{/* themes */}
<Flex direction="column">
<Text fontWeight="semibold" fontSize="xl">Theme</Text>
<Text fontWeight="semibold" fontSize="lg">Theme</Text>
<Flex direction="row" align="center" justify="space-between" pl={2}>
<Text fontSize="md">Appearance</Text>
<NativeSelect.Root size="sm" w="10rem" variant="subtle">
@@ -115,7 +120,7 @@ export default function Settings() {
</Flex>
</Flex>
{/* SAVE-ALL BUTTON */}
{/* SAVE-ALL PREFERENCES BUTTON */}
{/* TODO: grey out button until changes are detected */}
<Button type="submit" w="150px" variant="subtle" borderRadius="lg" alignSelf="center">
Save Changes
+234
View File
@@ -0,0 +1,234 @@
import {Flex, Separator, Text, Table, IconButton, NativeSelect, Button} from "@chakra-ui/react";
import { useMemo } from "react";
import { useForm, useFieldArray } from "react-hook-form";
import { Check, X, PenLine } from 'lucide-react';
import VdapApi from "../../hooks/useVdapApi.js";
import useBuildUrl from "../../hooks/useBuildUrl.js";
export default function WebAdmin() {
// TODO: add below api endpoint
// const {data: users, isLoading, error} = VdapApi('/api/v2/vdap_web_users');
const users = useMemo(() => [
{
"username": "vkuchenik",
"firstName": "Venessa",
"lastName": "Kuchenik",
"permissions": {
"ivans": 2, // 2 => requested permission
"rs": 0, // 0 => no permissions and not requested
},
"team": "Computer Science",
"group": "Super User",
},
{
"username": "amack",
"firstName": "Adeiline",
"lastName": "Mack",
"permissions": {
"ivans": 2, // 0 => no permissions and not requested
"rs": 2, // 1 => has permission
},
"team": "Remote Sensing",
"group": "Member",
}
], []);
const {
register,
handleSubmit,
control,
errors,
formState: { isDirty }
} = useForm({
defaultValues: {users: users}
});
const { fields, append, remove } = useFieldArray({
control,
name: "users" // Matches the key in defaultValues
})
const grant = (user, category) => {
// 1. post data via api
// 2. fetch the new data via api again to force re-render
console.log(user.username, "was granted", category, "permission");
}
const deny = (user, category) => {
console.log(user.username, "was denied", category, "permission");
}
const users_req_ivans = useMemo(() => {
return users.filter(user => user.permissions.ivans === 2);
}, [users]);
const users_req_rs = useMemo(() => {
return users.filter(user => user.permissions.rs === 2);
}, [users]);
const onSubmit = (data, event) => {
event.preventDefault();
console.log(data);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Flex direction="column" w="100%" h="100%" p="15rem" pt="3rem" gap="3rem">
{/* CONTENT */}
<Flex direction="column" w="100%" gap={4}>
{/* Header */}
<Text fontSize="xl" fontWeight="bold">Web Admin Panel</Text>
<Separator borderColor="gray.300"/>
{/* Permission Requests */}
<Flex direction="column" w="100%" gap={2} pb={4}>
<Text fontSize="lg" fontWeight="bold">Incoming Permission Requests</Text>
<Flex direction="row" w="100%" gap={5}>
{/* ivans requests table */}
<Flex direction="column" w="100%" gap={2}>
<Text fontSize="sm" fontWeight="bold">IVANS</Text>
<Table.Root w="100%" size="sm" variant="outline" borderRadius="lg">
<Table.Header>
<Table.Row>
<Table.ColumnHeader w="100%" fontWeight="bold">User</Table.ColumnHeader>
<Table.ColumnHeader fontWeight="bold" textAlign="left">Permission</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{users_req_ivans.map((user) => (
<Table.Row key={user.username}>
<Table.Cell w="100%">{user.lastName}, {user.firstName}</Table.Cell>
<Table.Cell >
<Flex w="100%" gap={1} justify="left">
<IconButton borderRadius="xl" size="xs" variant="subtle" onClick={() => grant(user, "ivans")}>
<Check />
</IconButton>
<IconButton borderRadius="xl" size="xs" variant="subtle" onClick={() => deny(user, "ivans")}>
<X />
</IconButton>
</Flex>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Flex>
{/* rs requests table */}
<Flex direction="column" w="100%" gap={2}>
<Text fontSize="sm" fontWeight="bold">Remote Sensing</Text>
<Table.Root w="100%" size="sm" variant="outline" borderRadius="lg">
<Table.Header>
<Table.Row>
<Table.ColumnHeader w="100%" fontWeight="bold">User</Table.ColumnHeader>
<Table.ColumnHeader fontWeight="bold" textAlign="left">Permission</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{users_req_rs.map((user) => (
<Table.Row key={user.username}>
<Table.Cell>{user.lastName}, {user.firstName}</Table.Cell>
<Table.Cell>
<Flex w="100%" gap={1} justify="left">
<IconButton borderRadius="xl" size="xs" variant="subtle" onClick={() => grant(user, "rs")}>
<Check />
</IconButton>
<IconButton borderRadius="xl" size="xs" variant="subtle" onClick={() => deny(user, "rs")}>
<X />
</IconButton>
</Flex>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Flex>
</Flex>
</Flex>
<Separator borderColor="gray.300"/>
{/* Group Assignment */}
<Flex direction="column" w="100%" gap={2} pb={4}>
<Text fontWeight="bold" fontSize="lg">Groups</Text>
<Table.Root w="50%" size="sm" variant="outline" borderRadius="lg">
<Table.Header>
<Table.Row>
<Table.ColumnHeader fontWeight="bold">User</Table.ColumnHeader>
<Table.ColumnHeader fontWeight="bold" textAlign="left">Group</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{users.map((user, index) => (
<Table.Row key={user.username}>
<Table.Cell w="100%">{user.lastName}, {user.firstName}</Table.Cell>
<Table.Cell>
<Flex w="100%" justify="left">
<NativeSelect.Root size="sm" w="175px" variant="subtle">
<NativeSelect.Field
defaultValue={user.group}
{...register(`users.${index}.group`)}
>
<option value="Super User">Super User</option>
<option value="Member">Member</option>
</NativeSelect.Field>
<NativeSelect.Indicator />
</NativeSelect.Root>
</Flex>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Flex>
<Separator borderColor="gray.300"/>
{/* Team Assignment */}
<Flex direction="column" w="100%" gap={2} pb={4}>
<Text fontWeight="bold" fontSize="lg">Teams</Text>
<Table.Root w="50%" size="sm" variant="outline" borderRadius="lg">
<Table.Header>
<Table.Row>
<Table.ColumnHeader fontWeight="bold" w="100%">User</Table.ColumnHeader>
<Table.ColumnHeader fontWeight="bold" textAlign="left">Team</Table.ColumnHeader>
</Table.Row>
</Table.Header>
<Table.Body>
{users.map((user, index) => {
console.log("user in team: ", user.team);
return(
<Table.Row key={user.username}>
<Table.Cell>
{user.lastName}, {user.firstName}
</Table.Cell>
<Table.Cell>
<Flex w="100%" justify="right">
<NativeSelect.Root size="sm" w="175px" variant="subtle">
<NativeSelect.Field
defaultValue={user.team}
{...register(`users.${index}.team`)}
>
<option value="Computer Science">Computer Science</option>
<option value="Remote Sensing">Remote Sensing</option>
</NativeSelect.Field>
<NativeSelect.Indicator />
</NativeSelect.Root>
</Flex>
</Table.Cell>
</Table.Row>
)})}
</Table.Body>
</Table.Root>
</Flex>
</Flex>
<Button type="submit" w="100px" variant="subtle" alignSelf="center">
Save
</Button>
</Flex>
</form>
)
}