From e27cce45e00188ed49844275cd4d53c2fff7a212 Mon Sep 17 00:00:00 2001 From: kevin Date: Tue, 28 Apr 2026 10:26:45 +0300 Subject: [PATCH] Create display droit access --- thanasoft-back/config/auth.php | 5 + .../database/seeders/AdminAccessSeeder.php | 39 + .../database/seeders/DatabaseSeeder.php | 6 +- thanasoft-front/src/services/accessControl.ts | 151 ++++ thanasoft-front/src/services/user.ts | 14 + .../src/stores/accessControlStore.ts | 237 ++++++ .../src/views/pages/Parametrage/Droits.vue | 731 +++++++++++++++++- 7 files changed, 1172 insertions(+), 11 deletions(-) create mode 100644 thanasoft-back/database/seeders/AdminAccessSeeder.php create mode 100644 thanasoft-front/src/services/accessControl.ts create mode 100644 thanasoft-front/src/stores/accessControlStore.ts diff --git a/thanasoft-back/config/auth.php b/thanasoft-back/config/auth.php index 7d1eb0d..b774d91 100644 --- a/thanasoft-back/config/auth.php +++ b/thanasoft-back/config/auth.php @@ -40,6 +40,11 @@ return [ 'driver' => 'session', 'provider' => 'users', ], + + 'sanctum' => [ + 'driver' => 'sanctum', + 'provider' => 'users', + ], ], /* diff --git a/thanasoft-back/database/seeders/AdminAccessSeeder.php b/thanasoft-back/database/seeders/AdminAccessSeeder.php new file mode 100644 index 0000000..603cea3 --- /dev/null +++ b/thanasoft-back/database/seeders/AdminAccessSeeder.php @@ -0,0 +1,39 @@ +firstOrCreate([ + 'name' => 'config.view_roles', + 'guard_name' => 'sanctum', + ]); + + $role = Role::query()->firstOrCreate([ + 'name' => 'administrator', + 'guard_name' => 'sanctum', + ]); + + $role->givePermissionTo($permission); + + $adminUser = User::query()->updateOrCreate( + ['email' => 'admin@admin.com'], + [ + 'name' => 'Admin User', + 'password' => 'password', + ] + ); + + $adminUser->assignRole($role); + } +} \ No newline at end of file diff --git a/thanasoft-back/database/seeders/DatabaseSeeder.php b/thanasoft-back/database/seeders/DatabaseSeeder.php index 0d7a314..7dba4cd 100644 --- a/thanasoft-back/database/seeders/DatabaseSeeder.php +++ b/thanasoft-back/database/seeders/DatabaseSeeder.php @@ -15,11 +15,7 @@ class DatabaseSeeder extends Seeder { // User::factory(10)->create(); - User::factory()->create([ - 'name' => 'Admin User', - 'email' => 'admin@admin.com', - 'password' => 'password', - ]); + $this->call(AdminAccessSeeder::class); $this->call(ProductCategorySeeder::class); $this->call(EmployeeSeeder::class); diff --git a/thanasoft-front/src/services/accessControl.ts b/thanasoft-front/src/services/accessControl.ts new file mode 100644 index 0000000..72d6096 --- /dev/null +++ b/thanasoft-front/src/services/accessControl.ts @@ -0,0 +1,151 @@ +import { request } from "./http"; + +export interface AccessControlRole { + id: number; + name: string; + guard_name: string; + permissions?: AccessControlPermission[]; + users_count?: number; +} + +export interface AccessControlPermission { + id: number; + name: string; + guard_name: string; + roles?: Array>; +} + +export interface AccessControlIndexResponse { + data: { + roles: AccessControlRole[]; + permissions: AccessControlPermission[]; + }; + message: string; +} + +export interface CreateRolePayload { + name: string; + guard_name?: string; + permissions?: string[]; +} + +export interface UpdateRolePayload extends Partial { + id: number; +} + +export interface SyncRolePermissionsPayload { + id: number; + permissions: string[]; +} + +export interface CreatePermissionPayload { + name: string; + guard_name?: string; +} + +export interface UpdatePermissionPayload + extends Partial { + id: number; +} + +export const AccessControlService = { + async getAccessControl(): Promise { + const response = await request({ + url: "/api/access-control", + method: "get", + }); + + return response.data; + }, + + async createRole(payload: CreateRolePayload): Promise { + const response = await request<{ + data: AccessControlRole; + message: string; + }>({ + url: "/api/access-control/roles", + method: "post", + data: payload, + }); + + return response.data; + }, + + async updateRole(payload: UpdateRolePayload): Promise { + const { id, ...data } = payload; + + const response = await request<{ + data: AccessControlRole; + message: string; + }>({ + url: `/api/access-control/roles/${id}`, + method: "put", + data, + }); + + return response.data; + }, + + async deleteRole(id: number): Promise<{ message: string }> { + return request<{ message: string }>({ + url: `/api/access-control/roles/${id}`, + method: "delete", + }); + }, + + async syncRolePermissions( + payload: SyncRolePermissionsPayload + ): Promise { + const response = await request<{ + data: AccessControlRole; + message: string; + }>({ + url: `/api/access-control/roles/${payload.id}/permissions`, + method: "put", + data: { permissions: payload.permissions }, + }); + + return response.data; + }, + + async createPermission( + payload: CreatePermissionPayload + ): Promise { + const response = await request<{ + data: AccessControlPermission; + message: string; + }>({ + url: "/api/access-control/permissions", + method: "post", + data: payload, + }); + + return response.data; + }, + + async updatePermission( + payload: UpdatePermissionPayload + ): Promise { + const { id, ...data } = payload; + + const response = await request<{ + data: AccessControlPermission; + message: string; + }>({ + url: `/api/access-control/permissions/${id}`, + method: "put", + data, + }); + + return response.data; + }, + + async deletePermission(id: number): Promise<{ message: string }> { + return request<{ message: string }>({ + url: `/api/access-control/permissions/${id}`, + method: "delete", + }); + }, +}; + +export default AccessControlService; diff --git a/thanasoft-front/src/services/user.ts b/thanasoft-front/src/services/user.ts index 7c6160a..fe82da6 100644 --- a/thanasoft-front/src/services/user.ts +++ b/thanasoft-front/src/services/user.ts @@ -4,12 +4,24 @@ export interface UserSummary { id: number; name: string; email: string; + roles?: Array<{ + id: number; + name: string; + guard_name: string; + }>; + permissions?: Array<{ + id: number; + name: string; + guard_name: string; + }>; } export interface CreateUserPayload { name: string; email: string; password?: string | null; + roles?: string[]; + permissions?: string[]; } export interface UpdateUserPayload { @@ -17,6 +29,8 @@ export interface UpdateUserPayload { name: string; email: string; password?: string | null; + roles?: string[]; + permissions?: string[]; } export const UserService = { diff --git a/thanasoft-front/src/stores/accessControlStore.ts b/thanasoft-front/src/stores/accessControlStore.ts new file mode 100644 index 0000000..7007002 --- /dev/null +++ b/thanasoft-front/src/stores/accessControlStore.ts @@ -0,0 +1,237 @@ +import { defineStore } from "pinia"; +import { computed, ref } from "vue"; +import AccessControlService from "@/services/accessControl"; +import type { + AccessControlPermission, + AccessControlRole, + CreatePermissionPayload, + CreateRolePayload, + UpdatePermissionPayload, + UpdateRolePayload, +} from "@/services/accessControl"; + +export const useAccessControlStore = defineStore("accessControl", () => { + const roles = ref([]); + const permissions = ref([]); + const loading = ref(false); + const error = ref(null); + + const allRoles = computed(() => roles.value); + const allPermissions = computed(() => permissions.value); + const isLoading = computed(() => loading.value); + const hasError = computed(() => error.value !== null); + + const setLoading = (value: boolean) => { + loading.value = value; + }; + + const setError = (value: string | null) => { + error.value = value; + }; + + const fetchAccessControl = async () => { + setLoading(true); + setError(null); + + try { + const response = await AccessControlService.getAccessControl(); + roles.value = response.roles; + permissions.value = response.permissions; + return response; + } catch (err: any) { + setError( + err.response?.data?.message || + err.message || + "Failed to fetch access control data" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const createRole = async (payload: CreateRolePayload) => { + setLoading(true); + setError(null); + + try { + const role = await AccessControlService.createRole(payload); + roles.value.push(role); + return role; + } catch (err: any) { + setError( + err.response?.data?.message || err.message || "Failed to create role" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const updateRole = async (payload: UpdateRolePayload) => { + setLoading(true); + setError(null); + + try { + const role = await AccessControlService.updateRole(payload); + const index = roles.value.findIndex((item) => item.id === role.id); + + if (index !== -1) { + roles.value[index] = role; + } + + return role; + } catch (err: any) { + setError( + err.response?.data?.message || err.message || "Failed to update role" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const deleteRole = async (id: number) => { + setLoading(true); + setError(null); + + try { + const response = await AccessControlService.deleteRole(id); + roles.value = roles.value.filter((role) => role.id !== id); + return response; + } catch (err: any) { + setError( + err.response?.data?.message || err.message || "Failed to delete role" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const syncRolePermissions = async (id: number, permissionNames: string[]) => { + setLoading(true); + setError(null); + + try { + const role = await AccessControlService.syncRolePermissions({ + id, + permissions: permissionNames, + }); + const index = roles.value.findIndex((item) => item.id === role.id); + + if (index !== -1) { + roles.value[index] = role; + } + + return role; + } catch (err: any) { + setError( + err.response?.data?.message || + err.message || + "Failed to sync role permissions" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const createPermission = async (payload: CreatePermissionPayload) => { + setLoading(true); + setError(null); + + try { + const permission = await AccessControlService.createPermission(payload); + permissions.value.push(permission); + return permission; + } catch (err: any) { + setError( + err.response?.data?.message || + err.message || + "Failed to create permission" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const updatePermission = async (payload: UpdatePermissionPayload) => { + setLoading(true); + setError(null); + + try { + const permission = await AccessControlService.updatePermission(payload); + const index = permissions.value.findIndex( + (item) => item.id === permission.id + ); + + if (index !== -1) { + permissions.value[index] = permission; + } + + return permission; + } catch (err: any) { + setError( + err.response?.data?.message || + err.message || + "Failed to update permission" + ); + throw err; + } finally { + setLoading(false); + } + }; + + const deletePermission = async (id: number) => { + setLoading(true); + setError(null); + + try { + const response = await AccessControlService.deletePermission(id); + permissions.value = permissions.value.filter( + (permission) => permission.id !== id + ); + + roles.value = roles.value.map((role) => ({ + ...role, + permissions: role.permissions?.filter( + (permission) => permission.id !== id + ), + })); + + return response; + } catch (err: any) { + setError( + err.response?.data?.message || + err.message || + "Failed to delete permission" + ); + throw err; + } finally { + setLoading(false); + } + }; + + return { + roles, + permissions, + loading, + error, + allRoles, + allPermissions, + isLoading, + hasError, + fetchAccessControl, + createRole, + updateRole, + deleteRole, + syncRolePermissions, + createPermission, + updatePermission, + deletePermission, + }; +}); + +export default useAccessControlStore; diff --git a/thanasoft-front/src/views/pages/Parametrage/Droits.vue b/thanasoft-front/src/views/pages/Parametrage/Droits.vue index a01f791..a54a2fa 100644 --- a/thanasoft-front/src/views/pages/Parametrage/Droits.vue +++ b/thanasoft-front/src/views/pages/Parametrage/Droits.vue @@ -1,11 +1,730 @@ - + +