diff --git a/src/assets/css/tailwind.presets.min.css b/src/assets/css/tailwind.presets.min.css index 5f5942530ddc156466edf15ea4d798a588320d5a..5b140dc9df1baffc784f856cc9cad91210a83f0c 100644 --- a/src/assets/css/tailwind.presets.min.css +++ b/src/assets/css/tailwind.presets.min.css @@ -1 +1 @@ -@tailwind base;@tailwind components;@tailwind utilities;@layer base{.conceal{@apply opacity-0 h-0 w-0 p-0 m-0 overflow-hidden}label,details{@apply block w-full pb-1 relative cursor-pointer disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-60 lg:min-w-xs lg:w-[calc(1/2*100%-(1*1rem/2))] xl:w-[calc((1/4*100%)-(3*1rem/4))]}fieldset{@apply pb-4 flex flex-wrap lg:gap-x-8 gap-x-4}img,svg,video,canvas,audio,iframe,embed,object{display:inline;vertical-align:middle}:root{--background: 0 0% 100%;--foreground: 0 0% 3.9%;--card: 0 0% 100%;--card-foreground: 0 0% 3.9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 3.9%;--primary: 204 98% 37%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96.1%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96.1%;--muted-foreground: 0 0% 45.1%;--accent: 0 0% 96.1%;--accent-foreground: 0 0% 9%;--destructive: 353 100% 44%;--destructive-foreground: 0 0% 98%;--border: 0 0% 89.8%;--input: 0 0% 89.8%;--ring: 0 0% 3.9%;--radius: 0.5rem}.dark{--background: 0 0% 3.9%;--foreground: 0 0% 98%;--card: 0 0% 3.9%;--card-foreground: 0 0% 98%;--popover: 0 0% 3.9%;--popover-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 9%;--secondary: 0 0% 14.9%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 14.9%;--muted-foreground: 0 0% 63.9%;--accent: 0 0% 14.9%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14.9%;--input: 0 0% 14.9%;--ring: 0 0% 83.1%}*{@apply border-border}body{@apply bg-background text-foreground}}/*# sourceMappingURL=tailwind.presets.min.css.map */ \ No newline at end of file +@tailwind base;@tailwind components;@tailwind utilities;@layer base{.conceal{@apply opacity-0 h-0 w-0 p-0 m-0 overflow-hidden}label,details{@apply block w-full pb-1 relative cursor-pointer disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-60 lg:min-w-xs lg:w-[calc(1/2*100%-(1*1rem/2))] xl:w-[calc((1/4*100%)-(3*1rem/4))]}fieldset{@apply pb-4 flex flex-wrap lg:gap-x-8 gap-4}img,svg,video,canvas,audio,iframe,embed,object{display:inline;vertical-align:middle}:root{--background: 0 0% 100%;--foreground: 0 0% 3.9%;--card: 0 0% 100%;--card-foreground: 0 0% 3.9%;--popover: 0 0% 100%;--popover-foreground: 0 0% 3.9%;--primary: 204 98% 37%;--primary-foreground: 0 0% 98%;--secondary: 0 0% 96.1%;--secondary-foreground: 0 0% 9%;--muted: 0 0% 96.1%;--muted-foreground: 0 0% 45.1%;--accent: 0 0% 96.1%;--accent-foreground: 0 0% 9%;--destructive: 353 100% 44%;--destructive-foreground: 0 0% 98%;--border: 0 0% 89.8%;--input: 0 0% 89.8%;--ring: 0 0% 3.9%;--radius: 0.5rem}.dark{--background: 0 0% 3.9%;--foreground: 0 0% 98%;--card: 0 0% 3.9%;--card-foreground: 0 0% 98%;--popover: 0 0% 3.9%;--popover-foreground: 0 0% 98%;--primary: 0 0% 98%;--primary-foreground: 0 0% 9%;--secondary: 0 0% 14.9%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 14.9%;--muted-foreground: 0 0% 63.9%;--accent: 0 0% 14.9%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 0% 98%;--border: 0 0% 14.9%;--input: 0 0% 14.9%;--ring: 0 0% 83.1%}*{@apply border-border}body{@apply bg-background text-foreground}}/*# sourceMappingURL=tailwind.presets.min.css.map */ \ No newline at end of file diff --git a/src/assets/css/tailwind.presets.min.css.map b/src/assets/css/tailwind.presets.min.css.map index eba59b315de1e3640407fff66e2a7487fe0ec9fd..9d16ae3ba983de16d4deb7fa8c28bc9e7f7737b7 100644 --- a/src/assets/css/tailwind.presets.min.css.map +++ b/src/assets/css/tailwind.presets.min.css.map @@ -1 +1 @@ -{"version":3,"sources":["../sass/tailwind.presets.scss"],"names":[],"mappings":"AAAA,cAAA,CACA,oBAAA,CACA,mBAAA,CACA,YACE,SACE,gDAAA,CAEF,cAEE,6MAAA,CAEF,SACE,6CAAA,CAEF,+CAQE,cAAA,CACA,qBAAA,CAEF,MACE,uBAAA,CACA,uBAAA,CAEA,iBAAA,CACA,4BAAA,CAEA,oBAAA,CACA,+BAAA,CAGA,sBAAA,CACA,8BAAA,CAEA,uBAAA,CACA,+BAAA,CAEA,mBAAA,CACA,8BAAA,CAEA,oBAAA,CACA,4BAAA,CAEA,2BAAA,CACA,kCAAA,CAEA,oBAAA,CACA,mBAAA,CACA,iBAAA,CAEA,gBAAA,CAGF,MACE,uBAAA,CACA,sBAAA,CAEA,iBAAA,CACA,2BAAA,CAEA,oBAAA,CACA,8BAAA,CAEA,mBAAA,CACA,6BAAA,CAEA,uBAAA,CACA,gCAAA,CAEA,mBAAA,CACA,8BAAA,CAEA,oBAAA,CACA,6BAAA,CAEA,4BAAA,CACA,kCAAA,CAEA,oBAAA,CACA,mBAAA,CACA,kBAAA,CAGF,EACE,oBAAA,CAEF,KACE,oCAAA,CAAA","file":"tailwind.presets.min.css"} \ No newline at end of file +{"version":3,"sources":["../sass/tailwind.presets.scss"],"names":[],"mappings":"AAAA,cAAA,CACA,oBAAA,CACA,mBAAA,CACA,YACE,SACE,gDAAA,CAEF,cAEE,6MAAA,CAEF,SACE,2CAAA,CAEF,+CAQE,cAAA,CACA,qBAAA,CAEF,MACE,uBAAA,CACA,uBAAA,CAEA,iBAAA,CACA,4BAAA,CAEA,oBAAA,CACA,+BAAA,CAGA,sBAAA,CACA,8BAAA,CAEA,uBAAA,CACA,+BAAA,CAEA,mBAAA,CACA,8BAAA,CAEA,oBAAA,CACA,4BAAA,CAEA,2BAAA,CACA,kCAAA,CAEA,oBAAA,CACA,mBAAA,CACA,iBAAA,CAEA,gBAAA,CAGF,MACE,uBAAA,CACA,sBAAA,CAEA,iBAAA,CACA,2BAAA,CAEA,oBAAA,CACA,8BAAA,CAEA,mBAAA,CACA,6BAAA,CAEA,uBAAA,CACA,gCAAA,CAEA,mBAAA,CACA,8BAAA,CAEA,oBAAA,CACA,6BAAA,CAEA,4BAAA,CACA,kCAAA,CAEA,oBAAA,CACA,mBAAA,CACA,kBAAA,CAGF,EACE,oBAAA,CAEF,KACE,oCAAA,CAAA","file":"tailwind.presets.min.css"} \ No newline at end of file diff --git a/src/assets/sass/tailwind.presets.scss b/src/assets/sass/tailwind.presets.scss index f2574039d174c30e2c4de27702286cf27d1003cb..d67f11863ea2d848c5ddfc63e2e4afb43f80a60e 100644 --- a/src/assets/sass/tailwind.presets.scss +++ b/src/assets/sass/tailwind.presets.scss @@ -10,7 +10,7 @@ @apply block w-full pb-1 relative cursor-pointer disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-60 lg:min-w-xs lg:w-[calc(1/2*100%-(1*1rem/2))] xl:w-[calc((1/4*100%)-(3*1rem/4))]; } fieldset { - @apply pb-4 flex flex-wrap lg:gap-x-8 gap-x-4; + @apply pb-4 flex flex-wrap lg:gap-x-8 gap-4; } img, svg, diff --git a/src/layouts/partials/navbar/DesktopNav.jsx b/src/layouts/partials/navbar/DesktopNav.jsx index 5fd7c77e894cda3feedda23c5b3a5c6cef812af1..9ffca67e7d603f6d580fa359ced0274335a709dc 100644 --- a/src/layouts/partials/navbar/DesktopNav.jsx +++ b/src/layouts/partials/navbar/DesktopNav.jsx @@ -1,10 +1,13 @@ import React from 'react'; +import { useAuth } from '../../../contexts/Auth/AuthState'; import DesktopLink from './DesktopLink'; function DesktopNav({ filteredSitemap }) { // ################################# // HOOKS // ################################# + // ### CONNECT AUTH CONTEXT + const { currentUser } = useAuth(); // ################################# // FUNCTIONS @@ -12,20 +15,28 @@ function DesktopNav({ filteredSitemap }) { // recursively render given menu const renderMenu = (menu, parent = null) => { if (!menu) return; - return menu.map((item, idx) => ( - <li key={`link-${idx}`} className="relative"> - {item.children?.length ? ( - <> + return menu.filter((item) => { + // dont show items that are above the current user role + if (item.gateKeeper && currentUser && currentUser.role < item.gateKeeper) return; + return item; + }).map((item, idx) => ( + // render menu items + <li key={`link-${idx}`} className="relative" > + { + item.children?.length ? ( + <> + <DesktopLink to={parent ? `${parent.path}/${item.path}` : item.path}>{item.title}</DesktopLink> + <ul className="absolute z-50 h-0 overflow-y-hidden bg-UhhBlue border-UhhBlue hover:h-auto peer-hover:h-auto"> + {renderMenu(item.children, item)} + </ul> + </> + ) : ( <DesktopLink to={parent ? `${parent.path}/${item.path}` : item.path}>{item.title}</DesktopLink> - <ul className="absolute z-50 h-0 overflow-y-hidden bg-UhhBlue border-UhhBlue hover:h-auto peer-hover:h-auto"> - {renderMenu(item.children, item)} - </ul> - </> - ) : ( - <DesktopLink to={parent ? `${parent.path}/${item.path}` : item.path}>{item.title}</DesktopLink> - )} + ) + } </li> + )); }; // ################################# diff --git a/src/layouts/partials/navbar/Navbar.jsx b/src/layouts/partials/navbar/Navbar.jsx index a9ccd8416f5a06cbe91b77612a384221ca1d1abc..e1e48c05bbb05bbb4939b93295b52cdb793b4e33 100644 --- a/src/layouts/partials/navbar/Navbar.jsx +++ b/src/layouts/partials/navbar/Navbar.jsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from 'react'; -import { sitemap } from "/src/routes/Sitemap"; +import { useAuth } from '../../../contexts/Auth/AuthState'; import DesktopNav from './DesktopNav'; import MobileNav from './MobileNav'; +import { sitemap } from "/src/routes/Sitemap"; function Navbar(props) { @@ -11,6 +12,9 @@ function Navbar(props) { // ### FILTERED SITEMAP const [filteredSitemap, setFilteredSitemap] = useState([]); + // ### CONNECT AUTH CONTEXT + const { currentUser } = useAuth(); + useEffect(() => { // fetch all links for navbars const [overall] = sitemap.filter((item) => item.title === 'MenuBar'); @@ -22,7 +26,12 @@ function Navbar(props) { function flatFilter(nestedProp, searchKey, searchValue, arr) { return arr.filter(o => { // slightly customized for searchKey = object - const keep = o[searchKey] && o[searchKey].hasOwnProperty(searchValue); + let keep = o[searchKey] && o[searchKey].hasOwnProperty(searchValue); + + // dont show items that are above the current user role + if (o.gateKeeper && currentUser && currentUser.role < o.gateKeeper) keep = false; + + if (keep && o[nestedProp]) { o[nestedProp] = flatFilter(nestedProp, searchKey, searchValue, o[nestedProp]); } diff --git a/src/pages/Config/AI/Models.jsx b/src/pages/Config/AI/Models.jsx index 3a36ab592b2f274b911555cf8c0ab2eeb3b0f1e9..182aa7e079d643cb925671be471556a246783e47 100644 --- a/src/pages/Config/AI/Models.jsx +++ b/src/pages/Config/AI/Models.jsx @@ -1,10 +1,9 @@ import React, { useState } from 'react'; -import { useAuth } from '/src/contexts/Auth/AuthState'; -import api from '/src/utils/AxiosConfig'; +import { RiDeleteBinLine, RiFileInfoLine, RiMoreLine, RiRefreshLine } from 'react-icons/ri'; import ConfirmBox from '/src/components/boxes/ConfirmBox'; import InfoBox from '/src/components/boxes/InfoBox'; import CustomTable from '/src/components/table/customTable'; -import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling'; +import { Button } from '/src/components/ui/button'; import { DropdownMenu, DropdownMenuContent, @@ -13,8 +12,9 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "/src/components/ui/dropdown-menu"; -import { Button } from '/src/components/ui/button'; -import { RiDeleteBinLine, RiFileInfoLine, RiMoreLine, RiRefreshLine } from 'react-icons/ri'; +import { useAuth } from '/src/contexts/Auth/AuthState'; +import api from '/src/utils/AxiosConfig'; +import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling'; function Models({ data, setData }) { @@ -152,7 +152,7 @@ function Models({ data, setData }) { // OUTPUT // ################################# return ( - <div> + <div className='self-start bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3'> {/* table */} <CustomTable columns={columns} data={data} title='installed models' /> diff --git a/src/pages/Config/AI/NewModel.jsx b/src/pages/Config/AI/NewModel.jsx index b2984761bec26f02a5bf12331191b6dd26d1ab7e..707bd12d0234928a86aa1d23da576ab636d293fa 100644 --- a/src/pages/Config/AI/NewModel.jsx +++ b/src/pages/Config/AI/NewModel.jsx @@ -49,18 +49,20 @@ function NewModel({ data, setData }) { // OUTPUT // ################################# return ( - <div> + <> {(currentUser?.role >= 2) ? - <FormProvider {...methods} > - <Heading level="4">install new model</Heading> - <form onSubmit={methods.handleSubmit(handleInstall)} className=''> - <Input name='model' type='text' title='Model Name' className='h-16' required={true} tooltip={<Link to='https://ollama.com/library' target='_blank' rel='noopener noreferrer'>Ollama Library</Link>} /> - <Submit size='sm' value={methods.formState.isSubmitting ? 'installing...' : 'install model'} /> - </form> - </FormProvider> - : null} + <div className='self-start bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3'> + <FormProvider {...methods} > + <Heading level="4">install new model</Heading> + <form onSubmit={methods.handleSubmit(handleInstall)} className=''> + <Input name='model' type='text' title='Model Name' className='h-16' required={true} tooltip={<Link to='https://ollama.com/library' target='_blank' rel='noopener noreferrer'>Ollama Library</Link>} /> + <Submit size='sm' value={methods.formState.isSubmitting ? 'installing...' : 'install model'} /> + </form> + </FormProvider> - </div> + </div> + : null} + </> ); } diff --git a/src/pages/Config/AI/Status.jsx b/src/pages/Config/AI/Status.jsx index c6f52227e10b102df0d4e146923064daca7c8150..59dff41b6d329366acac902727f39401d9867222 100644 --- a/src/pages/Config/AI/Status.jsx +++ b/src/pages/Config/AI/Status.jsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react'; -import { mergeBackendValidation } from '../../../utils/ErrorHandling'; -import api from '../../../utils/AxiosConfig'; import { RiWifiFill, RiWifiOffFill } from "react-icons/ri"; import Heading from '../../../components/font/Heading'; +import api from '../../../utils/AxiosConfig'; +import { mergeBackendValidation } from '../../../utils/ErrorHandling'; function AIStatus() { // ################################# @@ -43,11 +43,13 @@ function AIStatus() { // OUTPUT // ################################# return ( - <Heading level="4">status: - {status ? - <RiWifiFill className='ml-4 text-UhhBlue' title='AI backend reachable' /> - : <RiWifiOffFill className='ml-4 text-UhhRed' title='AI backend offline' />} - </Heading> + <div className='self-start bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3'> + <Heading level="4">status <br /> + {status ? + <RiWifiFill className='ml-4 text-UhhBlue' title='AI backend reachable' /> + : <RiWifiOffFill className='ml-4 text-UhhRed' title='AI backend offline' />} + </Heading> + </div> ); } diff --git a/src/pages/Config/Embeddings.jsx b/src/pages/Config/Embeddings.jsx index ae6b13cf34c242f71cf3008ddbae7e1d4c18d4f6..15d644ddd56e7ca38108515313b578c5c93c8959 100644 --- a/src/pages/Config/Embeddings.jsx +++ b/src/pages/Config/Embeddings.jsx @@ -62,7 +62,6 @@ function Embeddings() { <Update setStatus={setStatus} /> {/* delete embeddings */} <Delete setStatus={setStatus} /> - </fieldset> </section> ); diff --git a/src/pages/Config/Embeddings/Delete.jsx b/src/pages/Config/Embeddings/Delete.jsx index d1b2b1d50e923762ce7a29c11a852401b456f603..d3735abdb2d84dd11df2ba621072c966401ad8c6 100644 --- a/src/pages/Config/Embeddings/Delete.jsx +++ b/src/pages/Config/Embeddings/Delete.jsx @@ -44,9 +44,9 @@ function Delete({ setStatus }) { // OUTPUT // ################################# return ( - <div> + <> {(currentUser?.role >= 2) ? - <> + <div className='self-start bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3'> <FormProvider {...methods} > <Heading level="4">Delete Embedding Collection</Heading> <form onSubmit={methods.handleSubmit(handleConfirm)} className='md:w-1/3'> @@ -57,10 +57,10 @@ function Delete({ setStatus }) { <ConfirmBox confirmDialog={confirmDialog} closeDialog={() => setConfirmDialog({ ...confirmDialog, open: false })} handleProceed={() => { handleDelete(confirmDialog.idToDelete); }} /> - </> + </div> : null} - </div> + </> ); } diff --git a/src/pages/Config/Embeddings/Status.jsx b/src/pages/Config/Embeddings/Status.jsx index f54a2d95b27b3bcb13b5ddf3f17105cdf08b69ba..6a97dc052d5c55f3f50ecff0d826d97b167f3eb3 100644 --- a/src/pages/Config/Embeddings/Status.jsx +++ b/src/pages/Config/Embeddings/Status.jsx @@ -15,7 +15,7 @@ function Status({ status }) { // OUTPUT // ################################# return ( - <div> + <div className='self-start bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3'> <Heading level="4">Status</Heading> <JsonToHtmlDL jsonContent={status} /> </div> diff --git a/src/pages/Config/Embeddings/Update.jsx b/src/pages/Config/Embeddings/Update.jsx index 0f4cd095b8da956b980c25ec7fe140072abfc900..32040b2348c4db7b64b25b9d3ea3a1017988d959 100644 --- a/src/pages/Config/Embeddings/Update.jsx +++ b/src/pages/Config/Embeddings/Update.jsx @@ -46,22 +46,24 @@ function Update({ setStatus }) { // OUTPUT // ################################# return ( - <div> + <> {(currentUser?.role >= 2) ? - <FormProvider {...methods}> - <Heading level="4">Update Embeddings - <Tooltip><p className='text-base'>based on local RAG Files</p></Tooltip> - </Heading> - <form onSubmit={methods.handleSubmit(handleUpdate)} className='md:w-1/3'> - <Submit size='sm' value={methods.formState.isSubmitting ? 'updating...' : 'update'} /> - </form> - <details className='py-4 border-b border-grey-lighter'> - <summary>Update Result</summary> - <JsonToHtmlDL jsonContent={data} /> - </details> - </FormProvider> + <div className='self-start bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3'> + <FormProvider {...methods}> + <Heading level="4">Update Embeddings + <Tooltip><p className='text-base'>based on local RAG Files</p></Tooltip> + </Heading> + <form onSubmit={methods.handleSubmit(handleUpdate)} className='md:w-1/3'> + <Submit size='sm' value={methods.formState.isSubmitting ? 'updating...' : 'update'} /> + </form> + <details className='py-4 border-b border-grey-lighter'> + <summary>Update Result</summary> + <JsonToHtmlDL jsonContent={data} /> + </details> + </FormProvider> + </div> : null} - </div> + </> ); } diff --git a/src/pages/User/List/User.jsx b/src/pages/User/List/User.jsx index 54266a29d26141905ed5b6d8ec1d198f3187df5c..07a55a254dd71f088feda9a8e3a382e53f1fbbc7 100644 --- a/src/pages/User/List/User.jsx +++ b/src/pages/User/List/User.jsx @@ -11,7 +11,7 @@ import { ROLES } from '../UserTypes'; import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling'; -function User({ user, idx, handleDelete }) { +function User({ user, idx, setConfirmDialog }) { // ################################# // VALIDATION SCHEMA // ################################# @@ -67,7 +67,7 @@ function User({ user, idx, handleDelete }) { {currentUser?.role >= 2 ? <> <h2 className='text-Uhh-Grey font-bold text-2xl flex justify-end'> - <RiDeleteBinLine className='cursor-pointer hover:text-UhhRed' title='delete user' onClick={() => handleDelete(user.id, idx)} /> + <RiDeleteBinLine className='cursor-pointer hover:text-UhhRed' title='delete user' onClick={() => setConfirmDialog({ open: true, idToDelete: user.id, displayName: user.username })} /> </h2> <FormProvider {...methods} > <form onSubmit={methods.handleSubmit(handleSendForm)} noValidate> diff --git a/src/pages/User/List/Users.jsx b/src/pages/User/List/Users.jsx index 2ddb103b0671d16f5212b770571d57b311086e9e..86ec182da5f6c2413a86224f9f94e8ef6db898c1 100644 --- a/src/pages/User/List/Users.jsx +++ b/src/pages/User/List/Users.jsx @@ -1,9 +1,11 @@ import React, { useEffect, useState } from 'react'; import { Helmet } from 'react-helmet-async'; +import ConfirmBox from '../../../components/boxes/ConfirmBox'; import Heading from '../../../components/font/Heading'; import api from '../../../utils/AxiosConfig'; +import { setFlashMsg } from '../../../utils/ErrorHandling'; import User from './User'; -import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling'; +import { mergeBackendValidation } from '/src/utils/ErrorHandling'; function Users() { @@ -13,6 +15,9 @@ function Users() { // ### USERS const [users, setUsers] = useState(); + // ### CONFIRM DIALOG + const [confirmDialog, setConfirmDialog] = useState({ open: false, item: {} }); + // ### FETCH USERS useEffect(() => { // mount @@ -39,16 +44,19 @@ function Users() { // FUNCTIONS // ################################# // ### DELETE USERS - const handleDelete = async (id, idx) => { + const handleDelete = async (id) => { try { // delete in backend const result = await api.delete(`/users/${id}`); // delete in frontend - const list = [...users]; - list.splice(idx, 1); + const list = users.filter(user => user.id !== id); setUsers(list); setFlashMsg(result.data?.message); } catch (error) { + + console.log("🚀 ~ handleDelete ~ error:", error); + + mergeBackendValidation(error.response.status, error.response.data, methods.setError); } }; @@ -67,13 +75,15 @@ function Users() { ? ( <div className='flex flex-wrap justify-items-stretch'> {users.map((user, idx) => - <User key={user._id} idx={idx} user={user} handleDelete={handleDelete} /> + <User key={user._id} idx={idx} user={user} setConfirmDialog={setConfirmDialog} /> )} </div> ) : <p>No users to display</p> } </div> + {/* confirmDialog */} + <ConfirmBox confirmDialog={confirmDialog} closeDialog={() => setConfirmDialog({ ...confirmDialog, open: false })} handleProceed={() => { handleDelete(confirmDialog.idToDelete); }} /> </> ); } diff --git a/src/routes/Sitemap.jsx b/src/routes/Sitemap.jsx index 253a4b5e16c59a61f26fe22666ebae9217fc10a1..359bb1d0f4fe9bd7319786fbfcbf14e4d8d7e4e3 100644 --- a/src/routes/Sitemap.jsx +++ b/src/routes/Sitemap.jsx @@ -40,7 +40,7 @@ export const sitemap = [{ }, // USER { - title: 'Users', path: '/users', element: loadComponent('User/List/Users', true, true), handle: { crumb: () => <Link to="/users">Users</Link> } + title: 'Users', gateKeeper: 4, path: '/users', element: loadComponent('User/List/Users', true, true), handle: { crumb: () => <Link to="/users">Users</Link> } }, // PROFILE { diff --git a/src/routes/WrapRoutes.jsx b/src/routes/WrapRoutes.jsx index 562bd6b158ef08f44f06b8d52c93427300d67dff..6d3dcef2ba960ed40856cbbc6ecad88812fccc23 100644 --- a/src/routes/WrapRoutes.jsx +++ b/src/routes/WrapRoutes.jsx @@ -6,7 +6,7 @@ import PrivateRoute from './PrivateRoute'; * This method will be used in routes so that the files are loaded only * When users are on that route */ -export function loadComponent(componentPath, lazyLoad, privateRoute) { +export function loadComponent(componentPath, lazyLoad, privateRoute, gateKeeper = 0) { lazyLoad = typeof lazyLoad !== "undefined" ? lazyLoad : true; privateRoute = typeof privateRoute !== "undefined" ? privateRoute : false; @@ -22,4 +22,6 @@ export function loadComponent(componentPath, lazyLoad, privateRoute) { // Wrapping around the suspense component is mandatory return element; -} \ No newline at end of file +} + +