diff --git a/backend/src/admin/admin.controller.ts b/backend/src/admin/admin.controller.ts index 9fa23ef22c556d6321f7ebd258889610dfb8bfe3..5ed0d76f693df6677dd567a461614ba6506cc20e 100644 --- a/backend/src/admin/admin.controller.ts +++ b/backend/src/admin/admin.controller.ts @@ -1,7 +1,7 @@ -import { Controller, Get, Req, UseGuards } from '@nestjs/common'; +import { Controller, Get, Param, Req, UseGuards } from '@nestjs/common'; import { AdminService } from './admin.service'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { ADMIN_ROLES } from 'src/shared/enum/admin-role.enum'; +import { ApiBearerAuth, ApiParam, ApiTags } from '@nestjs/swagger'; +import { ADMIN_ROLES, SUPERADMIN_ROLES } from 'src/shared/enum/admin-role.enum'; import { Roles } from 'src/auth/role.decorator'; import { CustomAdminRequest } from 'src/auth/admin/interface/customAdminReq'; import { AuthAdminGuard } from 'src/auth/admin/authAdmin.guard'; @@ -24,4 +24,18 @@ export class AdminController { idTown: req.admin?.idTown?.townId || null, }; } + + @UseGuards(AuthAdminGuard) + @Roles(SUPERADMIN_ROLES) + @ApiParam({ name: 'idTown', type: Number }) + @Get('admin/:idTown') + @ApiBearerAuth('jwt') + async findAllByTown(@Param('idTown') idTown: number) { + try { + const admins = await this.adminService.findAllByTown(idTown); + return admins; + } catch (e) { + return e; + } + } } diff --git a/backend/src/admin/admin.service.ts b/backend/src/admin/admin.service.ts index 65c9a37712d9bf7d253c8f463226da095728d14e..395962f7c60b0ca1d1e29145f4b85fe46944dab6 100644 --- a/backend/src/admin/admin.service.ts +++ b/backend/src/admin/admin.service.ts @@ -5,6 +5,7 @@ import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; import { Town } from 'src/town/entities/town.entity'; import { ADMIN_ROLE } from 'src/shared/enum/admin-role.enum'; +import { GetAdminDto } from './dto/get-admin.dto'; @Injectable() export class AdminService { @@ -31,4 +32,22 @@ export class AdminService { async updatePassword(email: string, password: string) { await this.adminRepository.update({ email }, { password }); } + + async findAllByTown(idTown : number) : Promise { + const res: any[] = await this.adminRepository + .createQueryBuilder('admin') + .leftJoinAndSelect('admin.idTown', 'town') + .where('admin.idTown = :idTown', { idTown: idTown }) + .getMany(); + const admins: GetAdminDto[] = res.map((admin) :GetAdminDto => { + return { + email: admin.email, + idTown: admin.idTown.townId, + name: admin.name, + lastName: admin.lastName, + status: admin.status + }; + }); + return admins; + } } diff --git a/backend/src/admin/dto/get-admin.dto.ts b/backend/src/admin/dto/get-admin.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c59160bd48007d297d9f3a0cfa6ef5e0121b5cd --- /dev/null +++ b/backend/src/admin/dto/get-admin.dto.ts @@ -0,0 +1,18 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class GetAdminDto { + @ApiProperty() + email: string; + + @ApiProperty() + idTown: number; + + @ApiProperty() + name: string; + + @ApiProperty() + lastName: string; + + @ApiProperty() + status: string; +} \ No newline at end of file diff --git a/web/src/components/footer/assets/css/styles.css b/web/src/components/footer/assets/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..f09345290971ffcb350a13ef7a9e47d0b02e81f6 --- /dev/null +++ b/web/src/components/footer/assets/css/styles.css @@ -0,0 +1,39 @@ +.footer{ + display: flex; + height: 100%; + width: 100%; + align-items: center; + flex-wrap: wrap; + padding: 1em; +} + +.footer_license , +.footer_developers{ + display: flex; + flex-direction: column; +} + +.footer_license{ + margin-top: auto; +} + +.footer_credits{ + height: 100%; +} + +.footer_icons{ + height: 50%; + margin-left: auto; +} + +.footer_images{ + height: 100%; + display: flex; + align-items: center; +} + +.footer_images img{ + height: 100%; + width: 100%; + object-fit: contain; +} \ No newline at end of file diff --git a/web/src/components/footer/footer.tsx b/web/src/components/footer/footer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..425f31969ebd65d3854fc942cc3849a504aa312b --- /dev/null +++ b/web/src/components/footer/footer.tsx @@ -0,0 +1,70 @@ +import './assets/css/styles.css'; + +interface Developer{ + name: string; + lastName: string; + role: string; + linkedInURL: string; +} + +export const Footer = () => { + const style: React.CSSProperties = { + fontSize: 11, + textDecoration: 'none', + color: 'black', + textAlign: 'left' + } + + const developers: Developer[] = [ + { + name: 'Lorenzo', + lastName: 'Trujillo', + role: 'Desarrollador FrontEnd Mobile', + linkedInURL: 'https://www.linkedin.com/in/lorenzotrujillorojassd/' + }, + { + name: 'Diego I.', + lastName: 'Correa', + role: 'Desarrollador BackEnd', + linkedInURL: 'https://www.linkedin.com/in/diego-ivan-correa/' + }, + { + name: 'Omar', + lastName: 'Luna', + role: 'Desarrollador FrontEnd Web', + linkedInURL: 'https://www.linkedin.com/in/omar-luna-hern%C3%A1ndez-a280ba267/' + } + ] + + return ( +
+
+
+
+ ©2024 + Labsol Network. +
+ Bajo licencia GPL v.3. +
+
+ { + developers.map((dev) => { + return ( + @ {dev.name + ' ' + dev.lastName + ' (' + dev.role +')'} + ); + }) + } +
+
+ +
+
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/web/src/components/sa_panel_admin/sa_panel_admin_list/assets/css/styles.css b/web/src/components/sa_panel_admin/sa_panel_admin_list/assets/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..79284dfaf87aa5b1b293b3c19c93d2e4ee45db0d --- /dev/null +++ b/web/src/components/sa_panel_admin/sa_panel_admin_list/assets/css/styles.css @@ -0,0 +1,35 @@ +.admin_list_cnt{ + display: flex; + height: 100%; + width: 100%; + flex-direction: column; +} + +.admin_list_header{ + display: flex; + background: white; + width: 100%; + height: 8%; + justify-content: center; + align-items: center; +} + +.admin_list_body{ + display: flex; + width: 100%; + flex-grow: 1; +} + +.data_table{ + height: 100%; +} + +.bhFeAR{ + display: flex !important; + height: 100%; +} + +.rdt_TableBody{ + max-height: 100%; + overflow-y: auto; +} \ No newline at end of file diff --git a/web/src/components/sa_panel_admin/sa_panel_admin_list/sa_panel_admin_list.tsx b/web/src/components/sa_panel_admin/sa_panel_admin_list/sa_panel_admin_list.tsx new file mode 100644 index 0000000000000000000000000000000000000000..229cae1c6c0cd0db13ae52f1937fdbfd5ff5e0fa --- /dev/null +++ b/web/src/components/sa_panel_admin/sa_panel_admin_list/sa_panel_admin_list.tsx @@ -0,0 +1,149 @@ +import DataTable, { TableColumn } from 'react-data-table-component'; +import './assets/css/styles.css'; +import { LoadingSpinner } from '../../loading_spinner/loading_spinner'; +import { useState } from 'react'; +import { Admin } from '../../../infraestructure/entities/admin_form_values'; +import { State } from '../../../infraestructure/entities/state'; +import { useTown } from '../../../hooks/useTown'; +import axios, { AxiosError } from 'axios'; +import { showErrorAxios } from '../../../utils/Messages'; +import { useAdmin } from '../../../hooks/useAdmin'; + +interface props{ + isWindowActive: boolean; + statesList: State[]; +} + +export const SuperAdminPanelAdminList = ({isWindowActive, statesList}: props) => { + const [isLoading, setIsLoading] = useState(false); + const { + townsList, + getTownsByState + } = useTown(); + + const { + adminList, + getAdminListByTown + } = useAdmin(); + + const columns : TableColumn[] = [ + { + name: "Email", + selector: row => row.email, + sortable: true + }, + { + name: "Nombre", + selector: row => row.name.substring(0,40), + sortable: true + }, + { + name: "Apellido", + selector: row => row.lastName.substring(0,40), + sortable: true + }, + { + name: "Estatus", + selector: row => row.status?.substring(0,40) ?? "", + sortable: true + } + ]; + + const refreshListTown = (stateId: number, name: string) => { + setIsLoading(true); + const getTownsList = async () => { + try { + getTownsByState(stateId, name); + } catch (error: any) { + if (axios.isAxiosError(error)) { + error as AxiosError; + showErrorAxios(error); + } + } + } + getTownsList(); + setIsLoading(false); + }; + + const refreshListAdmins = (idTown: number) => { + setIsLoading(true); + const getAdminsList = async () => { + try { + getAdminListByTown(idTown); + } catch (error: any) { + if (axios.isAxiosError(error)) { + error as AxiosError; + showErrorAxios(error); + } + } + } + getAdminsList(); + setIsLoading(false); + }; + + if(isLoading) return + + return ( +
+
+ Estado + + + Estado + + +
+
+ + } + columns={columns} data={adminList} selectableRows className="data_table" + /> +
+
+ ); +} \ No newline at end of file diff --git a/web/src/components/sa_panel_admin/sa_panel_admin_register/assets/css/styles.css b/web/src/components/sa_panel_admin/sa_panel_admin_register/assets/css/styles.css index 12fd39103efd5b854dac93a2c11feb59757ebb1e..fccd3249756d59b8fc46f86db03ed6c703bf777e 100644 --- a/web/src/components/sa_panel_admin/sa_panel_admin_register/assets/css/styles.css +++ b/web/src/components/sa_panel_admin/sa_panel_admin_register/assets/css/styles.css @@ -13,6 +13,7 @@ height: 70vh; background: green; display: flex; + z-index: 999; flex-direction: column; } diff --git a/web/src/components/sa_panel_admin/sa_panel_admin_register/sa_panel_admin_register.tsx b/web/src/components/sa_panel_admin/sa_panel_admin_register/sa_panel_admin_register.tsx index eab2258613b8c11cebbd4ebf58bc0b8d59e520e3..5eb361a5fad02edaa57f48cde3028c7622e04f21 100644 --- a/web/src/components/sa_panel_admin/sa_panel_admin_register/sa_panel_admin_register.tsx +++ b/web/src/components/sa_panel_admin/sa_panel_admin_register/sa_panel_admin_register.tsx @@ -8,18 +8,18 @@ import { State } from "../../../infraestructure/entities/state"; import { useTown } from "../../../hooks/useTown"; interface props { - setWindowActive: Dispatch>, - setShowRegisterPanel: Dispatch>, - statesList : State[], + handleClickToClose: () => void; + forceRenderList: () => void; + statesList : State[]; } -export const SuperadminPanelAdminRegister = ({setWindowActive, setShowRegisterPanel, statesList}:props) => { +export const SuperadminPanelAdminRegister = ({handleClickToClose, forceRenderList, statesList}:props) => { const { register, errors, handleSubmit, onSubmit, - } = useAdmin(); + } = useAdmin(forceRenderList, handleClickToClose); const { values, @@ -27,14 +27,14 @@ export const SuperadminPanelAdminRegister = ({setWindowActive, setShowRegisterPa handleMouseDownPassword } = usePasswoordVisibility(); - const {townsList, getTownsByState, setTownsList} = useTown(); + const {townsList, getTownsByState} = useTown(); return (
Registra el administrador {setShowRegisterPanel(false); setWindowActive(false)}}/> + onClick={() => handleClickToClose()}/>
diff --git a/web/src/components/sa_panel_admin/sa_panel_admin_screen/sa_panel_admin_screen.tsx b/web/src/components/sa_panel_admin/sa_panel_admin_screen/sa_panel_admin_screen.tsx index d12a591342b723a69ebb621139dca007b4f22619..efc62d5e6d88c288d580d9c8958390a6922d8228 100644 --- a/web/src/components/sa_panel_admin/sa_panel_admin_screen/sa_panel_admin_screen.tsx +++ b/web/src/components/sa_panel_admin/sa_panel_admin_screen/sa_panel_admin_screen.tsx @@ -2,23 +2,34 @@ import { Dispatch, SetStateAction, useState } from 'react'; import './assets/css/styles.css'; import { SuperadminPanelAdminRegister } from '../sa_panel_admin_register/sa_panel_admin_register'; import { State } from '../../../infraestructure/entities/state'; +import { SuperAdminPanelAdminList } from '../sa_panel_admin_list/sa_panel_admin_list'; interface props { - windowActive: boolean; - setWindowActive: Dispatch>; + isWindowActive: boolean; + setIsWindowActive: Dispatch>; statesList: State[]; } -export const SuperadminPanelAdminScreen = ({windowActive, setWindowActive, statesList}:props) => { +export const SuperadminPanelAdminScreen = ({isWindowActive, setIsWindowActive, statesList}:props) => { const [showRegisterPanel, setShowRegisterPanel] = useState(false); + const [renderCount, setRenderCount] = useState(0); + + const handleClickToClose = () => { + setIsWindowActive(false); + setShowRegisterPanel(false); + } + + const forceRenderList = () =>{ + setRenderCount(prevCount => prevCount + 1); + } return (
Administrar administradores @@ -27,11 +38,12 @@ export const SuperadminPanelAdminScreen = ({windowActive, setWindowActive, state {showRegisterPanel && } +
); diff --git a/web/src/constants/api_routes.ts b/web/src/constants/api_routes.ts index 79d533d59113c3266f5b3ef17e132b6eed7dc36c..7322314f6894a9ad3b8c709df6651ae4dc54eef4 100644 --- a/web/src/constants/api_routes.ts +++ b/web/src/constants/api_routes.ts @@ -1,4 +1,4 @@ -const API_ROUTE_ADMIN = '/admin'; +export const API_ROUTE_ADMIN = '/admin'; export const API_ROUTE_ADMIN_SIGNUP = API_ROUTE_ADMIN+ '/signup'; export const API_ROUTE_ADMIN_SIGNIN = API_ROUTE_ADMIN+ '/signin'; export const API_ROUTE_ADMIN_CHANGE_PASSWORD = API_ROUTE_ADMIN+ '/change-password'; diff --git a/web/src/data/datasources/prod/admin_datasource.ts b/web/src/data/datasources/prod/admin_datasource.ts index 36e912eee5506785fa0bbbffbf92c349db9c7eab..517fe6bd9fa259dda834576670842eee44d45cce 100644 --- a/web/src/data/datasources/prod/admin_datasource.ts +++ b/web/src/data/datasources/prod/admin_datasource.ts @@ -4,7 +4,7 @@ import { Admin, AdminFormValues } from "../../../infraestructure/entities/admin_ import { APIUrl } from "../../../constants/api_url"; import { AdminModel } from "../../models/prod/AdminModel"; import { UserRole } from "../../../constants/roles"; -import { API_ROUTE_ADMIN_CHANGE_PASSWORD, API_ROUTE_ADMIN_SIGNUP, API_ROUTE_ADMIN_WHOAMI } from "../../../constants/api_routes"; +import { API_ROUTE_ADMIN, API_ROUTE_ADMIN_CHANGE_PASSWORD, API_ROUTE_ADMIN_SIGNUP, API_ROUTE_ADMIN_WHOAMI } from "../../../constants/api_routes"; export class AdminDatasourceProd implements AdminDatasourceInf{ async registerAdmin(form: AdminFormValues): Promise { @@ -49,4 +49,18 @@ export class AdminDatasourceProd implements AdminDatasourceInf{ } ); } + + async getAdminsByTown(idTown: number): Promise { + const {data} = await axios.get(APIUrl + API_ROUTE_ADMIN + `/${idTown}`); + const admins: Admin[] = data.map((admin): Admin => { + return { + email: admin.email, + idTown: admin.idTown, + name: admin.name, + lastName: admin.lastName, + status: admin.status + }; + }) + return admins; + } } \ No newline at end of file diff --git a/web/src/data/models/prod/AdminModel.ts b/web/src/data/models/prod/AdminModel.ts index 99e742352882309b8d1e9fe276a00180fc19ca18..2ac2cfc40cdf758298e4d82b4da8a5b3822936b5 100644 --- a/web/src/data/models/prod/AdminModel.ts +++ b/web/src/data/models/prod/AdminModel.ts @@ -2,6 +2,7 @@ export interface AdminModel { email: string; name: string; lastName: string; - role: string; + role?: string; idTown?: number; + status: string; } \ No newline at end of file diff --git a/web/src/data/repositories/prod/admin_repository.ts b/web/src/data/repositories/prod/admin_repository.ts index 81862bfff446a10b683db35290226d0b63925e36..34dc370404381ad3249fdebb81d3c4c55fe5252a 100644 --- a/web/src/data/repositories/prod/admin_repository.ts +++ b/web/src/data/repositories/prod/admin_repository.ts @@ -18,4 +18,8 @@ export class AdminRepositoryProd implements AdminRepositoryInf{ async changePassword(token: string, prevPassword: string, newPassword: string): Promise { return this.datasource.changePassword(token, prevPassword, newPassword); } + + async getAdminsByTown(idTown: number): Promise { + return this.datasource.getAdminsByTown(idTown); + } } \ No newline at end of file diff --git a/web/src/hooks/useAdmin.tsx b/web/src/hooks/useAdmin.tsx index 6d37e0ae2c12184b855d95c636593f671a4eb9e9..4fb54bd425edecf7eac9701f48c1709e4a46c4b6 100644 --- a/web/src/hooks/useAdmin.tsx +++ b/web/src/hooks/useAdmin.tsx @@ -74,17 +74,22 @@ const resolver: Resolver = async (data) => { }; }; -export const useAdmin = () => { +export const useAdmin = (forceRenderList?: () => void, handleClickToClose?: () => void) => { const { register, handleSubmit, formState: {errors}, } = useForm({resolver}); + const [adminList, setAdminList] = useState([]); const onSubmit: SubmitHandler = (data: AdminFormValues) => { const fetch = async () => { try{ await adminRepository.registerAdmin(data); + if(forceRenderList && handleClickToClose){ + forceRenderList(); + handleClickToClose(); + } }catch(error: any){ let errorMessage: string = "Ha ocurrido un error"; if(axios.isAxiosError(error)){ @@ -131,5 +136,17 @@ export const useAdmin = () => { return null; } - return {register, handleSubmit, errors, onSubmit, getAdminInfo}; + const getAdminListByTown = async (idTown: number) => { + try{ + const adminList = await adminRepository.getAdminsByTown(idTown); + setAdminList(adminList); + }catch(error: any){ + if(axios.isAxiosError(error)){ + error as AxiosError; + showErrorAxios(error); + } + } + } + + return {register, handleSubmit, errors, onSubmit, getAdminInfo, adminList, getAdminListByTown}; } \ No newline at end of file diff --git a/web/src/infraestructure/datasources/admin_datasource.ts b/web/src/infraestructure/datasources/admin_datasource.ts index c8d5eea7daf10259aaf3d6a5a17d604fe70e3a89..7b77243e2a62bc2ea93b86b4671c95d4bb9f5c67 100644 --- a/web/src/infraestructure/datasources/admin_datasource.ts +++ b/web/src/infraestructure/datasources/admin_datasource.ts @@ -4,4 +4,5 @@ export interface AdminDatasourceInf{ registerAdmin(form: AdminFormValues): Promise; getAdminInfo(token: string): Promise; changePassword(token : string, prevPassword: string, newPassword: string): Promise; + getAdminsByTown(idTown: number): Promise; } \ No newline at end of file diff --git a/web/src/infraestructure/entities/admin_form_values.ts b/web/src/infraestructure/entities/admin_form_values.ts index 4611708259dc2ff0da9659ccba173646e656934e..69786190480db24a6bcf326fa1d2969895d251de 100644 --- a/web/src/infraestructure/entities/admin_form_values.ts +++ b/web/src/infraestructure/entities/admin_form_values.ts @@ -13,8 +13,9 @@ export interface Admin { email: string; name: string; lastName: string; - role: UserRole; + role?: UserRole; idTown?: number; + status?: string; } export interface AdminPasswordValues { diff --git a/web/src/infraestructure/repositories/admin_repository.ts b/web/src/infraestructure/repositories/admin_repository.ts index 0fdd0615d8404af837b8b896eeb29eed494bc6bd..e995087bfab27997282fcd610ba36571a5086229 100644 --- a/web/src/infraestructure/repositories/admin_repository.ts +++ b/web/src/infraestructure/repositories/admin_repository.ts @@ -4,4 +4,5 @@ export interface AdminRepositoryInf{ registerAdmin(form: AdminFormValues): Promise; getAdminInfo(token: string): Promise; changePassword(token : string, prevPassword: string, newPassword: string): Promise; + getAdminsByTown(idTown: number): Promise; } \ No newline at end of file diff --git a/web/src/pages/home/admin_page/admin_home_page.tsx b/web/src/pages/home/admin_page/admin_home_page.tsx index 53b1d26debb597ee7826c7ee1d6a72403a2aff6e..c2e90d3baa72a137132ce2847041aef5b1b2d969 100644 --- a/web/src/pages/home/admin_page/admin_home_page.tsx +++ b/web/src/pages/home/admin_page/admin_home_page.tsx @@ -12,6 +12,7 @@ import { ErrorWindow } from "../../../components/error_window/error_window"; import { LoadingSpinner } from "../../../components/loading_spinner/loading_spinner"; import { AdminTownInfo } from "../../../components/admin_town_info/admin_town_info"; import { AdminPanelPoiScreen } from "../../../components/admin_panel_poi/admin_panel_poi_screen/admin_panel_poi_screen"; +import { Footer } from "../../../components/footer/footer"; export const AdminHomePage = () => { const { @@ -88,7 +89,7 @@ export const AdminHomePage = () => { })()}
- +
diff --git a/web/src/pages/home/admin_page/assets/styles/style.css b/web/src/pages/home/admin_page/assets/styles/style.css index 0e22ba9f21fa8801ea3a6e1f69f7230050f99b88..7683cef59b5c4381fd92442f2af68c67345ac1b1 100644 --- a/web/src/pages/home/admin_page/assets/styles/style.css +++ b/web/src/pages/home/admin_page/assets/styles/style.css @@ -21,12 +21,13 @@ } .admin-panel-content{ - max-height: 84vh; - min-height: 84vh; + max-height: 80vh; + min-height: 80vh; display: flex; + background: white; } .footer-cnt{ - height: 10vh; + height: 14vh; background: #ECEAFF; -} +} \ No newline at end of file diff --git a/web/src/pages/home/super_admin_page/assets/styles/style.css b/web/src/pages/home/super_admin_page/assets/styles/style.css index 70256ece2c8703858702914a1e790f0c3cc768f8..f83d159735900cd746cd17819f95714f7f66c807 100644 --- a/web/src/pages/home/super_admin_page/assets/styles/style.css +++ b/web/src/pages/home/super_admin_page/assets/styles/style.css @@ -21,12 +21,12 @@ } .superadmin-panel-content{ - max-height: 84vh; - min-height: 84vh; + max-height: 80vh; + min-height: 80vh; display: flex; } .footer-cnt{ - height: 10vh; + height: 14vh; background: #ECEAFF; } diff --git a/web/src/pages/home/super_admin_page/super_admin_home_page.tsx b/web/src/pages/home/super_admin_page/super_admin_home_page.tsx index 0b357fa94400cabbafa0f2c3a4537f521a191f25..499d6dd62a1f9330bafbf1f022af5744894afb4c 100644 --- a/web/src/pages/home/super_admin_page/super_admin_home_page.tsx +++ b/web/src/pages/home/super_admin_page/super_admin_home_page.tsx @@ -12,6 +12,7 @@ import { LoadingSpinner } from "../../../components/loading_spinner/loading_spin import { SuperAdminSelectedPanel } from "../../../constants/selected_panel"; import { useSuperAdminHomePage } from "../../../hooks/useSuperadminHomePage"; import { SuperAdminPanelCategoryScreen } from "../../../components/sa_panel_category/sa_panel_category_screen/sa_panel_category_screen"; +import { Footer } from "../../../components/footer/footer"; export const SuperAdminHomePage = () => { const { @@ -83,8 +84,8 @@ export const SuperAdminHomePage = () => { /> case SuperAdminSelectedPanel.ADMINS: return case SuperAdminSelectedPanel.CATEGORIES: @@ -98,7 +99,7 @@ export const SuperAdminHomePage = () => { })()}
- +