diff --git a/veda-auth-portal/package-lock.json b/veda-auth-portal/package-lock.json index f1b291d..53a2459 100644 --- a/veda-auth-portal/package-lock.json +++ b/veda-auth-portal/package-lock.json @@ -1708,6 +1708,15 @@ "@types/lodash": "*" } }, + "@types/node": { + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", + "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", + "dev": true, + "requires": { + "undici-types": "~6.19.2" + } + }, "@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -3142,6 +3151,11 @@ "tslib": "^2.0.0" } }, + "react-tag-input-component": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-tag-input-component/-/react-tag-input-component-2.0.2.tgz", + "integrity": "sha512-dydI9luVwwv9vrjE5u1TTnkcOVkOVL6mhFti8r6hLi78V2F2EKWQOLptURz79UYbDHLSk6tnbvGl8FE+sMpADg==" + }, "regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -3349,6 +3363,12 @@ "@typescript-eslint/utils": "8.1.0" } }, + "undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, "update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", diff --git a/veda-auth-portal/package.json b/veda-auth-portal/package.json index 7bc7893..1eb76f2 100644 --- a/veda-auth-portal/package.json +++ b/veda-auth-portal/package.json @@ -21,10 +21,12 @@ "react-dom": "^18.3.1", "react-icons": "^5.3.0", "react-oidc-context": "^3.1.0", - "react-router-dom": "^6.26.1" + "react-router-dom": "^6.26.1", + "react-tag-input-component": "^2.0.2" }, "devDependencies": { "@eslint/js": "^9.9.0", + "@types/node": "^22.5.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/veda-auth-portal/src/App.css b/veda-auth-portal/src/App.css deleted file mode 100644 index e69de29..0000000 diff --git a/veda-auth-portal/src/App.tsx b/veda-auth-portal/src/App.tsx index 127299e..e27d506 100644 --- a/veda-auth-portal/src/App.tsx +++ b/veda-auth-portal/src/App.tsx @@ -1,7 +1,6 @@ import { Routes, Route, BrowserRouter } from 'react-router-dom'; import { Heading } from '@chakra-ui/react'; import { Groups } from './components/Groups'; -import './App.css'; import { NavContainer } from './components/NavContainer'; import { GroupDetails } from './components/Groups/GroupDetails'; import { Login } from './components/Login'; @@ -25,7 +24,7 @@ export default function App() { } /> } /> } /> - } /> + } /> } /> diff --git a/veda-auth-portal/src/components/Groups/AddGroupMemberModal.tsx b/veda-auth-portal/src/components/Groups/AddGroupMemberModal.tsx new file mode 100644 index 0000000..66dc3c3 --- /dev/null +++ b/veda-auth-portal/src/components/Groups/AddGroupMemberModal.tsx @@ -0,0 +1,122 @@ +import { + useToast, + Button, Input, Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, FormControl, + FormLabel, + Stack, + Select +} from "@chakra-ui/react"; +import { BACKEND_URL } from "../../lib/constants"; +import axios, { AxiosResponse } from "axios"; +import React from "react"; +import { useAuth } from "react-oidc-context"; +import { useNavigate } from "react-router-dom"; + + export const AddGroupMemberModal = ({ + groupId, + isOpen, + onClose, + }: { + groupId: string | undefined; + isOpen: boolean; + onClose: () => void; + }) => { + const toast = useToast(); + const [newEmail, setNewEmail] = React.useState(""); + const [newRole, setNewRole] = React.useState("MEMBER"); + const auth = useAuth(); + const navigate = useNavigate(); + + const handleAddMember = async () => { + const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/; + + if (!emailRegex.test(newEmail)) { + toast({ + title: "Invalid email", + status: "error", + duration: 2000, + isClosable: true, + }); + return; + } + + const resp = await axios + .post( + `${BACKEND_URL}/api/v1/group-management/groups/${groupId}/members`, + { + username: newEmail, + type: newRole, + }, + { + headers: { + Authorization: `Bearer ${auth.user?.access_token}`, + }, + } + ) + .catch((error) => { + toast({ + title: "Error adding member", + description: error.response.data.error, + status: "error", + duration: 2000, + isClosable: true, + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as AxiosResponse; + + if (resp.status > 199 && resp.status < 300) { + onClose(); + navigate(0); + } + }; + + return ( + <> + + + + Add Member + + + + + Email + setNewEmail(e.target.value)} + /> + + + + Role + + + + + + + + + + + + ); + }; + \ No newline at end of file diff --git a/veda-auth-portal/src/components/Groups/CreateGroupModal.tsx b/veda-auth-portal/src/components/Groups/CreateGroupModal.tsx new file mode 100644 index 0000000..410c42d --- /dev/null +++ b/veda-auth-portal/src/components/Groups/CreateGroupModal.tsx @@ -0,0 +1,231 @@ +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalFooter, + ModalBody, + ModalCloseButton, + Button, + VStack, + FormControl, + FormLabel, + Input, + Textarea, + Box, + useToast +} from '@chakra-ui/react'; +import { Group } from '../../interfaces/Groups'; +import { useState } from 'react'; +import { TagsInput } from "react-tag-input-component"; +import axios from 'axios'; +import { BACKEND_URL } from '../../lib/constants'; +import { useNavigate } from 'react-router-dom'; + +export const CreateGroupModal = ({isOpen, onClose, auth }: { + isOpen: boolean; + onClose: () => void; + auth: any; +}) => { + const [group, setGroup] = useState({} as Group); + const toast = useToast(); + const navigate = useNavigate(); + + const handleSubmit = () => { + if (!group.id || !group.name || !group.description) { + toast({ + title: 'Invalid input', + description: 'Please fill out all required fields', + status: 'error', + duration: 3000, + isClosable: true, + }); + return; + } + + const groupObj = { + ...group, + owner_id: auth?.user?.profile?.email, + } + + axios.post(`${BACKEND_URL}/api/v1/group-management/groups`, groupObj, { + headers: { + Authorization: `Bearer ${auth.user.access_token}` + } + }).then(() => { + onClose(); + navigate(0); + }).catch((err) => { + console.log(err); + toast({ + title: 'An error occurred', + description: err.response?.data?.message || 'Please try again later', + status: 'error', + duration: 3000, + isClosable: true, + }); + }); + }; + + return ( + <> + + + + Create Group + + + + + Id + setGroup((prev) => ({...prev, id: e.target.value}))} + /> + + + + Name + setGroup((prev) => ({...prev, name: e.target.value}))} + /> + + + + + Description +