Skip to content

Commit

Permalink
[front] ♻️ refactor: view by role permission
Browse files Browse the repository at this point in the history
  • Loading branch information
JohnPetros committed Dec 1, 2024
1 parent c92c684 commit 7f3e455
Show file tree
Hide file tree
Showing 23 changed files with 160 additions and 93 deletions.
13 changes: 13 additions & 0 deletions apps/server/src/api/controllers/auth/get-permissions-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { IHttp } from '@stocker/core/interfaces'
import { HTTP_STATUS_CODE } from '@stocker/core/constants'

import { companiesRepository } from '@/database'
import { User } from '@stocker/core/entities'

export class GetPermissionsController {
async handle(http: IHttp) {
const user = User.create(await http.getUser())
const role = await companiesRepository.findRoleById(user.role, user.companyId)
return http.send(role?.permissions, HTTP_STATUS_CODE.ok)
}
}
1 change: 1 addition & 0 deletions apps/server/src/api/controllers/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { ConfirmAuthController } from './confirm-auth-controller'
export { DeleteAccountController } from './delete-account-controller'
export { ResetPasswordController } from './reset-password-controller'
export { UpdateAccountController } from './update-account-controller'
export { GetPermissionsController } from './get-permissions-controller'
7 changes: 7 additions & 0 deletions apps/server/src/app/fastify/routes/auth-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FastifyHandler } from '../fastify-handler'
import {
ConfirmAuthController,
DeleteAccountController,
GetPermissionsController,
LoginController,
LogoutController,
ResetPasswordController,
Expand All @@ -23,6 +24,7 @@ export const AuthRoutes = async (app: FastifyInstance) => {
const deleteAccountController = new DeleteAccountController()
const updateAccountController = new UpdateAccountController()
const requestPasswordResetController = new RequestPasswordResetController()
const getPermissionsController = new GetPermissionsController()
const verifyJwtMiddleware = new FastifyHandler(new VerifyJwtMiddleware())
const verifyAdminRoleMiddleware = new FastifyHandler(
new VerifyRolePermissionMiddleware('all'),
Expand All @@ -39,6 +41,11 @@ export const AuthRoutes = async (app: FastifyInstance) => {
ws.join(userId, socket)
})

app.get('/permissions', async (request, response) => {
const http = new FastifyHttp(request, response)
return getPermissionsController.handle(http)
})

app.post('/confirm', async (request, response) => {
const http = new FastifyHttp(request, response)
return confirmAuthController.handle(http)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ export class PrismaUsersRepository implements IUsersRepository {
const role = await prisma.role.findUnique({ where: { name: user.role } })
if (!role) return

const prismaRoles = await prisma.role.findMany()
const prismaUser = this.mapper.toPrisma(user)
await prisma.user.create({
data: {
id: prismaUser.id,
name: prismaUser.name,
email: prismaUser.email,
password: prismaUser.password,
role_id: role.id,
role_id:
prismaRoles.find((prismaRole) => prismaRole.name === prismaUser.role?.name)
?.id ?? '',
has_first_password_reset: prismaUser.has_first_password_reset,
company_id: prismaUser.company_id,
},
Expand All @@ -35,14 +38,18 @@ export class PrismaUsersRepository implements IUsersRepository {
async addMany(users: User[]): Promise<void> {
try {
const prismaUsers = users.map(this.mapper.toPrisma)
const prismaRoles = await prisma.role.findMany()

await prisma.user.createMany({
data: prismaUsers.map((prismaUser) => {
return {
id: prismaUser.id,
name: prismaUser.name,
email: prismaUser.email,
password: prismaUser.password,
role_id: prismaUser.role_id,
role_id:
prismaRoles.find((prismaRole) => prismaRole.name === prismaUser.role?.name)
?.id ?? '',
has_first_password_reset: prismaUser.has_first_password_reset,
company_id: prismaUser.company_id,
}
Expand Down Expand Up @@ -174,13 +181,17 @@ export class PrismaUsersRepository implements IUsersRepository {
async update(user: User, userId: string): Promise<void> {
try {
const prismaUser = this.mapper.toPrisma(user)
const prismaRoles = await prisma.role.findMany()

await prisma.user.update({
data: {
name: prismaUser.name,
email: prismaUser.email,
password: prismaUser.password,
company_id: prismaUser.company_id,
role_id: prismaUser.role_id,
role_id:
prismaRoles.find((prismaRole) => prismaRole.name === prismaUser.role?.name)
?.id ?? '',
has_first_password_reset: prismaUser.has_first_password_reset,
},
where: { id: userId },
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/database/prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ export async function seed() {
)

const fakeUsers = UsersFaker.fakeMany(10, {
role: RolesFaker.fake({ name: 'employee' }),
role: 'employee',
companyId: fakeCompany.id,
})
fakeUsers.push(
UsersFaker.fake({
role: RolesFaker.fake({ name: 'admin' }),
role: 'admin',
companyId: fakeCompany.id,
email: '[email protected]',
password: await new CryptoProvider().hash('stocker123'),
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { getCookieAction } from './get-cookie-action'
export { setCookieAction } from './set-cookie-action'
export { deleteCookieAction } from './delete-cookie-action'
export { verifyUserRoleAction } from './verify-user-role-action'
export { verifyRolePermissionAction } from './verify-role-permission-action'
14 changes: 14 additions & 0 deletions apps/web/src/actions/verify-role-permission-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use server'

import { NextServerApiClient } from '@/api/next/clients/next-server-api-client'
import { AuthService } from '@/api/services'
import type { RolePermission } from '@stocker/core/types'

export async function verifyRolePermissionAction(permission: RolePermission) {
const apiClient = await NextServerApiClient({ isCacheEnabled: false })
const authService = AuthService(apiClient)
const response = await authService.getPermissions()
if (response.isFailure) response.throwError()

return response.body.includes(permission)
}
16 changes: 0 additions & 16 deletions apps/web/src/actions/verify-user-role-action.ts

This file was deleted.

4 changes: 2 additions & 2 deletions apps/web/src/app/(private)/dashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { verifyUserRoleAction } from '@/actions'
import { verifyRolePermissionAction } from '@/actions'
import { DashboardPage } from '@/ui/components/pages/dashboard'
import { notFound } from 'next/navigation'

const Page = async () => {
const isValidRole = await verifyUserRoleAction('manager')
const isValidRole = await verifyRolePermissionAction('reports')
if (!isValidRole) {
return notFound()
}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/(private)/inventory/movements/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { verifyUserRoleAction } from '@/actions'
import { verifyRolePermissionAction } from '@/actions'
import { InventoryMovementsPage } from '@/ui/components/pages/inventory-movements'
import { notFound } from 'next/navigation'

const Page = async () => {
const isValidRole = await verifyUserRoleAction('manager')
const isValidRole = await verifyRolePermissionAction('reports')
if (!isValidRole) {
return notFound()
}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/(private)/records/employees/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { verifyUserRoleAction } from '@/actions'
import { verifyRolePermissionAction } from '@/actions'
import { EmployeesPage } from '@/ui/components/pages/employees'
import { notFound } from 'next/navigation'

const Page = async () => {
const isValidRole = await verifyUserRoleAction('admin')
const isValidRole = await verifyRolePermissionAction('all')
if (!isValidRole) return notFound()

return <EmployeesPage />
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/(private)/records/locations/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { verifyUserRoleAction } from '@/actions'
import { verifyRolePermissionAction } from '@/actions'
import { LocationsPage } from '@/ui/components/pages/locations'
import { notFound } from 'next/navigation'

const Page = async () => {
const isValidRole = await verifyUserRoleAction('manager')
const isValidRole = await verifyRolePermissionAction('categories-control')
if (!isValidRole) {
return notFound()
}
Expand Down
10 changes: 9 additions & 1 deletion apps/web/src/app/(private)/records/products/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { notFound } from 'next/navigation'

import { verifyRolePermissionAction } from '@/actions'
import { ProductsPage } from '@/ui/components/pages/products'

const Page = () => {
const Page = async () => {
const isValidRole = await verifyRolePermissionAction('products-control')
if (!isValidRole) {
return notFound()
}

return <ProductsPage />
}

Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/app/(private)/records/suppliers/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { verifyUserRoleAction } from '@/actions'
import { verifyRolePermissionAction } from '@/actions'
import { SuppliersPage } from '@/ui/components/pages/suppliers'
import { notFound } from 'next/navigation'

const Page = async () => {
const isValidRole = await verifyUserRoleAction('manager')
const isValidRole = await verifyRolePermissionAction('suppliers-control')
if (!isValidRole) {
return notFound()
}
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/constants/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export const CACHE = {
company: {
key: '/company',
},
permissions: {
key: '/permissions',
},
supplier: {
key: '/supplier',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,29 @@ export function useAuthContextProvider({
response.throwError()
}

const { data, mutate } = useCache({
async function fetchPermissions() {
const response = await authService.getPermissions()

if (response.isSuccess) {
return response.body
}

response.throwError()
}

const { data: comapanyDto, mutate } = useCache({
fetcher: fetchCompany,
key: CACHE.company.key,
isEnabled: Boolean(user),
})

const company = data ? Company.create(data) : null
const company = comapanyDto ? Company.create(comapanyDto) : null

const { data: permissions } = useCache({
fetcher: fetchPermissions,
key: CACHE.company.key,
isEnabled: Boolean(user),
})

function getRouteByUserRole(role: RoleName) {
switch (role) {
Expand Down Expand Up @@ -197,6 +213,7 @@ export function useAuthContextProvider({
return {
user,
company,
permissions,
login,
logout,
subscribe,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { CompanyDto, UserDto } from '@stocker/core/dtos'
import type { Company, User } from '@stocker/core/entities'
import type { RolePermission } from '@stocker/core/types'

export type AuthContextValue = {
user: User | null
company: Company | null
jwt: string | null
permissions: RolePermission[]
login: (email: string, password: string) => Promise<void>
subscribe: (userDto: UserDto, companyDto: CompanyDto) => Promise<void>
logout: () => Promise<void>
Expand Down
15 changes: 7 additions & 8 deletions apps/web/src/ui/components/layouts/dashboard/navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import { useAuthContext } from '@/ui/components/contexts/auth-context'

export const Navbar = () => {
const { currentRoute } = useNavigation()
const { user } = useAuthContext()
const userLevel = user?.role
const { permissions } = useAuthContext()

return (
<NavbarRoot
Expand All @@ -34,7 +33,7 @@ export const Navbar = () => {
</NavbarBrand>
<NavbarContent className='flex-col gap-0 mt-6 p-0 w-full'>
<NavbarItem className='w-full'>
{userLevel !== 'employee' && (
{permissions.includes('reports') && (
<NavbarLink
href={ROUTES.dashboard}
icon='dashboard'
Expand Down Expand Up @@ -70,7 +69,7 @@ export const Navbar = () => {
</NavbarLink>
</NavbarItem>
<NavbarItem className='w-full'>
{userLevel !== 'employee' && (
{permissions.includes('reports') && (
<NavbarLink
href={ROUTES.inventory.movements}
icon='arrow-up-down'
Expand Down Expand Up @@ -106,7 +105,7 @@ export const Navbar = () => {
</NavbarLink>
</NavbarItem>
<NavbarItem className='w-full'>
{userLevel === 'admin' && (
{permissions.includes('all') && (
<NavbarLink
href={ROUTES.records.employees}
icon='employee'
Expand All @@ -117,7 +116,7 @@ export const Navbar = () => {
)}
</NavbarItem>
<NavbarItem className='w-full'>
{userLevel !== 'employee' && (
{permissions.includes('suppliers-control') && (
<NavbarLink
href={ROUTES.records.suppliers}
icon='supplier'
Expand All @@ -128,7 +127,7 @@ export const Navbar = () => {
)}
</NavbarItem>
<NavbarItem className='w-full'>
{userLevel !== 'employee' && (
{permissions.includes('categories-control') && (
<NavbarLink
href={ROUTES.records.categories}
icon='category'
Expand All @@ -139,7 +138,7 @@ export const Navbar = () => {
)}
</NavbarItem>
<NavbarItem className='w-full'>
{userLevel !== 'employee' && (
{permissions.includes('locations-control') && (
<NavbarLink
href={ROUTES.records.locations}
icon='location'
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/ui/components/pages/employees/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export const EmployeesPage = () => {
<SelectItem key='employee' value='employee'>
Funcionário
</SelectItem>

<SelectItem key='manager' value='manager'>
Gerente
</SelectItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { RoleName } from '@stocker/core/types'

import { CACHE } from '@/constants'
import {
useApi,
Expand All @@ -6,10 +8,8 @@ import {
useUrlParamNumber,
useUrlParamString,
} from '@/ui/hooks'
import type { UserDto } from '@stocker/core/dtos'
import { useState } from 'react'
import { useAuthContext } from '../../contexts/auth-context'
import type { UserRole } from '@stocker/core/types'

export function useEmployeesPage() {
const { showSuccess, showError } = useToast()
Expand All @@ -36,7 +36,7 @@ export function useEmployeesPage() {
page,
companyId: companyId,
name: nameSearchValue,
role: roleSearchValue as UserRole,
role: roleSearchValue as RoleName,
})
if (response.isFailure) {
showError(response.errorMessage)
Expand Down
Loading

0 comments on commit 7f3e455

Please sign in to comment.