From f15dbb56d13f252ecb3f3a3dca0204dc61ce99f2 Mon Sep 17 00:00:00 2001 From: Faell4328 Date: Thu, 4 Dec 2025 18:49:25 -0300 Subject: [PATCH 1/2] feat: add list accounts with pagination --- .../application/services/accounts/index.ts | 4 + .../src/application/services/session/index.ts | 1 + .../src/application/usecases/account/index.ts | 1 + .../usecases/account/listAccounts/contract.ts | 16 ++ .../usecases/account/listAccounts/index.ts | 34 ++++ apps/api/src/application/usecases/factory.ts | 2 + .../usecases/session/info/contract.ts | 1 + .../src/domain/repositories/account/index.ts | 21 +++ apps/api/src/infra/di/containers/usecases.ts | 6 + apps/api/src/infra/di/tokens.ts | 2 + .../v1/routes/admin/accounts/index.ts | 6 + .../v1/routes/admin/accounts/list/index.ts | 22 +++ .../src/presentation/v1/routes/admin/index.ts | 6 + apps/api/src/presentation/v1/routes/index.ts | 2 + apps/api/src/shared/schemas/ListAccounts.ts | 8 + apps/web/.env.example | 5 + apps/web/src/components/Menu/Item/index.tsx | 4 +- .../src/components/MenuAdmin/Item/index.tsx | 112 +++++++++++++ apps/web/src/components/MenuAdmin/index.tsx | 16 ++ apps/web/src/layout/Navigation/index.tsx | 5 + apps/web/src/routeTree.gen.ts | 54 +++++++ .../_auth/admin/accounts/list/index.lazy.tsx | 10 ++ .../routes/_auth/list-accounts/index.lazy.tsx | 10 ++ apps/web/src/sections/list_acconts/index.tsx | 148 ++++++++++++++++++ 24 files changed, 494 insertions(+), 2 deletions(-) create mode 100644 apps/api/src/application/usecases/account/listAccounts/contract.ts create mode 100644 apps/api/src/application/usecases/account/listAccounts/index.ts create mode 100644 apps/api/src/presentation/v1/routes/admin/accounts/index.ts create mode 100644 apps/api/src/presentation/v1/routes/admin/accounts/list/index.ts create mode 100644 apps/api/src/presentation/v1/routes/admin/index.ts create mode 100644 apps/api/src/shared/schemas/ListAccounts.ts create mode 100644 apps/web/.env.example create mode 100644 apps/web/src/components/MenuAdmin/Item/index.tsx create mode 100644 apps/web/src/components/MenuAdmin/index.tsx create mode 100644 apps/web/src/routes/_auth/admin/accounts/list/index.lazy.tsx create mode 100644 apps/web/src/routes/_auth/list-accounts/index.lazy.tsx create mode 100644 apps/web/src/sections/list_acconts/index.tsx diff --git a/apps/api/src/application/services/accounts/index.ts b/apps/api/src/application/services/accounts/index.ts index 4c6edcf..82f2862 100644 --- a/apps/api/src/application/services/accounts/index.ts +++ b/apps/api/src/application/services/accounts/index.ts @@ -470,6 +470,10 @@ export class AccountsService { pagination, }); } + @Catch() + async listAccounts({ pagination }: { pagination: PaginationInput }) { + return this.accountRepository.listAccounts({ pagination }); + } @Catch() async upsertRegistration( diff --git a/apps/api/src/application/services/session/index.ts b/apps/api/src/application/services/session/index.ts index 3a30fcc..353fb3e 100644 --- a/apps/api/src/application/services/session/index.ts +++ b/apps/api/src/application/services/session/index.ts @@ -119,6 +119,7 @@ export class SessionService { authenticated: true, session: { email: account.email, + type: account.type, token: session.token, }, }; diff --git a/apps/api/src/application/usecases/account/index.ts b/apps/api/src/application/usecases/account/index.ts index 14ebf44..4b833ce 100644 --- a/apps/api/src/application/usecases/account/index.ts +++ b/apps/api/src/application/usecases/account/index.ts @@ -11,6 +11,7 @@ export * from "./details"; export * from "./editCharacter"; export * from "./findCharacter"; export * from "./generatePasswordReset"; +export * from "./listAccounts"; export * from "./login"; export * from "./logout"; export * from "./permissioned"; diff --git a/apps/api/src/application/usecases/account/listAccounts/contract.ts b/apps/api/src/application/usecases/account/listAccounts/contract.ts new file mode 100644 index 0000000..d501bd9 --- /dev/null +++ b/apps/api/src/application/usecases/account/listAccounts/contract.ts @@ -0,0 +1,16 @@ +import type z from "zod"; +import { ListAccountSchema } from "@/shared/schemas/ListAccounts"; +import { createPaginateSchema, InputPageSchema } from "@/shared/utils/paginate"; + +export const ListAccountsContractSchema = { + input: InputPageSchema, + output: createPaginateSchema(ListAccountSchema), +}; + +export type ListAccountsContractInput = z.infer< + typeof ListAccountsContractSchema.input +>; + +export type ListAccountsContractOutput = z.infer< + typeof ListAccountsContractSchema.output +>; diff --git a/apps/api/src/application/usecases/account/listAccounts/index.ts b/apps/api/src/application/usecases/account/listAccounts/index.ts new file mode 100644 index 0000000..2924250 --- /dev/null +++ b/apps/api/src/application/usecases/account/listAccounts/index.ts @@ -0,0 +1,34 @@ +import { inject, injectable } from "tsyringe"; +import type { AccountsService } from "@/application/services"; +import type { Pagination } from "@/domain/modules"; +import { TOKENS } from "@/infra/di/tokens"; +import type { UseCase } from "@/shared/interfaces/usecase"; +import type { + ListAccountsContractInput, + ListAccountsContractOutput, +} from "./contract"; + +@injectable() +export class ListAccountsUseCase + implements UseCase +{ + constructor( + @inject(TOKENS.AccountsService) + private readonly accountsService: AccountsService, + @inject(TOKENS.Pagination) private readonly pagination: Pagination, + ) {} + + async execute( + input: ListAccountsContractInput, + ): Promise { + const { storeHistory, total } = await this.accountsService.listAccounts({ + pagination: input, + }); + + return this.pagination.paginate(storeHistory, { + page: input.page ?? 1, + size: input.size ?? 10, + total, + }); + } +} diff --git a/apps/api/src/application/usecases/factory.ts b/apps/api/src/application/usecases/factory.ts index 977d008..80f126e 100644 --- a/apps/api/src/application/usecases/factory.ts +++ b/apps/api/src/application/usecases/factory.ts @@ -64,6 +64,7 @@ export class UseCasesFactory { const changePasswordWithToken = this.di.resolve( TOKENS.AccountChangePasswordWithTokenUseCase, ); + const listAccounts = this.di.resolve(TOKENS.ListAccountsUseCase); return { create, @@ -84,6 +85,7 @@ export class UseCasesFactory { changePasswordWithOld, generatePasswordReset, changePasswordWithToken, + listAccounts, } as const; } diff --git a/apps/api/src/application/usecases/session/info/contract.ts b/apps/api/src/application/usecases/session/info/contract.ts index b74f76e..2121f20 100644 --- a/apps/api/src/application/usecases/session/info/contract.ts +++ b/apps/api/src/application/usecases/session/info/contract.ts @@ -8,6 +8,7 @@ export const SessionInfoContractSchema = { .object({ token: z.string(), email: z.email(), + type: z.number().nullable(), }) .nullable(), }), diff --git a/apps/api/src/domain/repositories/account/index.ts b/apps/api/src/domain/repositories/account/index.ts index 6466c00..0c8d107 100644 --- a/apps/api/src/domain/repositories/account/index.ts +++ b/apps/api/src/domain/repositories/account/index.ts @@ -135,6 +135,27 @@ export class AccountRepository { }; } + async listAccounts(opts?: { pagination: PaginationInput }) { + const page = opts?.pagination.page ?? 1; + const size = opts?.pagination.size ?? 10; + + const [storeHistory, total] = await Promise.all([ + this.prisma.accounts.findMany({ + orderBy: { + name: "desc", + }, + skip: (page - 1) * size, + take: size, + }), + this.prisma.accounts.count(), + ]); + + return { + storeHistory, + total, + }; + } + async details(email: string) { return this.prisma.accounts.findFirst({ where: { diff --git a/apps/api/src/infra/di/containers/usecases.ts b/apps/api/src/infra/di/containers/usecases.ts index 3da0f4c..37e9834 100644 --- a/apps/api/src/infra/di/containers/usecases.ts +++ b/apps/api/src/infra/di/containers/usecases.ts @@ -20,6 +20,7 @@ import { ChangePasswordWithTokenUseCase, ConfigInfoUseCase, ConfigUpdateUseCase, + ListAccountsUseCase, SessionAuthenticatedUseCase, SessionInfoUseCase, SessionNotAuthenticatedUseCase, @@ -114,6 +115,11 @@ export function registerUseCases() { { useClass: ChangePasswordWithTokenUseCase }, { lifecycle: Lifecycle.ResolutionScoped }, ); + container.register( + TOKENS.ListAccountsUseCase, + { useClass: ListAccountsUseCase }, + { lifecycle: Lifecycle.ResolutionScoped }, + ); container.register( TOKENS.SessionInfoUseCase, diff --git a/apps/api/src/infra/di/tokens.ts b/apps/api/src/infra/di/tokens.ts index d204777..30da6e4 100644 --- a/apps/api/src/infra/di/tokens.ts +++ b/apps/api/src/infra/di/tokens.ts @@ -30,6 +30,7 @@ import type { ChangePasswordWithTokenUseCase, ConfigInfoUseCase, ConfigUpdateUseCase, + ListAccountsUseCase, SessionAuthenticatedUseCase, SessionInfoUseCase, SessionNotAuthenticatedUseCase, @@ -183,6 +184,7 @@ export const TOKENS = { AccountChangePasswordWithTokenUseCase: token( "AccountChangePasswordWithTokenUseCase", ), + ListAccountsUseCase: token("ListAccountsUseCase"), WorldsListUseCase: token("WorldsListUseCase"), diff --git a/apps/api/src/presentation/v1/routes/admin/accounts/index.ts b/apps/api/src/presentation/v1/routes/admin/accounts/index.ts new file mode 100644 index 0000000..282e37e --- /dev/null +++ b/apps/api/src/presentation/v1/routes/admin/accounts/index.ts @@ -0,0 +1,6 @@ +import { base } from "@/infra/rpc/base"; +import { listAccountsRouter } from "./list"; + +export const adminAccountsRouter = base.prefix("/accounts").router({ + list: listAccountsRouter, +}); diff --git a/apps/api/src/presentation/v1/routes/admin/accounts/list/index.ts b/apps/api/src/presentation/v1/routes/admin/accounts/list/index.ts new file mode 100644 index 0000000..da85ad2 --- /dev/null +++ b/apps/api/src/presentation/v1/routes/admin/accounts/list/index.ts @@ -0,0 +1,22 @@ +import { ListAccountsContractSchema } from "@/application/usecases/account/listAccounts/contract"; +import { isPermissionedProcedure } from "@/presentation/procedures/isPermissioned"; + +export const listAccountsRouter = isPermissionedProcedure + .meta({ + permission: { + type: "GAME_MASTER", + }, + }) + .route({ + method: "GET", + path: "/list", + summary: "List Accounts", + successStatus: 200, + description: + "Retrieves a list of accounts registered on the server. Only GAME_MASTER and ADMIN users are allowed to perform this action", + }) + .input(ListAccountsContractSchema.input) + .output(ListAccountsContractSchema.output) + .handler(async ({ context, input }) => { + return await context.usecases.account.listAccounts.execute(input); + }); diff --git a/apps/api/src/presentation/v1/routes/admin/index.ts b/apps/api/src/presentation/v1/routes/admin/index.ts new file mode 100644 index 0000000..443e8d5 --- /dev/null +++ b/apps/api/src/presentation/v1/routes/admin/index.ts @@ -0,0 +1,6 @@ +import { base } from "@/infra/rpc/base"; +import { adminAccountsRouter } from "./accounts"; + +export const adminRouter = base.prefix("/admin").tag("Admin").router({ + accounts: adminAccountsRouter, +}); diff --git a/apps/api/src/presentation/v1/routes/index.ts b/apps/api/src/presentation/v1/routes/index.ts index 5a900ce..63280f6 100644 --- a/apps/api/src/presentation/v1/routes/index.ts +++ b/apps/api/src/presentation/v1/routes/index.ts @@ -1,5 +1,6 @@ import { base } from "@/infra/rpc/base"; import { accountsRouter } from "./accounts"; +import { adminRouter } from "./admin"; import { clientRouter } from "./client"; import { configRouter } from "./config"; import { pingRoute } from "./ping"; @@ -7,6 +8,7 @@ import { sessionRouter } from "./session"; import { worldsRouter } from "./worlds"; export const router = base.router({ + admin: adminRouter, ping: pingRoute, client: clientRouter, accounts: accountsRouter, diff --git a/apps/api/src/shared/schemas/ListAccounts.ts b/apps/api/src/shared/schemas/ListAccounts.ts new file mode 100644 index 0000000..9a3a483 --- /dev/null +++ b/apps/api/src/shared/schemas/ListAccounts.ts @@ -0,0 +1,8 @@ +import z from "zod"; + +export const ListAccountSchema = z.object({ + id: z.number(), + name: z.string().nullable(), + email: z.email(), + type: z.number(), +}); diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..7f33d16 --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1,5 @@ +VITE_SHOW_DEVTOOLS=true + +# ==== RPC ==== # +VITE_MIFORGE_RPC_URL="http://localhost:4000" +VITE_MIFORGE_RPC_PATH="/v1/rpc" \ No newline at end of file diff --git a/apps/web/src/components/Menu/Item/index.tsx b/apps/web/src/components/Menu/Item/index.tsx index 3ee4908..d35ba97 100644 --- a/apps/web/src/components/Menu/Item/index.tsx +++ b/apps/web/src/components/Menu/Item/index.tsx @@ -65,12 +65,12 @@ export const MenuItem = ({ icon, label, menus = [] }: Props) => { }} >
{menus.map((subMenu) => { const isActive = routerState.location.pathname === subMenu.to; diff --git a/apps/web/src/components/MenuAdmin/Item/index.tsx b/apps/web/src/components/MenuAdmin/Item/index.tsx new file mode 100644 index 0000000..2af573a --- /dev/null +++ b/apps/web/src/components/MenuAdmin/Item/index.tsx @@ -0,0 +1,112 @@ +import { Link, type LinkProps, useRouterState } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; +import { cn } from "@/sdk/utils/cn"; + +const Icons = { + management: "/assets/icons/32/loremaster_doll.gif", + news: "/assets/icons/32/news-menu.gif", + sphere: "/assets/icons/32/armillary_sphere.gif", + munster: "/assets/icons/32/baby_munster.gif", +}; + +type Props = { + label: string; + icon: keyof typeof Icons; + menus: Array<{ + label: string; + to: LinkProps["to"]; + new?: boolean; + hot?: boolean; + }>; +}; + +export const MenuItem = ({ icon, label, menus = [] }: Props) => { + const routerState = useRouterState(); + const [show, setShow] = useState(() => { + return menus.some( + (subMenu) => subMenu.to === routerState.location.pathname, + ); + }); + + useEffect(() => { + const shouldShow = menus.some( + (subMenu) => subMenu.to === routerState.location.pathname, + ); + setShow((prev) => (prev ? prev : shouldShow)); + }, [routerState.location.pathname, menus]); + + const heightTotal = menus.length * 18.8; + + return ( +
+
setShow(!show)} + > + {`${label}-icon-plus-minus`} + teste + + {label} + +
+ +
+
+
+ {menus.map((subMenu) => { + const isActive = routerState.location.pathname === subMenu.to; + + return ( + +
+ {isActive && {">"}} + {subMenu.label} +
+ {subMenu.new && ( + {`${subMenu.label}-new`} + )} + {subMenu.hot && ( + {`${subMenu.label}-hot`} + )} + + ); + })} +
+
+ ); +}; diff --git a/apps/web/src/components/MenuAdmin/index.tsx b/apps/web/src/components/MenuAdmin/index.tsx new file mode 100644 index 0000000..60b9271 --- /dev/null +++ b/apps/web/src/components/MenuAdmin/index.tsx @@ -0,0 +1,16 @@ +import { MenuBox } from "@/components/Box/Menu"; +import { MenuItem } from "./Item"; + +export const MenuAdmin = () => { + return ( +
+ + + +
+ ); +}; diff --git a/apps/web/src/layout/Navigation/index.tsx b/apps/web/src/layout/Navigation/index.tsx index 8efe2a8..2cce5e8 100644 --- a/apps/web/src/layout/Navigation/index.tsx +++ b/apps/web/src/layout/Navigation/index.tsx @@ -1,8 +1,12 @@ import { BoxDownload } from "@/components/Box/Download"; import { BoxLogin } from "@/components/Box/Login"; +import { MenuAdmin } from "@/components/MenuAdmin"; +import { useSession } from "@/sdk/contexts/session"; import { Menu } from "../../components/Menu"; export const Navigation = () => { + const role: number | null = useSession().session?.type || null; + return ( ); diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 3579720..82477ea 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -23,6 +23,9 @@ import { Route as AuthAccountPlayerNameDeleteIndexRouteImport } from './routes/_ const PublicTermsIndexLazyRouteImport = createFileRoute('/_public/terms/')() const Not_authLoginIndexLazyRouteImport = createFileRoute('/_not_auth/login/')() +const AuthListAccountsIndexLazyRouteImport = createFileRoute( + '/_auth/list-accounts/', +)() const Not_authAccountCreateIndexLazyRouteImport = createFileRoute( '/_not_auth/account/create/', )() @@ -44,6 +47,9 @@ const AuthAccountAudit_historyIndexLazyRouteImport = createFileRoute( const Not_authAccountEmailConfirmIndexLazyRouteImport = createFileRoute( '/_not_auth/account/$email/confirm/', )() +const AuthAdminAccountsListIndexLazyRouteImport = createFileRoute( + '/_auth/admin/accounts/list/', +)() const PublicRouteRoute = PublicRouteRouteImport.update({ id: '/_public', @@ -76,6 +82,14 @@ const Not_authLoginIndexLazyRoute = Not_authLoginIndexLazyRouteImport.update({ } as any).lazy(() => import('./routes/_not_auth/login/index.lazy').then((d) => d.Route), ) +const AuthListAccountsIndexLazyRoute = + AuthListAccountsIndexLazyRouteImport.update({ + id: '/list-accounts/', + path: '/list-accounts/', + getParentRoute: () => AuthRouteRoute, + } as any).lazy(() => + import('./routes/_auth/list-accounts/index.lazy').then((d) => d.Route), + ) const AuthAccountIndexRoute = AuthAccountIndexRouteImport.update({ id: '/account/', path: '/account/', @@ -149,6 +163,16 @@ const Not_authAccountEmailConfirmIndexLazyRoute = (d) => d.Route, ), ) +const AuthAdminAccountsListIndexLazyRoute = + AuthAdminAccountsListIndexLazyRouteImport.update({ + id: '/admin/accounts/list/', + path: '/admin/accounts/list/', + getParentRoute: () => AuthRouteRoute, + } as any).lazy(() => + import('./routes/_auth/admin/accounts/list/index.lazy').then( + (d) => d.Route, + ), + ) const AuthAccountPlayerCreateIndexRoute = AuthAccountPlayerCreateIndexRouteImport.update({ id: '/account/player/create/', @@ -193,6 +217,7 @@ const AuthAccountPlayerNameDeleteIndexRoute = export interface FileRoutesByFullPath { '/': typeof PublicIndexRoute '/account': typeof AuthAccountIndexRoute + '/list-accounts': typeof AuthListAccountsIndexLazyRoute '/login': typeof Not_authLoginIndexLazyRoute '/terms': typeof PublicTermsIndexLazyRoute '/account/audit_history': typeof AuthAccountAudit_historyIndexLazyRoute @@ -202,6 +227,7 @@ export interface FileRoutesByFullPath { '/account/reset_password': typeof AuthAccountReset_passwordIndexLazyRoute '/account/create': typeof Not_authAccountCreateIndexLazyRoute '/account/player/create': typeof AuthAccountPlayerCreateIndexRoute + '/admin/accounts/list': typeof AuthAdminAccountsListIndexLazyRoute '/account/$email/confirm': typeof Not_authAccountEmailConfirmIndexLazyRoute '/account/player/$name/delete': typeof AuthAccountPlayerNameDeleteIndexRoute '/account/player/$name/edit': typeof AuthAccountPlayerNameEditIndexRoute @@ -210,6 +236,7 @@ export interface FileRoutesByFullPath { export interface FileRoutesByTo { '/': typeof PublicIndexRoute '/account': typeof AuthAccountIndexRoute + '/list-accounts': typeof AuthListAccountsIndexLazyRoute '/login': typeof Not_authLoginIndexLazyRoute '/terms': typeof PublicTermsIndexLazyRoute '/account/audit_history': typeof AuthAccountAudit_historyIndexLazyRoute @@ -219,6 +246,7 @@ export interface FileRoutesByTo { '/account/reset_password': typeof AuthAccountReset_passwordIndexLazyRoute '/account/create': typeof Not_authAccountCreateIndexLazyRoute '/account/player/create': typeof AuthAccountPlayerCreateIndexRoute + '/admin/accounts/list': typeof AuthAdminAccountsListIndexLazyRoute '/account/$email/confirm': typeof Not_authAccountEmailConfirmIndexLazyRoute '/account/player/$name/delete': typeof AuthAccountPlayerNameDeleteIndexRoute '/account/player/$name/edit': typeof AuthAccountPlayerNameEditIndexRoute @@ -231,6 +259,7 @@ export interface FileRoutesById { '/_public': typeof PublicRouteRouteWithChildren '/_public/': typeof PublicIndexRoute '/_auth/account/': typeof AuthAccountIndexRoute + '/_auth/list-accounts/': typeof AuthListAccountsIndexLazyRoute '/_not_auth/login/': typeof Not_authLoginIndexLazyRoute '/_public/terms/': typeof PublicTermsIndexLazyRoute '/_auth/account/audit_history/': typeof AuthAccountAudit_historyIndexLazyRoute @@ -240,6 +269,7 @@ export interface FileRoutesById { '/_auth/account/reset_password/': typeof AuthAccountReset_passwordIndexLazyRoute '/_not_auth/account/create/': typeof Not_authAccountCreateIndexLazyRoute '/_auth/account/player/create/': typeof AuthAccountPlayerCreateIndexRoute + '/_auth/admin/accounts/list/': typeof AuthAdminAccountsListIndexLazyRoute '/_not_auth/account/$email/confirm/': typeof Not_authAccountEmailConfirmIndexLazyRoute '/_auth/account/player/$name/delete/': typeof AuthAccountPlayerNameDeleteIndexRoute '/_auth/account/player/$name/edit/': typeof AuthAccountPlayerNameEditIndexRoute @@ -250,6 +280,7 @@ export interface FileRouteTypes { fullPaths: | '/' | '/account' + | '/list-accounts' | '/login' | '/terms' | '/account/audit_history' @@ -259,6 +290,7 @@ export interface FileRouteTypes { | '/account/reset_password' | '/account/create' | '/account/player/create' + | '/admin/accounts/list' | '/account/$email/confirm' | '/account/player/$name/delete' | '/account/player/$name/edit' @@ -267,6 +299,7 @@ export interface FileRouteTypes { to: | '/' | '/account' + | '/list-accounts' | '/login' | '/terms' | '/account/audit_history' @@ -276,6 +309,7 @@ export interface FileRouteTypes { | '/account/reset_password' | '/account/create' | '/account/player/create' + | '/admin/accounts/list' | '/account/$email/confirm' | '/account/player/$name/delete' | '/account/player/$name/edit' @@ -287,6 +321,7 @@ export interface FileRouteTypes { | '/_public' | '/_public/' | '/_auth/account/' + | '/_auth/list-accounts/' | '/_not_auth/login/' | '/_public/terms/' | '/_auth/account/audit_history/' @@ -296,6 +331,7 @@ export interface FileRouteTypes { | '/_auth/account/reset_password/' | '/_not_auth/account/create/' | '/_auth/account/player/create/' + | '/_auth/admin/accounts/list/' | '/_not_auth/account/$email/confirm/' | '/_auth/account/player/$name/delete/' | '/_auth/account/player/$name/edit/' @@ -352,6 +388,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Not_authLoginIndexLazyRouteImport parentRoute: typeof Not_authRouteRoute } + '/_auth/list-accounts/': { + id: '/_auth/list-accounts/' + path: '/list-accounts' + fullPath: '/list-accounts' + preLoaderRoute: typeof AuthListAccountsIndexLazyRouteImport + parentRoute: typeof AuthRouteRoute + } '/_auth/account/': { id: '/_auth/account/' path: '/account' @@ -408,6 +451,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof Not_authAccountEmailConfirmIndexLazyRouteImport parentRoute: typeof Not_authRouteRoute } + '/_auth/admin/accounts/list/': { + id: '/_auth/admin/accounts/list/' + path: '/admin/accounts/list' + fullPath: '/admin/accounts/list' + preLoaderRoute: typeof AuthAdminAccountsListIndexLazyRouteImport + parentRoute: typeof AuthRouteRoute + } '/_auth/account/player/create/': { id: '/_auth/account/player/create/' path: '/account/player/create' @@ -441,12 +491,14 @@ declare module '@tanstack/react-router' { interface AuthRouteRouteChildren { AuthAccountIndexRoute: typeof AuthAccountIndexRoute + AuthListAccountsIndexLazyRoute: typeof AuthListAccountsIndexLazyRoute AuthAccountAudit_historyIndexLazyRoute: typeof AuthAccountAudit_historyIndexLazyRoute AuthAccountCoins_historyIndexLazyRoute: typeof AuthAccountCoins_historyIndexLazyRoute AuthAccountDetailsIndexLazyRoute: typeof AuthAccountDetailsIndexLazyRoute AuthAccountRegistrationIndexLazyRoute: typeof AuthAccountRegistrationIndexLazyRoute AuthAccountReset_passwordIndexLazyRoute: typeof AuthAccountReset_passwordIndexLazyRoute AuthAccountPlayerCreateIndexRoute: typeof AuthAccountPlayerCreateIndexRoute + AuthAdminAccountsListIndexLazyRoute: typeof AuthAdminAccountsListIndexLazyRoute AuthAccountPlayerNameDeleteIndexRoute: typeof AuthAccountPlayerNameDeleteIndexRoute AuthAccountPlayerNameEditIndexRoute: typeof AuthAccountPlayerNameEditIndexRoute AuthAccountPlayerNameUndeleteIndexRoute: typeof AuthAccountPlayerNameUndeleteIndexRoute @@ -454,6 +506,7 @@ interface AuthRouteRouteChildren { const AuthRouteRouteChildren: AuthRouteRouteChildren = { AuthAccountIndexRoute: AuthAccountIndexRoute, + AuthListAccountsIndexLazyRoute: AuthListAccountsIndexLazyRoute, AuthAccountAudit_historyIndexLazyRoute: AuthAccountAudit_historyIndexLazyRoute, AuthAccountCoins_historyIndexLazyRoute: @@ -463,6 +516,7 @@ const AuthRouteRouteChildren: AuthRouteRouteChildren = { AuthAccountReset_passwordIndexLazyRoute: AuthAccountReset_passwordIndexLazyRoute, AuthAccountPlayerCreateIndexRoute: AuthAccountPlayerCreateIndexRoute, + AuthAdminAccountsListIndexLazyRoute: AuthAdminAccountsListIndexLazyRoute, AuthAccountPlayerNameDeleteIndexRoute: AuthAccountPlayerNameDeleteIndexRoute, AuthAccountPlayerNameEditIndexRoute: AuthAccountPlayerNameEditIndexRoute, AuthAccountPlayerNameUndeleteIndexRoute: diff --git a/apps/web/src/routes/_auth/admin/accounts/list/index.lazy.tsx b/apps/web/src/routes/_auth/admin/accounts/list/index.lazy.tsx new file mode 100644 index 0000000..a436987 --- /dev/null +++ b/apps/web/src/routes/_auth/admin/accounts/list/index.lazy.tsx @@ -0,0 +1,10 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { ListAccounts } from "@/sections/list_acconts"; + +export const Route = createLazyFileRoute("/_auth/admin/accounts/list/")({ + component: RouteComponent, +}); + +function RouteComponent() { + return ; +} diff --git a/apps/web/src/routes/_auth/list-accounts/index.lazy.tsx b/apps/web/src/routes/_auth/list-accounts/index.lazy.tsx new file mode 100644 index 0000000..a7d582c --- /dev/null +++ b/apps/web/src/routes/_auth/list-accounts/index.lazy.tsx @@ -0,0 +1,10 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { ListAccounts } from "@/sections/list_acconts"; + +export const Route = createLazyFileRoute("/_auth/list-accounts/")({ + component: RouteComponent, +}); + +function RouteComponent() { + return ; +} diff --git a/apps/web/src/sections/list_acconts/index.tsx b/apps/web/src/sections/list_acconts/index.tsx new file mode 100644 index 0000000..08657e8 --- /dev/null +++ b/apps/web/src/sections/list_acconts/index.tsx @@ -0,0 +1,148 @@ +import { keepPreviousData, useQuery } from "@tanstack/react-query"; +import { Link } from "@tanstack/react-router"; +import { PaginationControls } from "@/components/Pagination"; +import { + usePagination, + usePaginationControls, +} from "@/sdk/hooks/usePagination"; +import { api } from "@/sdk/lib/api/factory"; +import { cn } from "@/sdk/utils/cn"; +import { Container } from "@/ui/Container"; +import { InnerContainer } from "@/ui/Container/Inner"; +import { Tooltip } from "@/ui/Tooltip"; + +export const ListAccounts = () => { + const { pagination, setPagination } = usePagination({ + initialPage: 1, + initialSize: 10, + }); + const { data, refetch, isFetching } = useQuery( + api.query.miforge.admin.accounts.list.queryOptions({ + placeholderData: keepPreviousData, + input: { + page: pagination?.page, + size: pagination?.size, + }, + }), + ); + + const { + canGoToNextPage, + canGoToPreviousPage, + goToNextPage, + goToPreviousPage, + } = usePaginationControls({ + pagination, + setPagination, + totalItems: data?.meta?.total, + totalPages: data?.meta?.totalPages, + }); + + const accounts = data?.results ?? []; + + const roles = [ + "GUEST", + "PLAYER", + "TUTOR", + "SENIOR_TUTOR", + "GAME_MASTER", + "ADMIN", + ]; + + return ( + + + + } + > + + + + + + + + + + + {accounts.map((account, index) => { + return ( + + + + + + + + ); + })} + +
+ # + + Name + + Email + + Role + + Action +
+ + {index + 1}. + + +

{account.name}

+
+ {account.email} + +

{roles[account.type]}

+
+ [ + + Detailis + + ] +
+
+ + + +
+ ); +}; From 5e8f0ac3a0468907cd72d7146a978ab05e0740f1 Mon Sep 17 00:00:00 2001 From: Faell4328 Date: Thu, 4 Dec 2025 22:12:21 -0300 Subject: [PATCH 2/2] fix: applying corrections suggested by SonarQube --- apps/web/src/components/Menu/Item/index.tsx | 12 +- apps/web/src/components/Menu/index.tsx | 35 +++- .../src/components/MenuAdmin/Item/index.tsx | 112 ---------- apps/web/src/components/MenuAdmin/index.tsx | 16 -- apps/web/src/layout/Navigation/index.tsx | 28 ++- apps/web/src/sections/list_acconts/index.tsx | 196 +++++++++--------- 6 files changed, 156 insertions(+), 243 deletions(-) delete mode 100644 apps/web/src/components/MenuAdmin/Item/index.tsx delete mode 100644 apps/web/src/components/MenuAdmin/index.tsx diff --git a/apps/web/src/components/Menu/Item/index.tsx b/apps/web/src/components/Menu/Item/index.tsx index d35ba97..d4bb6be 100644 --- a/apps/web/src/components/Menu/Item/index.tsx +++ b/apps/web/src/components/Menu/Item/index.tsx @@ -1,12 +1,7 @@ import { Link, type LinkProps, useRouterState } from "@tanstack/react-router"; import { useEffect, useState } from "react"; import { cn } from "@/sdk/utils/cn"; - -const Icons = { - news: "/assets/icons/32/news-menu.gif", - sphere: "/assets/icons/32/armillary_sphere.gif", - munster: "/assets/icons/32/baby_munster.gif", -}; +import { Icons } from ".."; type Props = { label: string; @@ -38,7 +33,8 @@ export const MenuItem = ({ icon, label, menus = [] }: Props) => { return (
-
{ {label} -
+
{ +export const Icons = { + management: "/assets/icons/32/loremaster_doll.gif", + news: "/assets/icons/32/news-menu.gif", + sphere: "/assets/icons/32/armillary_sphere.gif", + munster: "/assets/icons/32/baby_munster.gif", +}; + +interface MenuProps { + items: Array<{ + label: string; + icon: keyof typeof Icons; + menus: Array<{ + label: string; + to: LinkProps["to"]; + hot?: boolean; + }>; + }>; +} + +export const Menu = ({ items }: MenuProps) => { return (
- - + {items.map((item) => ( + + ))}
); diff --git a/apps/web/src/components/MenuAdmin/Item/index.tsx b/apps/web/src/components/MenuAdmin/Item/index.tsx deleted file mode 100644 index 2af573a..0000000 --- a/apps/web/src/components/MenuAdmin/Item/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Link, type LinkProps, useRouterState } from "@tanstack/react-router"; -import { useEffect, useState } from "react"; -import { cn } from "@/sdk/utils/cn"; - -const Icons = { - management: "/assets/icons/32/loremaster_doll.gif", - news: "/assets/icons/32/news-menu.gif", - sphere: "/assets/icons/32/armillary_sphere.gif", - munster: "/assets/icons/32/baby_munster.gif", -}; - -type Props = { - label: string; - icon: keyof typeof Icons; - menus: Array<{ - label: string; - to: LinkProps["to"]; - new?: boolean; - hot?: boolean; - }>; -}; - -export const MenuItem = ({ icon, label, menus = [] }: Props) => { - const routerState = useRouterState(); - const [show, setShow] = useState(() => { - return menus.some( - (subMenu) => subMenu.to === routerState.location.pathname, - ); - }); - - useEffect(() => { - const shouldShow = menus.some( - (subMenu) => subMenu.to === routerState.location.pathname, - ); - setShow((prev) => (prev ? prev : shouldShow)); - }, [routerState.location.pathname, menus]); - - const heightTotal = menus.length * 18.8; - - return ( -
-
setShow(!show)} - > - {`${label}-icon-plus-minus`} - teste - - {label} - -
- -
-
-
- {menus.map((subMenu) => { - const isActive = routerState.location.pathname === subMenu.to; - - return ( - -
- {isActive && {">"}} - {subMenu.label} -
- {subMenu.new && ( - {`${subMenu.label}-new`} - )} - {subMenu.hot && ( - {`${subMenu.label}-hot`} - )} - - ); - })} -
-
- ); -}; diff --git a/apps/web/src/components/MenuAdmin/index.tsx b/apps/web/src/components/MenuAdmin/index.tsx deleted file mode 100644 index 60b9271..0000000 --- a/apps/web/src/components/MenuAdmin/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { MenuBox } from "@/components/Box/Menu"; -import { MenuItem } from "./Item"; - -export const MenuAdmin = () => { - return ( -
- - - -
- ); -}; diff --git a/apps/web/src/layout/Navigation/index.tsx b/apps/web/src/layout/Navigation/index.tsx index 2cce5e8..261f415 100644 --- a/apps/web/src/layout/Navigation/index.tsx +++ b/apps/web/src/layout/Navigation/index.tsx @@ -1,6 +1,5 @@ import { BoxDownload } from "@/components/Box/Download"; import { BoxLogin } from "@/components/Box/Login"; -import { MenuAdmin } from "@/components/MenuAdmin"; import { useSession } from "@/sdk/contexts/session"; import { Menu } from "../../components/Menu"; @@ -16,8 +15,31 @@ export const Navigation = () => { > - {role !== null && role >= 4 && } - + {role !== null && role >= 4 && ( + + )} + ); }; diff --git a/apps/web/src/sections/list_acconts/index.tsx b/apps/web/src/sections/list_acconts/index.tsx index 08657e8..d46abe1 100644 --- a/apps/web/src/sections/list_acconts/index.tsx +++ b/apps/web/src/sections/list_acconts/index.tsx @@ -9,6 +9,9 @@ import { api } from "@/sdk/lib/api/factory"; import { cn } from "@/sdk/utils/cn"; import { Container } from "@/ui/Container"; import { InnerContainer } from "@/ui/Container/Inner"; +import { Section } from "@/ui/Section"; +import { SectionHeader } from "@/ui/Section/Header"; +import { InnerSection } from "@/ui/Section/Inner"; import { Tooltip } from "@/ui/Tooltip"; export const ListAccounts = () => { @@ -50,99 +53,106 @@ export const ListAccounts = () => { ]; return ( - - - - } - > - - - - - - - - - - - {accounts.map((account, index) => { - return ( - - - - - - + + + + + + ); + })} + +
- # - - Name - - Email - - Role - - Action -
- - {index + 1}. - - -

{account.name}

-
- {account.email} - -

{roles[account.type]}

-
- [ - + +

Management

+
+ + + + + } + > + + + + + + + + + + + {accounts.map((account, index) => { + return ( + - Detailis - - ] - - - ); - })} - -
+ # + + Name + + Email + + Role + + Action +
-
- - - -
+
+ + {index + 1}. + + +

{account.name}

+
+ {account.email} + +

{roles[account.type]}

+
+ [ + + Detailis + + ] +
+
+ + + +
+ + ); };