create page manage profiles, page add profile user
This commit is contained in:
		
							parent
							
								
									7c8267d6e3
								
							
						
					
					
						commit
						e226ad41d7
					
				| 
						 | 
					@ -115,4 +115,126 @@ class ProfileController extends Controller
 | 
				
			||||||
        $user->save();
 | 
					        $user->save();
 | 
				
			||||||
        return AbstractController::ResultSuccess($path);
 | 
					        return AbstractController::ResultSuccess($path);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function listFiles(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Get the root folder from the input URL
 | 
				
			||||||
 | 
					        $rootFolder = $request->input('root_folder');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Ensure the root folder is correctly formatted
 | 
				
			||||||
 | 
					        $rootFolder = rtrim($rootFolder, '/') . '/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get all files and directories in the specified root folder
 | 
				
			||||||
 | 
					        $fileList = $this->getDirectoryTree(public_path($rootFolder), $rootFolder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->json(['data' => $fileList, 'status' => true]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private function getDirectoryTree($dir, $urlRoot)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $results = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Scan the directory for files and folders
 | 
				
			||||||
 | 
					        $files = scandir($dir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        foreach ($files as $file) {
 | 
				
			||||||
 | 
					            if ($file !== '.' && $file !== '..') {
 | 
				
			||||||
 | 
					                $filePath = $dir . DIRECTORY_SEPARATOR . $file;
 | 
				
			||||||
 | 
					                $fileUrl = url($urlRoot . $file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (is_dir($filePath)) {
 | 
				
			||||||
 | 
					                    // If it's a directory, recurse into it
 | 
				
			||||||
 | 
					                    $results[] = [
 | 
				
			||||||
 | 
					                        'label' => $file,
 | 
				
			||||||
 | 
					                        'type' => 'directory',
 | 
				
			||||||
 | 
					                        'value' => $fileUrl,
 | 
				
			||||||
 | 
					                        'children' => $this->getDirectoryTree($filePath, $urlRoot . $file . '/')
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // If it's a file, add it to the list
 | 
				
			||||||
 | 
					                    $results[] = [
 | 
				
			||||||
 | 
					                        'label' => $file,
 | 
				
			||||||
 | 
					                        'type' => 'file',
 | 
				
			||||||
 | 
					                        'value' => $fileUrl
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function updateProfile(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $name = $request->input('name') ?? auth('admins')->user()->name;
 | 
				
			||||||
 | 
					        // Validate the incoming files
 | 
				
			||||||
 | 
					        $request->validate([
 | 
				
			||||||
 | 
					            'files.*' => 'required|file|mimes:jpg,png,jpeg,pdf,doc,docx|max:5120', // Adjust file types and size limit as needed
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $uploadedFiles = [];
 | 
				
			||||||
 | 
					        $baseDirectory = 'profiles/' . $name;
 | 
				
			||||||
 | 
					        $othersDirectory = $baseDirectory . '/others';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if the base directory exists, if not create it
 | 
				
			||||||
 | 
					        if (!Storage::disk('public')->exists($baseDirectory)) {
 | 
				
			||||||
 | 
					            Storage::disk('public')->makeDirectory($baseDirectory);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if the "others" directory exists, if not create it
 | 
				
			||||||
 | 
					        if (!Storage::disk('public')->exists($othersDirectory)) {
 | 
				
			||||||
 | 
					            Storage::disk('public')->makeDirectory($othersDirectory);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if ($request->hasFile('files')) {
 | 
				
			||||||
 | 
					            foreach ($request->file('files') as $file) {
 | 
				
			||||||
 | 
					                // Store the file and get its path
 | 
				
			||||||
 | 
					                $originalFilename = $file->getClientOriginalName();
 | 
				
			||||||
 | 
					                if (strpos($originalFilename, '__') === 0) {
 | 
				
			||||||
 | 
					                    // Store the file in the "others" directory
 | 
				
			||||||
 | 
					                    $path = $file->storeAs($othersDirectory, $originalFilename, 'public');
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // Store the file in the base directory
 | 
				
			||||||
 | 
					                    $path = $file->storeAs($baseDirectory, $originalFilename, 'public');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                $uploadedFiles[] = $path;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            'status' => true,
 | 
				
			||||||
 | 
					            'message' => 'Files uploaded successfully',
 | 
				
			||||||
 | 
					            'files' => $uploadedFiles,
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function removeFile(Request $request)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Validate that the file URL is provided in the request
 | 
				
			||||||
 | 
					        $request->validate([
 | 
				
			||||||
 | 
					            'file_url' => 'required|string',
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the full file URL from the request
 | 
				
			||||||
 | 
					        $fileUrl = $request->input('file_url');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Parse the file path from the URL (remove the base URL part)
 | 
				
			||||||
 | 
					        $storagePath = parse_url($fileUrl, PHP_URL_PATH); // Extract the path part of the URL
 | 
				
			||||||
 | 
					        $filePath = str_replace('/storage/', '', $storagePath); // Remove "/storage/" to get the actual file path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if the file exists before attempting to delete it
 | 
				
			||||||
 | 
					        if (Storage::disk('public')->exists($filePath)) {
 | 
				
			||||||
 | 
					            // Delete the file
 | 
				
			||||||
 | 
					            Storage::disk('public')->delete($filePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return response()->json([
 | 
				
			||||||
 | 
					                'status' => true,
 | 
				
			||||||
 | 
					                'message' => 'File deleted successfully',
 | 
				
			||||||
 | 
					            ]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response()->json([
 | 
				
			||||||
 | 
					            'status' => false,
 | 
				
			||||||
 | 
					            'message' => 'File not found',
 | 
				
			||||||
 | 
					        ], 404);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ Route::middleware('api')
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'timekeeping',
 | 
					                'prefix' => 'timekeeping',
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
                Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff');
 | 
					                Route::get('/', [TimekeepingController::class, 'get'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
                Route::post('/addMutilple', [TimekeepingController::class, 'addWorkingTimeForMultipleUser'])->middleware('check.permission:admin.hr');
 | 
					                Route::post('/addMutilple', [TimekeepingController::class, 'addWorkingTimeForMultipleUser'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
                Route::post('/addNote', [TimekeepingController::class, 'addNoteForUser'])->middleware('check.permission:admin.hr');
 | 
					                Route::post('/addNote', [TimekeepingController::class, 'addNoteForUser'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
                Route::get('/delete', [TimekeepingController::class, 'deleteNote'])->middleware('check.permission:admin.hr');
 | 
					                Route::get('/delete', [TimekeepingController::class, 'deleteNote'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
| 
						 | 
					@ -145,7 +145,7 @@ Route::middleware('api')
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'leave-management',
 | 
					                'prefix' => 'leave-management',
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
                Route::get('/', [LeaveManagementController::class, 'get'])->middleware('check.permission:admin.hr.staff');
 | 
					                Route::get('/', [LeaveManagementController::class, 'get'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
                Route::post('/saveNoteLeave', [LeaveManagementController::class, 'saveNoteLeave'])->middleware('check.permission:admin.hr');
 | 
					                Route::post('/saveNoteLeave', [LeaveManagementController::class, 'saveNoteLeave'])->middleware('check.permission:admin.hr');
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -159,6 +159,14 @@ Route::middleware('api')
 | 
				
			||||||
                Route::post('/handle-ticket', [TicketController::class, 'handleTicket'])->middleware('check.permission:admin');
 | 
					                Route::post('/handle-ticket', [TicketController::class, 'handleTicket'])->middleware('check.permission:admin');
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Route::group([
 | 
				
			||||||
 | 
					                'prefix' => 'profile',
 | 
				
			||||||
 | 
					            ], function () {
 | 
				
			||||||
 | 
					                Route::get('/all-files', [ProfileController::class, 'listFiles'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
 | 
					                Route::post('/update-profile', [ProfileController::class, 'updateProfile'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
 | 
					                Route::get('/delete-profile-file', [ProfileController::class, 'removeFile'])->middleware('check.permission:admin.hr.staff.accountant');
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Route::group([
 | 
					            Route::group([
 | 
				
			||||||
                'prefix' => 'criterias',
 | 
					                'prefix' => 'criterias',
 | 
				
			||||||
            ], function () {
 | 
					            ], function () {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ return [
 | 
				
			||||||
    |
 | 
					    |
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
 | 
					    'paths' => ['api/*', 'sanctum/csrf-cookie', 'storage/*'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'allowed_methods' => ['*'],
 | 
					    'allowed_methods' => ['*'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -11,19 +11,17 @@
 | 
				
			||||||
    "test": "npm run jest"
 | 
					    "test": "npm run jest"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@codemirror/lang-javascript": "^6.2.1",
 | 
					    "@codemirror/lang-javascript": "^6.2.2",
 | 
				
			||||||
    "@mantine/charts": "^7.4.1",
 | 
					    "@mantine/core": "^7.13.2",
 | 
				
			||||||
    "@mantine/core": "7.2.1",
 | 
					    "@mantine/dates": "^7.13.2",
 | 
				
			||||||
    "@mantine/dates": "^7.3.2",
 | 
					    "@mantine/form": "^7.13.2",
 | 
				
			||||||
    "@mantine/ds": "^7.2.2",
 | 
					    "@mantine/hooks": "^7.13.2",
 | 
				
			||||||
    "@mantine/form": "^7.2.2",
 | 
					    "@mantine/notifications": "^7.13.2",
 | 
				
			||||||
    "@mantine/hooks": "7.2.1",
 | 
					    "@mantine/vanilla-extract": "^7.13.2",
 | 
				
			||||||
    "@mantine/notifications": "^7.2.2",
 | 
					    "@reduxjs/toolkit": "^2.3.0",
 | 
				
			||||||
    "@mantine/vanilla-extract": "7.2.1",
 | 
					    "@tabler/icons-react": "^3.19.0",
 | 
				
			||||||
    "@reduxjs/toolkit": "^1.9.7",
 | 
					    "@uiw/react-codemirror": "^4.23.5",
 | 
				
			||||||
    "@tabler/icons-react": "^2.40.0",
 | 
					    "@vanilla-extract/css": "^1.16.0",
 | 
				
			||||||
    "@uiw/react-codemirror": "^4.21.21",
 | 
					 | 
				
			||||||
    "@vanilla-extract/css": "^1.13.0",
 | 
					 | 
				
			||||||
    "axios": "^1.6.1",
 | 
					    "axios": "^1.6.1",
 | 
				
			||||||
    "clsx": "^2.1.1",
 | 
					    "clsx": "^2.1.1",
 | 
				
			||||||
    "dayjs": "^1.11.10",
 | 
					    "dayjs": "^1.11.10",
 | 
				
			||||||
| 
						 | 
					@ -33,6 +31,7 @@
 | 
				
			||||||
    "jwt-decode": "^4.0.0",
 | 
					    "jwt-decode": "^4.0.0",
 | 
				
			||||||
    "moment": "^2.30.1",
 | 
					    "moment": "^2.30.1",
 | 
				
			||||||
    "react": "^18.2.0",
 | 
					    "react": "^18.2.0",
 | 
				
			||||||
 | 
					    "react-doc-viewer": "^0.1.14",
 | 
				
			||||||
    "react-dom": "^18.2.0",
 | 
					    "react-dom": "^18.2.0",
 | 
				
			||||||
    "react-redux": "^8.1.3",
 | 
					    "react-redux": "^8.1.3",
 | 
				
			||||||
    "react-router-dom": "^6.19.0",
 | 
					    "react-router-dom": "^6.19.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,6 +86,10 @@ export const getAllTechByUserId =
 | 
				
			||||||
export const evaluation = API_URL + 'v1/admin/evaluation/report'
 | 
					export const evaluation = API_URL + 'v1/admin/evaluation/report'
 | 
				
			||||||
export const sprintReview = API_URL + 'v1/admin/evaluation/sprint-review'
 | 
					export const sprintReview = API_URL + 'v1/admin/evaluation/sprint-review'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getAllFilesInProfiles = API_URL + 'v1/admin/profile/all-files'
 | 
				
			||||||
 | 
					export const updateProfileFolder = API_URL + 'v1/admin/profile/update-profile'
 | 
				
			||||||
 | 
					export const deleteFile = API_URL + 'v1/admin/profile/delete-profile-file'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//Technical
 | 
					//Technical
 | 
				
			||||||
export const listTechnical = API_URL + 'v1/admin/technical/get-all'
 | 
					export const listTechnical = API_URL + 'v1/admin/technical/get-all'
 | 
				
			||||||
export const createTechnical = API_URL + 'v1/admin/technical/create'
 | 
					export const createTechnical = API_URL + 'v1/admin/technical/create'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					import DocViewer, { DocViewerRenderers } from "react-doc-viewer";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function FilePreview() {
 | 
				
			||||||
 | 
					  const docs = [
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					    //   uri:
 | 
				
			||||||
 | 
					    //     "http://localhost:9000/uploads/ULRYB3ATJ56B/Screenshot%202021-04-28%20at%2014.04.23.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minio%2F20210507%2F%2Fs3%2Faws4_request&X-Amz-Date=20210507T142426Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host&X-Amz-Signature=761187860be22801088ab8c212733f7f52af8f62d638f1341ee2ae4c18944251"
 | 
				
			||||||
 | 
					    //   // "http://localhost:9000/uploads/6QK5HJ84MAEM/RAS-118_CompanyCodes__SalesOffices.xlsx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minio%2F20210507%2F%2Fs3%2Faws4_request&X-Amz-Date=20210507T110429Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host&X-Amz-Signature=c20f9b77ffdc1a15910cea5acd3420b6583a1d4d38ce5716da30f1d0ea4315d5"
 | 
				
			||||||
 | 
					    //   // "https://res.cloudinary.com/cloudinaryforme/image/upload/v1618339571/workplace-1245776_1920_i9ayae.jpg"
 | 
				
			||||||
 | 
					    // },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // {
 | 
				
			||||||
 | 
					    //   uri:
 | 
				
			||||||
 | 
					    //     "https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf"
 | 
				
			||||||
 | 
					    // },
 | 
				
			||||||
 | 
					    { uri: "https://s28.q4cdn.com/392171258/files/doc_downloads/test.pdf" },
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="App">
 | 
				
			||||||
 | 
					      <h1>Hello CodeSandbox</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <DocViewer pluginRenderers={DocViewerRenderers} documents={docs} />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,9 +0,0 @@
 | 
				
			||||||
// import { render } from 'tests'
 | 
					 | 
				
			||||||
// import Footer from './Footer'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// describe('Footer component', () => {
 | 
					 | 
				
			||||||
//   it('renders "Footer"', () => {
 | 
					 | 
				
			||||||
//     render(<Footer />)
 | 
					 | 
				
			||||||
//     // expect(getByRole('divider', { name: 'Login' })).toBeInTheDocument()
 | 
					 | 
				
			||||||
//   })
 | 
					 | 
				
			||||||
// })
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,107 +0,0 @@
 | 
				
			||||||
import { Text, Container, ActionIcon, Group, rem } from '@mantine/core'
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  IconBrandTwitter,
 | 
					 | 
				
			||||||
  IconBrandYoutube,
 | 
					 | 
				
			||||||
  IconBrandInstagram,
 | 
					 | 
				
			||||||
} from '@tabler/icons-react'
 | 
					 | 
				
			||||||
import { MantineLogo } from '@mantine/ds'
 | 
					 | 
				
			||||||
import * as classes from './FooterLinks.module.css.ts'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const data = [
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    title: 'About',
 | 
					 | 
				
			||||||
    links: [
 | 
					 | 
				
			||||||
      { label: 'Features', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Pricing', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Support', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Forums', link: '#' },
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    title: 'Project',
 | 
					 | 
				
			||||||
    links: [
 | 
					 | 
				
			||||||
      { label: 'Contribute', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Media assets', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Changelog', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Releases', link: '#' },
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    title: 'Community',
 | 
					 | 
				
			||||||
    links: [
 | 
					 | 
				
			||||||
      { label: 'Join Discord', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Follow on Twitter', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'Email newsletter', link: '#' },
 | 
					 | 
				
			||||||
      { label: 'GitHub discussions', link: '#' },
 | 
					 | 
				
			||||||
    ],
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
const Footer = () => {
 | 
					 | 
				
			||||||
  const groups = data.map((group) => {
 | 
					 | 
				
			||||||
    const links = group.links.map((link, index) => (
 | 
					 | 
				
			||||||
      <Text<'a'>
 | 
					 | 
				
			||||||
        key={index}
 | 
					 | 
				
			||||||
        className={classes.link}
 | 
					 | 
				
			||||||
        component="a"
 | 
					 | 
				
			||||||
        href={link.link}
 | 
					 | 
				
			||||||
        onClick={(event) => event.preventDefault()}
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        {link.label}
 | 
					 | 
				
			||||||
      </Text>
 | 
					 | 
				
			||||||
    ))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
      <div className={classes.wrapper} key={group.title}>
 | 
					 | 
				
			||||||
        <Text className={classes.title}>{group.title}</Text>
 | 
					 | 
				
			||||||
        {links}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <footer className={classes.footer}>
 | 
					 | 
				
			||||||
      <Container className={classes.inner}>
 | 
					 | 
				
			||||||
        <div className={classes.logo}>
 | 
					 | 
				
			||||||
          <MantineLogo size={30} />
 | 
					 | 
				
			||||||
          <Text size="xs" c="dimmed" className={classes.description}>
 | 
					 | 
				
			||||||
            Build fully functional accessible web applications faster than ever
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div className={classes.groups}>{groups}</div>
 | 
					 | 
				
			||||||
      </Container>
 | 
					 | 
				
			||||||
      <Container className={classes.afterFooter}>
 | 
					 | 
				
			||||||
        <Text c="dimmed" size="sm">
 | 
					 | 
				
			||||||
          © 2023 APACTECH.dev. All rights reserved.
 | 
					 | 
				
			||||||
        </Text>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <Group
 | 
					 | 
				
			||||||
          gap={0}
 | 
					 | 
				
			||||||
          className={classes.social}
 | 
					 | 
				
			||||||
          justify="flex-end"
 | 
					 | 
				
			||||||
          wrap="nowrap"
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <ActionIcon size="lg" color="gray" variant="subtle">
 | 
					 | 
				
			||||||
            <IconBrandTwitter
 | 
					 | 
				
			||||||
              style={{ width: rem(18), height: rem(18) }}
 | 
					 | 
				
			||||||
              stroke={1.5}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </ActionIcon>
 | 
					 | 
				
			||||||
          <ActionIcon size="lg" color="gray" variant="subtle">
 | 
					 | 
				
			||||||
            <IconBrandYoutube
 | 
					 | 
				
			||||||
              style={{ width: rem(18), height: rem(18) }}
 | 
					 | 
				
			||||||
              stroke={1.5}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </ActionIcon>
 | 
					 | 
				
			||||||
          <ActionIcon size="lg" color="gray" variant="subtle">
 | 
					 | 
				
			||||||
            <IconBrandInstagram
 | 
					 | 
				
			||||||
              style={{ width: rem(18), height: rem(18) }}
 | 
					 | 
				
			||||||
              stroke={1.5}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </ActionIcon>
 | 
					 | 
				
			||||||
        </Group>
 | 
					 | 
				
			||||||
      </Container>
 | 
					 | 
				
			||||||
    </footer>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Footer
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,104 +0,0 @@
 | 
				
			||||||
import { rem } from '@mantine/core'
 | 
					 | 
				
			||||||
import { vars } from '@/theme'
 | 
					 | 
				
			||||||
import { style } from '@vanilla-extract/css'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const footer = style({
 | 
					 | 
				
			||||||
  paddingTop: rem(vars.spacing.xl),
 | 
					 | 
				
			||||||
  paddingBottom: rem(vars.spacing.xl),
 | 
					 | 
				
			||||||
  backgroundColor: `light-dark(${vars.colors.gray[0]}, ${vars.colors.dark[6]})`,
 | 
					 | 
				
			||||||
  borderTop: `${rem(1)} solid light-dark(${vars.colors.gray[2]}, transparent)`,
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const logo = style({
 | 
					 | 
				
			||||||
  maxWidth: rem(200),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.sm})`]: {
 | 
					 | 
				
			||||||
      display: 'flex',
 | 
					 | 
				
			||||||
      flexDirection: 'column',
 | 
					 | 
				
			||||||
      alignItems: 'center',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const description = style({
 | 
					 | 
				
			||||||
  marginTop: rem(5),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.sm})`]: {
 | 
					 | 
				
			||||||
      marginTop: rem(vars.spacing.xs),
 | 
					 | 
				
			||||||
      textAlign: 'center',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const inner = style({
 | 
					 | 
				
			||||||
  display: 'flex',
 | 
					 | 
				
			||||||
  justifyContent: 'space-between',
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.sm})`]: {
 | 
					 | 
				
			||||||
      flexDirection: 'column',
 | 
					 | 
				
			||||||
      alignItems: 'center',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const groups = style({
 | 
					 | 
				
			||||||
  display: 'flex',
 | 
					 | 
				
			||||||
  flexWrap: 'wrap',
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.sm})`]: {
 | 
					 | 
				
			||||||
      display: 'none',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const wrapper = style({
 | 
					 | 
				
			||||||
  width: rem(160),
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const link = style({
 | 
					 | 
				
			||||||
  display: 'block',
 | 
					 | 
				
			||||||
  color: `light-dark(${vars.colors.gray[6]}, ${vars.colors.dark[1]})`,
 | 
					 | 
				
			||||||
  fontSize: vars.fontSizes.sm,
 | 
					 | 
				
			||||||
  paddingTop: rem(3),
 | 
					 | 
				
			||||||
  paddingBottom: rem(3),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ':hover': {
 | 
					 | 
				
			||||||
    textDecoration: 'underline',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const title = style({
 | 
					 | 
				
			||||||
  fontSize: vars.fontSizes.lg,
 | 
					 | 
				
			||||||
  fontWeight: 700,
 | 
					 | 
				
			||||||
  fontFamily: `Greycliff CF, ${vars.fontFamily}`,
 | 
					 | 
				
			||||||
  marginBottom: Number(vars.spacing.xs) / 2,
 | 
					 | 
				
			||||||
  color: `light-dark(${vars.colors.black}, ${vars.colors.white})`,
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const afterFooter = style({
 | 
					 | 
				
			||||||
  display: 'flex',
 | 
					 | 
				
			||||||
  justifyContent: 'space-between',
 | 
					 | 
				
			||||||
  alignItems: 'center',
 | 
					 | 
				
			||||||
  marginTop: vars.spacing.xl,
 | 
					 | 
				
			||||||
  paddingTop: vars.spacing.xl,
 | 
					 | 
				
			||||||
  paddingBottom: vars.spacing.xl,
 | 
					 | 
				
			||||||
  borderTop: `rem(1) solid light-dark(${vars.colors.gray[2]}, ${vars.colors.dark[4]})`,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.sm})`]: {
 | 
					 | 
				
			||||||
      flexDirection: 'column',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const social = style({
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.sm})`]: {
 | 
					 | 
				
			||||||
      marginTop: vars.spacing.xs,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,62 +0,0 @@
 | 
				
			||||||
import { style, styleVariants } from '@vanilla-extract/css'
 | 
					 | 
				
			||||||
import { vars } from '../../theme'
 | 
					 | 
				
			||||||
import { rem } from '@mantine/core'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const header = style({
 | 
					 | 
				
			||||||
  paddingTop: rem(vars.spacing.sm),
 | 
					 | 
				
			||||||
  backgroundColor: `light-dark(${vars.colors.gray[0]}, ${vars.colors.dark[6]})`,
 | 
					 | 
				
			||||||
  borderBottom: `${rem(1)} solid light-dark(${
 | 
					 | 
				
			||||||
    vars.colors.gray[2]
 | 
					 | 
				
			||||||
  }, transparent)`,
 | 
					 | 
				
			||||||
  marginBottom: rem(120),
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const mainSection = style({
 | 
					 | 
				
			||||||
  paddingBottom: rem(vars.spacing.sm),
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const user = style({
 | 
					 | 
				
			||||||
  color: `light-dark(${vars.colors.black}, ${vars.colors.dark[0]})`,
 | 
					 | 
				
			||||||
  padding: `${rem(vars.spacing.xs)} ${rem(vars.spacing.sm)}`,
 | 
					 | 
				
			||||||
  borderRadius: rem(vars.radius.sm),
 | 
					 | 
				
			||||||
  transition: 'background-color 100ms ease',
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ':hover': {
 | 
					 | 
				
			||||||
    backgroundColor: `light-dark(${vars.colors.white}, ${vars.colors.dark[8]})`,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  '@media': {
 | 
					 | 
				
			||||||
    [`(max-width: ${vars.breakpoints.xs})`]: {
 | 
					 | 
				
			||||||
      display: 'none',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const userActive = style({
 | 
					 | 
				
			||||||
  backgroundColor: `light-dark(${vars.colors.white}, ${vars.colors.dark[8]})`,
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const tabsList = style({
 | 
					 | 
				
			||||||
  '::before': {
 | 
					 | 
				
			||||||
    display: 'none',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const tab = styleVariants({
 | 
					 | 
				
			||||||
  default: {
 | 
					 | 
				
			||||||
    fontWeight: 500,
 | 
					 | 
				
			||||||
    height: rem(38),
 | 
					 | 
				
			||||||
    backgroundColor: 'transparent',
 | 
					 | 
				
			||||||
    position: 'relative',
 | 
					 | 
				
			||||||
    bottom: '-1px',
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ':hover': {
 | 
					 | 
				
			||||||
      backgroundColor: `light-dark(${vars.colors.gray[1]}, ${vars.colors.dark[5]})`,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  active: {
 | 
					 | 
				
			||||||
    backgroundColor: `light-dark(${vars.colors.white}, ${vars.colors.dark[7]})`,
 | 
					 | 
				
			||||||
    borderColor: `light-dark(${vars.colors.gray[2]}, ${vars.colors.dark[7]})`,
 | 
					 | 
				
			||||||
    borderBottomColor: 'transparent',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,11 +0,0 @@
 | 
				
			||||||
// import { render } from '@tests'
 | 
					 | 
				
			||||||
// import Header from './Header'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// describe('Header component', () => {
 | 
					 | 
				
			||||||
//   it('renders "Header"', () => {
 | 
					 | 
				
			||||||
//     render(<Header header={''} setHeader={function(): void {
 | 
					 | 
				
			||||||
//       throw new Error('Function not implemented.')
 | 
					 | 
				
			||||||
//     } } />)
 | 
					 | 
				
			||||||
//     // expect(getByRole('divider', { name: 'Login' })).toBeInTheDocument()
 | 
					 | 
				
			||||||
//   })
 | 
					 | 
				
			||||||
// })
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,210 +0,0 @@
 | 
				
			||||||
import { logout } from '@/rtk/dispatches/auth'
 | 
					 | 
				
			||||||
import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  Avatar,
 | 
					 | 
				
			||||||
  Burger,
 | 
					 | 
				
			||||||
  Container,
 | 
					 | 
				
			||||||
  Group,
 | 
					 | 
				
			||||||
  Menu,
 | 
					 | 
				
			||||||
  Tabs,
 | 
					 | 
				
			||||||
  Text,
 | 
					 | 
				
			||||||
  UnstyledButton,
 | 
					 | 
				
			||||||
  rem,
 | 
					 | 
				
			||||||
} from '@mantine/core'
 | 
					 | 
				
			||||||
import { MantineLogo } from '@mantine/ds'
 | 
					 | 
				
			||||||
import { useDisclosure } from '@mantine/hooks'
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  IconChevronDown,
 | 
					 | 
				
			||||||
  IconLogout,
 | 
					 | 
				
			||||||
  IconSettings,
 | 
					 | 
				
			||||||
  IconSwitchHorizontal,
 | 
					 | 
				
			||||||
} from '@tabler/icons-react'
 | 
					 | 
				
			||||||
import cx from 'clsx'
 | 
					 | 
				
			||||||
import { useCallback, useState } from 'react'
 | 
					 | 
				
			||||||
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle'
 | 
					 | 
				
			||||||
import LanguagePicker from '../LanguagePicker/LanguagePicker'
 | 
					 | 
				
			||||||
import * as classes from './DoubleHeader.module.css.ts'
 | 
					 | 
				
			||||||
import { useNavigate } from 'react-router-dom'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// const user = {
 | 
					 | 
				
			||||||
//   name: 'Jane Spoonfighter',
 | 
					 | 
				
			||||||
//   email: 'janspoon@fighter.dev',
 | 
					 | 
				
			||||||
//   image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-5.png',
 | 
					 | 
				
			||||||
// };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const tabs = ['Home', 'Page 1', 'Page 2', 'Page 3', 'Page 4', 'Page 5']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Header = ({
 | 
					 | 
				
			||||||
  header,
 | 
					 | 
				
			||||||
  setHeader,
 | 
					 | 
				
			||||||
}: {
 | 
					 | 
				
			||||||
  header: string
 | 
					 | 
				
			||||||
  setHeader: (newHeader: string) => void
 | 
					 | 
				
			||||||
}) => {
 | 
					 | 
				
			||||||
  const [opened, { toggle }] = useDisclosure(false)
 | 
					 | 
				
			||||||
  const [userMenuOpened, setUserMenuOpened] = useState(false)
 | 
					 | 
				
			||||||
  const dispatch = useAppDispatch()
 | 
					 | 
				
			||||||
  const navigate = useNavigate()
 | 
					 | 
				
			||||||
  const user = useAppSelector((state) => state.authentication.user.user)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const items = tabs.map((tab) => (
 | 
					 | 
				
			||||||
    <Tabs.Tab value={tab} key={tab} onClick={() => setHeader(tab)}>
 | 
					 | 
				
			||||||
      {tab}
 | 
					 | 
				
			||||||
    </Tabs.Tab>
 | 
					 | 
				
			||||||
  ))
 | 
					 | 
				
			||||||
  const handleLogout = useCallback(() => {
 | 
					 | 
				
			||||||
    dispatch(logout(navigate))
 | 
					 | 
				
			||||||
  }, [dispatch, navigate])
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <div className={classes.header}>
 | 
					 | 
				
			||||||
      <Container className={classes.mainSection} size="xl">
 | 
					 | 
				
			||||||
        <Group justify="space-between">
 | 
					 | 
				
			||||||
          <MantineLogo size={28} />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          <Burger opened={opened} onClick={toggle} hiddenFrom="xs" size="sm" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          <Group>
 | 
					 | 
				
			||||||
            <LanguagePicker />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <ColorSchemeToggle />
 | 
					 | 
				
			||||||
            <Menu
 | 
					 | 
				
			||||||
              width={260}
 | 
					 | 
				
			||||||
              position="bottom-end"
 | 
					 | 
				
			||||||
              transitionProps={{ transition: 'pop-top-right' }}
 | 
					 | 
				
			||||||
              onClose={() => setUserMenuOpened(false)}
 | 
					 | 
				
			||||||
              onOpen={() => setUserMenuOpened(true)}
 | 
					 | 
				
			||||||
              withinPortal
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
              <Menu.Target>
 | 
					 | 
				
			||||||
                <UnstyledButton
 | 
					 | 
				
			||||||
                  className={cx(classes.user, {
 | 
					 | 
				
			||||||
                    [classes.userActive]: userMenuOpened,
 | 
					 | 
				
			||||||
                  })}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  <Group gap={7}>
 | 
					 | 
				
			||||||
                    <Avatar
 | 
					 | 
				
			||||||
                      src={
 | 
					 | 
				
			||||||
                        'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-5.png'
 | 
					 | 
				
			||||||
                      }
 | 
					 | 
				
			||||||
                      alt={user.name}
 | 
					 | 
				
			||||||
                      radius="xl"
 | 
					 | 
				
			||||||
                      size={25}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                    <Text fw={500} size="sm" lh={1} mr={3}>
 | 
					 | 
				
			||||||
                      {user.name}
 | 
					 | 
				
			||||||
                    </Text>
 | 
					 | 
				
			||||||
                    <IconChevronDown
 | 
					 | 
				
			||||||
                      style={{ width: rem(12), height: rem(12) }}
 | 
					 | 
				
			||||||
                      stroke={1.5}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  </Group>
 | 
					 | 
				
			||||||
                </UnstyledButton>
 | 
					 | 
				
			||||||
              </Menu.Target>
 | 
					 | 
				
			||||||
              <Menu.Dropdown>
 | 
					 | 
				
			||||||
                {/* <Menu.Item
 | 
					 | 
				
			||||||
                leftSection={
 | 
					 | 
				
			||||||
                  <IconHeart
 | 
					 | 
				
			||||||
                    style={{ width: rem(16), height: rem(16) }}
 | 
					 | 
				
			||||||
                    color={theme.colors.red[6]}
 | 
					 | 
				
			||||||
                    stroke={1.5}
 | 
					 | 
				
			||||||
                  />
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                Liked posts
 | 
					 | 
				
			||||||
              </Menu.Item>
 | 
					 | 
				
			||||||
              <Menu.Item
 | 
					 | 
				
			||||||
                leftSection={
 | 
					 | 
				
			||||||
                  <IconStar
 | 
					 | 
				
			||||||
                    style={{ width: rem(16), height: rem(16) }}
 | 
					 | 
				
			||||||
                    color={theme.colors.yellow[6]}
 | 
					 | 
				
			||||||
                    stroke={1.5}
 | 
					 | 
				
			||||||
                  />
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                Saved posts
 | 
					 | 
				
			||||||
              </Menu.Item>
 | 
					 | 
				
			||||||
              <Menu.Item
 | 
					 | 
				
			||||||
                leftSection={
 | 
					 | 
				
			||||||
                  <IconMessage
 | 
					 | 
				
			||||||
                    style={{ width: rem(16), height: rem(16) }}
 | 
					 | 
				
			||||||
                    color={theme.colors.blue[6]}
 | 
					 | 
				
			||||||
                    stroke={1.5}
 | 
					 | 
				
			||||||
                  />
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                Your comments
 | 
					 | 
				
			||||||
              </Menu.Item> */}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <Menu.Label>Settings</Menu.Label>
 | 
					 | 
				
			||||||
                <Menu.Item
 | 
					 | 
				
			||||||
                  leftSection={
 | 
					 | 
				
			||||||
                    <IconSettings
 | 
					 | 
				
			||||||
                      style={{ width: rem(16), height: rem(16) }}
 | 
					 | 
				
			||||||
                      stroke={1.5}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  Account settings
 | 
					 | 
				
			||||||
                </Menu.Item>
 | 
					 | 
				
			||||||
                <Menu.Item
 | 
					 | 
				
			||||||
                  leftSection={
 | 
					 | 
				
			||||||
                    <IconSwitchHorizontal
 | 
					 | 
				
			||||||
                      style={{ width: rem(16), height: rem(16) }}
 | 
					 | 
				
			||||||
                      stroke={1.5}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  Change account
 | 
					 | 
				
			||||||
                </Menu.Item>
 | 
					 | 
				
			||||||
                <Menu.Item
 | 
					 | 
				
			||||||
                  leftSection={
 | 
					 | 
				
			||||||
                    <IconLogout
 | 
					 | 
				
			||||||
                      style={{ width: rem(16), height: rem(16) }}
 | 
					 | 
				
			||||||
                      stroke={1.5}
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                  }
 | 
					 | 
				
			||||||
                  onClick={handleLogout}
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                  Logout
 | 
					 | 
				
			||||||
                </Menu.Item>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                {/* <Menu.Divider />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              <Menu.Label>Danger zone</Menu.Label>
 | 
					 | 
				
			||||||
              <Menu.Item
 | 
					 | 
				
			||||||
                leftSection={
 | 
					 | 
				
			||||||
                  <IconPlayerPause style={{ width: rem(16), height: rem(16) }} stroke={1.5} />
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                Pause subscription
 | 
					 | 
				
			||||||
              </Menu.Item>
 | 
					 | 
				
			||||||
              <Menu.Item
 | 
					 | 
				
			||||||
                color="red"
 | 
					 | 
				
			||||||
                leftSection={<IconTrash style={{ width: rem(16), height: rem(16) }} stroke={1.5} />}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                Delete account
 | 
					 | 
				
			||||||
              </Menu.Item> */}
 | 
					 | 
				
			||||||
              </Menu.Dropdown>
 | 
					 | 
				
			||||||
            </Menu>
 | 
					 | 
				
			||||||
          </Group>
 | 
					 | 
				
			||||||
        </Group>
 | 
					 | 
				
			||||||
      </Container>
 | 
					 | 
				
			||||||
      <Container size="md">
 | 
					 | 
				
			||||||
        <Tabs
 | 
					 | 
				
			||||||
          defaultValue={header}
 | 
					 | 
				
			||||||
          variant="outline"
 | 
					 | 
				
			||||||
          visibleFrom="sm"
 | 
					 | 
				
			||||||
          classNames={{
 | 
					 | 
				
			||||||
            // root: classes.tab.active,
 | 
					 | 
				
			||||||
            list: classes.tabsList,
 | 
					 | 
				
			||||||
            tab: classes.tab.default,
 | 
					 | 
				
			||||||
          }}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <Tabs.List justify="center">{items}</Tabs.List>
 | 
					 | 
				
			||||||
        </Tabs>
 | 
					 | 
				
			||||||
      </Container>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Header
 | 
					 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import { logout } from '@/rtk/dispatches/auth'
 | 
				
			||||||
import { get, post } from '@/rtk/helpers/apiService'
 | 
					import { get, post } from '@/rtk/helpers/apiService'
 | 
				
			||||||
import { requirementsPassword } from '@/rtk/helpers/variables'
 | 
					import { requirementsPassword } from '@/rtk/helpers/variables'
 | 
				
			||||||
import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
 | 
					import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
 | 
				
			||||||
 | 
					import { checkPermissions } from '@/utils/checkRoles'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Avatar,
 | 
					  Avatar,
 | 
				
			||||||
  Box,
 | 
					  Box,
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,7 @@ import {
 | 
				
			||||||
  IconCalendarClock,
 | 
					  IconCalendarClock,
 | 
				
			||||||
  IconChartDots2,
 | 
					  IconChartDots2,
 | 
				
			||||||
  IconDevices,
 | 
					  IconDevices,
 | 
				
			||||||
 | 
					  IconFolders,
 | 
				
			||||||
  IconLayoutSidebarLeftExpand,
 | 
					  IconLayoutSidebarLeftExpand,
 | 
				
			||||||
  IconLayoutSidebarRightExpand,
 | 
					  IconLayoutSidebarRightExpand,
 | 
				
			||||||
  IconListCheck,
 | 
					  IconListCheck,
 | 
				
			||||||
| 
						 | 
					@ -39,13 +41,12 @@ import {
 | 
				
			||||||
  IconSun,
 | 
					  IconSun,
 | 
				
			||||||
  IconTicket,
 | 
					  IconTicket,
 | 
				
			||||||
  IconUsersGroup,
 | 
					  IconUsersGroup,
 | 
				
			||||||
  IconZoomExclamation,
 | 
					  IconZoomExclamation
 | 
				
			||||||
} from '@tabler/icons-react'
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
import { useCallback, useEffect, useState } from 'react'
 | 
					import { useCallback, useEffect, useState } from 'react'
 | 
				
			||||||
import { useLocation, useNavigate } from 'react-router-dom'
 | 
					import { useLocation, useNavigate } from 'react-router-dom'
 | 
				
			||||||
import PasswordRequirementInput from '../PasswordRequirementInput/PasswordRequirementInput'
 | 
					import PasswordRequirementInput from '../PasswordRequirementInput/PasswordRequirementInput'
 | 
				
			||||||
import classes from './NavbarSimpleColored.module.css'
 | 
					import classes from './NavbarSimpleColored.module.css'
 | 
				
			||||||
import { checkPermissions } from '@/utils/checkRoles'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const data = [
 | 
					const data = [
 | 
				
			||||||
  // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
 | 
					  // { link: '/dashboard', label: 'Dashboard', icon: IconHome },
 | 
				
			||||||
| 
						 | 
					@ -53,7 +54,7 @@ const data = [
 | 
				
			||||||
    link: '/timekeeping',
 | 
					    link: '/timekeeping',
 | 
				
			||||||
    label: 'Timekeeping',
 | 
					    label: 'Timekeeping',
 | 
				
			||||||
    icon: IconCalendar,
 | 
					    icon: IconCalendar,
 | 
				
			||||||
    permissions: 'admin,hr,staff,tester',
 | 
					    permissions: 'admin,hr,staff,tester,accountant',
 | 
				
			||||||
    group: 'staff',
 | 
					    group: 'staff',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
| 
						 | 
					@ -74,7 +75,7 @@ const data = [
 | 
				
			||||||
    link: '/leave-management',
 | 
					    link: '/leave-management',
 | 
				
			||||||
    label: 'Leave Management',
 | 
					    label: 'Leave Management',
 | 
				
			||||||
    icon: IconCalendarClock,
 | 
					    icon: IconCalendarClock,
 | 
				
			||||||
    permissions: 'admin,hr,staff,tester',
 | 
					    permissions: 'admin,hr,staff,tester,accountant',
 | 
				
			||||||
    group: 'staff',
 | 
					    group: 'staff',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
| 
						 | 
					@ -140,6 +141,13 @@ const data = [
 | 
				
			||||||
    group: 'admin',
 | 
					    group: 'admin',
 | 
				
			||||||
    permissions: 'admin',
 | 
					    permissions: 'admin',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    link: '/profiles',
 | 
				
			||||||
 | 
					    label: 'Profiles',
 | 
				
			||||||
 | 
					    icon: IconFolders,
 | 
				
			||||||
 | 
					    group: 'admin',
 | 
				
			||||||
 | 
					    permissions: 'admin,accountant',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  // { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
					  // { link: '/jira', label: 'Jira', icon: IconSubtask },
 | 
				
			||||||
  // { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
 | 
					  // { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
 | 
				
			||||||
  // { link: '/general-setting', label: 'General Setting', icon: IconSettings },
 | 
					  // { link: '/general-setting', label: 'General Setting', icon: IconSettings },
 | 
				
			||||||
| 
						 | 
					@ -231,7 +239,7 @@ const Navbar = ({
 | 
				
			||||||
  // })
 | 
					  // })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const group = [
 | 
					  const group = [
 | 
				
			||||||
    { name: 'staff', label: 'General', permissions: 'admin,hr,staff,tester' },
 | 
					    { name: 'staff', label: 'General', permissions: 'admin,hr,staff,tester,accountant' },
 | 
				
			||||||
    { name: 'admin', label: 'Admin', permissions: 'admin' },
 | 
					    { name: 'admin', label: 'Admin', permissions: 'admin' },
 | 
				
			||||||
    { name: 'other', label: 'Other', permissions: 'admin,hr' },
 | 
					    { name: 'other', label: 'Other', permissions: 'admin,hr' },
 | 
				
			||||||
    { name: 'test', label: 'Test', permissions: 'admin,tester' },
 | 
					    { name: 'test', label: 'Test', permissions: 'admin,tester' },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					.root {
 | 
				
			||||||
 | 
					    /* font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; */
 | 
				
			||||||
 | 
					    font-size: 14px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.label[data-hovered] {
 | 
				
			||||||
 | 
					  background-color: var(--mantine-color-gray-0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/* Add the light and dark logic using classes, for example */
 | 
				
			||||||
 | 
					.label[data-hovered].light {
 | 
				
			||||||
 | 
					  background-color: var(--mantine-color-gray-0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.label[data-hovered].dark {
 | 
				
			||||||
 | 
					  background-color: var(--mantine-color-dark-6);
 | 
				
			||||||
 | 
					  color: var(--mantine-color-white);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.label[data-selected] {
 | 
				
			||||||
 | 
					  font-weight: 700;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.label[data-selected].light {
 | 
				
			||||||
 | 
					  background-color: var(--mantine-color-blue-0);
 | 
				
			||||||
 | 
					  color: var(--mantine-color-black);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.label[data-selected].dark {
 | 
				
			||||||
 | 
					  background-color: rgba(var(--mantine-color-blue-8), 0.35);
 | 
				
			||||||
 | 
					  color: var(--mantine-color-blue-0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,504 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  deleteFile,
 | 
				
			||||||
 | 
					  getAllFilesInProfiles,
 | 
				
			||||||
 | 
					  updateProfileFolder,
 | 
				
			||||||
 | 
					} from '@/api/Admin'
 | 
				
			||||||
 | 
					import { Xdelete } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
 | 
					import { get } from '@/rtk/helpers/apiService'
 | 
				
			||||||
 | 
					import { getAccessToken } from '@/rtk/localStorage'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Box,
 | 
				
			||||||
 | 
					  Button,
 | 
				
			||||||
 | 
					  FileInput,
 | 
				
			||||||
 | 
					  Group,
 | 
				
			||||||
 | 
					  Modal,
 | 
				
			||||||
 | 
					  RenderTreeNodePayload,
 | 
				
			||||||
 | 
					  Stack,
 | 
				
			||||||
 | 
					  Text,
 | 
				
			||||||
 | 
					  TextInput,
 | 
				
			||||||
 | 
					  Tooltip,
 | 
				
			||||||
 | 
					  Tree,
 | 
				
			||||||
 | 
					} from '@mantine/core'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  IconFileTypeDocx,
 | 
				
			||||||
 | 
					  IconFileTypePdf,
 | 
				
			||||||
 | 
					  IconFolder,
 | 
				
			||||||
 | 
					  IconFolderOpen,
 | 
				
			||||||
 | 
					  IconFolderX,
 | 
				
			||||||
 | 
					  IconListCheck,
 | 
				
			||||||
 | 
					  IconPhoto,
 | 
				
			||||||
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
 | 
					import axios from 'axios'
 | 
				
			||||||
 | 
					import { useEffect, useState } from 'react'
 | 
				
			||||||
 | 
					import classes from './AllProfiles.module.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface FileIconProps {
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  isFolder: boolean
 | 
				
			||||||
 | 
					  expanded: boolean
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TFileProfile = {
 | 
				
			||||||
 | 
					  label: string
 | 
				
			||||||
 | 
					  type: string
 | 
				
			||||||
 | 
					  value: string
 | 
				
			||||||
 | 
					  children?: TFileProfile[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AllProfiles = () => {
 | 
				
			||||||
 | 
					  const [treeData, setTreeData] = useState([])
 | 
				
			||||||
 | 
					  const [cv, setCv] = useState<File>()
 | 
				
			||||||
 | 
					  const [idCard, setIdCard] = useState<File>()
 | 
				
			||||||
 | 
					  const [transcript, setTranscript] = useState<File>()
 | 
				
			||||||
 | 
					  const [universityDiploma, setUniversityDiploma] = useState<File>()
 | 
				
			||||||
 | 
					  const [otherFiles, setOtherFiles] = useState([{ file: null, type: '' }])
 | 
				
			||||||
 | 
					  const [data, setData] = useState<TFileProfile[]>([])
 | 
				
			||||||
 | 
					  const [currentName, setCurrentName] = useState<string>('')
 | 
				
			||||||
 | 
					  const [openedProfile, setOpenedProfile] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function FileIcon({ name, isFolder, expanded }: FileIconProps) {
 | 
				
			||||||
 | 
					    if (name.endsWith('.pdf')) {
 | 
				
			||||||
 | 
					      return <IconFileTypePdf size={14} />
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (name.endsWith('.doc') || name.endsWith('.docx')) {
 | 
				
			||||||
 | 
					      return <IconFileTypeDocx size={14} />
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      name.endsWith('.jpg') ||
 | 
				
			||||||
 | 
					      name.endsWith('.png') ||
 | 
				
			||||||
 | 
					      name.endsWith('.jpeg') ||
 | 
				
			||||||
 | 
					      name.endsWith('.webp')
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      return <IconPhoto size={14} />
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isFolder) {
 | 
				
			||||||
 | 
					      return expanded ? (
 | 
				
			||||||
 | 
					        <IconFolderOpen
 | 
				
			||||||
 | 
					          color="var(--mantine-color-yellow-9)"
 | 
				
			||||||
 | 
					          size={14}
 | 
				
			||||||
 | 
					          stroke={2.5}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      ) : (
 | 
				
			||||||
 | 
					        <IconFolder
 | 
				
			||||||
 | 
					          color="var(--mantine-color-yellow-9)"
 | 
				
			||||||
 | 
					          size={14}
 | 
				
			||||||
 | 
					          stroke={2.5}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <IconFolderX color="var(--mantine-color-red-9)" size={14} stroke={2.5} />
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function Leaf({
 | 
				
			||||||
 | 
					    node,
 | 
				
			||||||
 | 
					    expanded,
 | 
				
			||||||
 | 
					    hasChildren,
 | 
				
			||||||
 | 
					    elementProps,
 | 
				
			||||||
 | 
					  }: RenderTreeNodePayload) {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Group gap={5} {...elementProps}>
 | 
				
			||||||
 | 
					        {!node.children ? (
 | 
				
			||||||
 | 
					          <a href={node.value} target="_blank">
 | 
				
			||||||
 | 
					            <FileIcon
 | 
				
			||||||
 | 
					              name={node.value}
 | 
				
			||||||
 | 
					              isFolder={hasChildren}
 | 
				
			||||||
 | 
					              expanded={expanded}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <span>{node.label}</span>
 | 
				
			||||||
 | 
					          </a>
 | 
				
			||||||
 | 
					        ) : (
 | 
				
			||||||
 | 
					          <>
 | 
				
			||||||
 | 
					            <FileIcon
 | 
				
			||||||
 | 
					              name={node.value}
 | 
				
			||||||
 | 
					              isFolder={hasChildren}
 | 
				
			||||||
 | 
					              expanded={expanded}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <span>{node.label}</span>
 | 
				
			||||||
 | 
					            <Tooltip label="Upload">
 | 
				
			||||||
 | 
					              <IconListCheck
 | 
				
			||||||
 | 
					                color="green"
 | 
				
			||||||
 | 
					                width={15}
 | 
				
			||||||
 | 
					                height={15}
 | 
				
			||||||
 | 
					                style={{
 | 
				
			||||||
 | 
					                  display: node.label !== 'others' ? 'block' : 'none',
 | 
				
			||||||
 | 
					                  cursor: 'pointer',
 | 
				
			||||||
 | 
					                  zIndex: 100000,
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					                onClick={() => {
 | 
				
			||||||
 | 
					                  setCurrentName(node.label!.toString())
 | 
				
			||||||
 | 
					                  setOpenedProfile(true)
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            </Tooltip>
 | 
				
			||||||
 | 
					          </>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </Group>
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleOtherFileChange = (
 | 
				
			||||||
 | 
					    index: number,
 | 
				
			||||||
 | 
					    field: string,
 | 
				
			||||||
 | 
					    value: File | string,
 | 
				
			||||||
 | 
					  ) => {
 | 
				
			||||||
 | 
					    const updatedFiles: any = [...otherFiles]
 | 
				
			||||||
 | 
					    updatedFiles[index][field] = value
 | 
				
			||||||
 | 
					    setOtherFiles(updatedFiles)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const addOtherFileInput = () => {
 | 
				
			||||||
 | 
					    setOtherFiles([...otherFiles, { file: null, type: '' }])
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSubmit = async (e: any) => {
 | 
				
			||||||
 | 
					    e.preventDefault()
 | 
				
			||||||
 | 
					    const formData = new FormData()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Append each selected file to FormData
 | 
				
			||||||
 | 
					    for (let i = 0; i < otherFiles.length; i++) {
 | 
				
			||||||
 | 
					      if (otherFiles[i].file !== null && otherFiles[i].type !== '') {
 | 
				
			||||||
 | 
					        formData.append(
 | 
				
			||||||
 | 
					          'files[]',
 | 
				
			||||||
 | 
					          handleChangeFileName(otherFiles[i].file!, `__${otherFiles[i].type}`)!,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (cv) {
 | 
				
			||||||
 | 
					      formData.append('files[]', cv)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (idCard) {
 | 
				
			||||||
 | 
					      formData.append('files[]', idCard)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (transcript) {
 | 
				
			||||||
 | 
					      formData.append('files[]', transcript)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (universityDiploma) {
 | 
				
			||||||
 | 
					      formData.append('files[]', universityDiploma)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    formData.append('name', currentName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const token = await getAccessToken()
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const response = await axios.post(updateProfileFolder, formData, {
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					          'Content-Type': 'multipart/form-data',
 | 
				
			||||||
 | 
					          Authorization: `Bearer ${token}`,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (response.status === 200) {
 | 
				
			||||||
 | 
					        getAllFile()
 | 
				
			||||||
 | 
					        getTree()
 | 
				
			||||||
 | 
					        setOtherFiles([])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('Error uploading files', error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getAllFile = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await get(getAllFilesInProfiles, {
 | 
				
			||||||
 | 
					        root_folder: '/storage/profiles/' + currentName,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.status === true) {
 | 
				
			||||||
 | 
					        setData(res.data)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const removeFile = async (url: string) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await Xdelete(deleteFile, { file_url: url }, getAllFile)
 | 
				
			||||||
 | 
					      getTree()
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  const getTree = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await get(getAllFilesInProfiles, {
 | 
				
			||||||
 | 
					        root_folder: '/storage/profiles',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.status === true) {
 | 
				
			||||||
 | 
					        setTreeData(res.data)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleChangeFileName = (e: File, newName: string) => {
 | 
				
			||||||
 | 
					    const originalFile = e // Get the original file
 | 
				
			||||||
 | 
					    const extend = originalFile.name.split('.')[1]
 | 
				
			||||||
 | 
					    if (originalFile) {
 | 
				
			||||||
 | 
					      const newFileName = `${newName}.${extend}` // Create new file name
 | 
				
			||||||
 | 
					      const newFile = new File([originalFile], newFileName, {
 | 
				
			||||||
 | 
					        type: originalFile.type,
 | 
				
			||||||
 | 
					      }) // Create new file object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return newFile // Save the new file object for further processing
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const checkFileExist = (nameField: string) => {
 | 
				
			||||||
 | 
					    const file = data.find((f) => f.label.includes(nameField))
 | 
				
			||||||
 | 
					    return file
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getTree()
 | 
				
			||||||
 | 
					  }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    getAllFile()
 | 
				
			||||||
 | 
					  }, [currentName])
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <div className={classes.title}>
 | 
				
			||||||
 | 
					        <h3>
 | 
				
			||||||
 | 
					          <Text>Admin/</Text>
 | 
				
			||||||
 | 
					          Profiles
 | 
				
			||||||
 | 
					        </h3>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <Box ml={'lg'}>
 | 
				
			||||||
 | 
					        <Tree
 | 
				
			||||||
 | 
					          classNames={classes}
 | 
				
			||||||
 | 
					          selectOnClick
 | 
				
			||||||
 | 
					          clearSelectionOnOutsideClick
 | 
				
			||||||
 | 
					          data={treeData}
 | 
				
			||||||
 | 
					          renderNode={(payload) => <Leaf {...payload} />}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					        <Modal
 | 
				
			||||||
 | 
					          size={'lg'}
 | 
				
			||||||
 | 
					          opened={openedProfile}
 | 
				
			||||||
 | 
					          onClose={() => {
 | 
				
			||||||
 | 
					            setOpenedProfile(false)
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Box>
 | 
				
			||||||
 | 
					            <form onSubmit={handleSubmit}>
 | 
				
			||||||
 | 
					              <Stack>
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('cv') ? 'flex' : 'none' }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>CV</Text>
 | 
				
			||||||
 | 
					                  <a href={checkFileExist('cv')?.value} target="_blank">
 | 
				
			||||||
 | 
					                    <Text>{`: ${checkFileExist('cv')?.label}`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button
 | 
				
			||||||
 | 
					                    variant="outline"
 | 
				
			||||||
 | 
					                    size="xs"
 | 
				
			||||||
 | 
					                    color="red"
 | 
				
			||||||
 | 
					                    ml={'sm'}
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      removeFile(checkFileExist('cv')?.value!)
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label={'CV'}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên CV"
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('cv') ? 'none' : 'block' }}
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    0
 | 
				
			||||||
 | 
					                    setCv(handleChangeFileName(e!, 'cv'))
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".pdf,.doc,.docx"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('idCard') ? 'flex' : 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>CCCD</Text>
 | 
				
			||||||
 | 
					                  <a href={checkFileExist('idCard')?.value} target="_blank">
 | 
				
			||||||
 | 
					                    <Text>{`: ${checkFileExist('idCard')?.label}`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button
 | 
				
			||||||
 | 
					                    variant="outline"
 | 
				
			||||||
 | 
					                    size="xs"
 | 
				
			||||||
 | 
					                    color="red"
 | 
				
			||||||
 | 
					                    ml={'sm'}
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      removeFile(checkFileExist('idCard')?.value!)
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label="CCCD"
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('idCard') ? 'none' : 'block',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên CCCD"
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    setIdCard(handleChangeFileName(e!, 'idCard'))
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".jpg,.jpeg,.png,.pdf"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('transcript') ? 'flex' : 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>Bảng điểm</Text>
 | 
				
			||||||
 | 
					                  <a href={checkFileExist('transcript')?.value} target="_blank">
 | 
				
			||||||
 | 
					                    <Text>{`: ${checkFileExist('transcript')?.label}`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button
 | 
				
			||||||
 | 
					                    variant="outline"
 | 
				
			||||||
 | 
					                    size="xs"
 | 
				
			||||||
 | 
					                    color="red"
 | 
				
			||||||
 | 
					                    ml={'sm'}
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      removeFile(checkFileExist('transcript')?.value!)
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label="Bảng điểm"
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('transcript') ? 'none' : 'block',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên bảng điểm"
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    setTranscript(handleChangeFileName(e!, 'transcript'))
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".pdf"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('universityDiploma')
 | 
				
			||||||
 | 
					                      ? 'flex'
 | 
				
			||||||
 | 
					                      : 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>Bằng đại học</Text>
 | 
				
			||||||
 | 
					                  <a
 | 
				
			||||||
 | 
					                    href={checkFileExist('universityDiploma')?.value}
 | 
				
			||||||
 | 
					                    target="_blank"
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <Text>{`: ${
 | 
				
			||||||
 | 
					                      checkFileExist('universityDiploma')?.label
 | 
				
			||||||
 | 
					                    }`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button
 | 
				
			||||||
 | 
					                    variant="outline"
 | 
				
			||||||
 | 
					                    size="xs"
 | 
				
			||||||
 | 
					                    color="red"
 | 
				
			||||||
 | 
					                    ml={'sm'}
 | 
				
			||||||
 | 
					                    onClick={() => {
 | 
				
			||||||
 | 
					                      removeFile(checkFileExist('universityDiploma')?.value!)
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label="Bằng đại học"
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('universityDiploma')
 | 
				
			||||||
 | 
					                      ? 'none'
 | 
				
			||||||
 | 
					                      : 'block',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên bằng đại học"
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    setUniversityDiploma(
 | 
				
			||||||
 | 
					                      handleChangeFileName(e!, 'universityDiploma'),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".pdf,.jpg,.jpeg,.png"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Text>Danh sách file khác:</Text>
 | 
				
			||||||
 | 
					                <Box>
 | 
				
			||||||
 | 
					                  {data
 | 
				
			||||||
 | 
					                    .find((f) => f.label === 'others')
 | 
				
			||||||
 | 
					                    ?.children?.map((c, index) => {
 | 
				
			||||||
 | 
					                      return (
 | 
				
			||||||
 | 
					                        <Box
 | 
				
			||||||
 | 
					                          key={index}
 | 
				
			||||||
 | 
					                          style={{
 | 
				
			||||||
 | 
					                            display: 'flex',
 | 
				
			||||||
 | 
					                            marginBottom: '5px',
 | 
				
			||||||
 | 
					                          }}
 | 
				
			||||||
 | 
					                        >
 | 
				
			||||||
 | 
					                          <a href={c?.value} target="_blank">
 | 
				
			||||||
 | 
					                            <Text>{`${c?.label}`}</Text>
 | 
				
			||||||
 | 
					                          </a>
 | 
				
			||||||
 | 
					                          <Button
 | 
				
			||||||
 | 
					                            variant="outline"
 | 
				
			||||||
 | 
					                            size="xs"
 | 
				
			||||||
 | 
					                            color="red"
 | 
				
			||||||
 | 
					                            ml={'sm'}
 | 
				
			||||||
 | 
					                            onClick={() => {
 | 
				
			||||||
 | 
					                              removeFile(c?.value!)
 | 
				
			||||||
 | 
					                            }}
 | 
				
			||||||
 | 
					                          >
 | 
				
			||||||
 | 
					                            Delete
 | 
				
			||||||
 | 
					                          </Button>
 | 
				
			||||||
 | 
					                        </Box>
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                    })}
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                {otherFiles.map((fileInput, index) => (
 | 
				
			||||||
 | 
					                  <Group key={index}>
 | 
				
			||||||
 | 
					                    <FileInput
 | 
				
			||||||
 | 
					                      placeholder="Chọn file"
 | 
				
			||||||
 | 
					                      onChange={(file) =>
 | 
				
			||||||
 | 
					                        handleOtherFileChange(index, 'file', file!)
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                      w={'30%'}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                    <TextInput
 | 
				
			||||||
 | 
					                      placeholder="Nhập tên file (không cần phần mở rộng)"
 | 
				
			||||||
 | 
					                      value={fileInput.type}
 | 
				
			||||||
 | 
					                      w={'65%'}
 | 
				
			||||||
 | 
					                      onChange={(e) =>
 | 
				
			||||||
 | 
					                        handleOtherFileChange(
 | 
				
			||||||
 | 
					                          index,
 | 
				
			||||||
 | 
					                          'type',
 | 
				
			||||||
 | 
					                          e.currentTarget.value,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </Group>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					                <Button type="button" onClick={addOtherFileInput}>
 | 
				
			||||||
 | 
					                  Thêm file khác
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Button type="submit" color="blue">
 | 
				
			||||||
 | 
					                  Save
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Stack>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Modal>
 | 
				
			||||||
 | 
					      </Box>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default AllProfiles
 | 
				
			||||||
| 
						 | 
					@ -1,26 +1,33 @@
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
 | 
					  deleteFile,
 | 
				
			||||||
 | 
					  getAllFilesInProfiles,
 | 
				
			||||||
  getProfilesData,
 | 
					  getProfilesData,
 | 
				
			||||||
  listUserTechnical,
 | 
					  listUserTechnical,
 | 
				
			||||||
 | 
					  updateProfileFolder,
 | 
				
			||||||
  updateProfilesData,
 | 
					  updateProfilesData,
 | 
				
			||||||
  updateUserTechnical,
 | 
					  updateUserTechnical,
 | 
				
			||||||
} from '@/api/Admin'
 | 
					} from '@/api/Admin'
 | 
				
			||||||
import { changePassword } from '@/api/Auth'
 | 
					import { changePassword } from '@/api/Auth'
 | 
				
			||||||
 | 
					import DataTableAll from '@/components/DataTable/DataTable'
 | 
				
			||||||
import PasswordRequirementInput from '@/components/PasswordRequirementInput/PasswordRequirementInput'
 | 
					import PasswordRequirementInput from '@/components/PasswordRequirementInput/PasswordRequirementInput'
 | 
				
			||||||
import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
 | 
					import ProjectInvolvement from '@/components/ProjectInvolvement/ProjectInvolvement'
 | 
				
			||||||
import { logout } from '@/rtk/dispatches/auth'
 | 
					import { logout } from '@/rtk/dispatches/auth'
 | 
				
			||||||
import { get, post, postImage } from '@/rtk/helpers/apiService'
 | 
					import { get, post, postImage } from '@/rtk/helpers/apiService'
 | 
				
			||||||
import { requirementsPassword } from '@/rtk/helpers/variables'
 | 
					import { requirementsPassword } from '@/rtk/helpers/variables'
 | 
				
			||||||
import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
 | 
					import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
 | 
				
			||||||
import { getUser } from '@/rtk/localStorage'
 | 
					import { getAccessToken, getUser } from '@/rtk/localStorage'
 | 
				
			||||||
import { success } from '@/rtk/slices/auth'
 | 
					import { success } from '@/rtk/slices/auth'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Avatar,
 | 
					  Avatar,
 | 
				
			||||||
  Box,
 | 
					  Box,
 | 
				
			||||||
  Button,
 | 
					  Button,
 | 
				
			||||||
 | 
					  FileInput,
 | 
				
			||||||
  Flex,
 | 
					  Flex,
 | 
				
			||||||
 | 
					  Group,
 | 
				
			||||||
  Loader,
 | 
					  Loader,
 | 
				
			||||||
  Modal,
 | 
					  Modal,
 | 
				
			||||||
  PasswordInput,
 | 
					  PasswordInput,
 | 
				
			||||||
 | 
					  Stack,
 | 
				
			||||||
  Text,
 | 
					  Text,
 | 
				
			||||||
  TextInput,
 | 
					  TextInput,
 | 
				
			||||||
  Title,
 | 
					  Title,
 | 
				
			||||||
| 
						 | 
					@ -28,18 +35,26 @@ import {
 | 
				
			||||||
import { notifications } from '@mantine/notifications'
 | 
					import { notifications } from '@mantine/notifications'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  IconExchange,
 | 
					  IconExchange,
 | 
				
			||||||
 | 
					  IconFolder,
 | 
				
			||||||
  IconPasswordUser,
 | 
					  IconPasswordUser,
 | 
				
			||||||
  IconUserCode,
 | 
					  IconUserCode,
 | 
				
			||||||
  IconUserCog,
 | 
					  IconUserCog,
 | 
				
			||||||
} from '@tabler/icons-react'
 | 
					} from '@tabler/icons-react'
 | 
				
			||||||
 | 
					import axios from 'axios'
 | 
				
			||||||
 | 
					import moment from 'moment'
 | 
				
			||||||
import { useCallback, useEffect, useState } from 'react'
 | 
					import { useCallback, useEffect, useState } from 'react'
 | 
				
			||||||
import { useNavigate } from 'react-router-dom'
 | 
					import { useNavigate } from 'react-router-dom'
 | 
				
			||||||
import classes from './Profile.module.css'
 | 
					import classes from './Profile.module.css'
 | 
				
			||||||
import DataTableAll from '@/components/DataTable/DataTable'
 | 
					import { Xdelete } from '@/rtk/helpers/CRUD'
 | 
				
			||||||
import moment from 'moment'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isCompactMenu = false
 | 
					const isCompactMenu = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TFileProfile = {
 | 
				
			||||||
 | 
					  label: string
 | 
				
			||||||
 | 
					  type: string
 | 
				
			||||||
 | 
					  value: string
 | 
				
			||||||
 | 
					  children?: TFileProfile[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
const Profile = () => {
 | 
					const Profile = () => {
 | 
				
			||||||
  const user = useAppSelector((state) => state.authentication.user)
 | 
					  const user = useAppSelector((state) => state.authentication.user)
 | 
				
			||||||
  const userData = getUser()
 | 
					  const userData = getUser()
 | 
				
			||||||
| 
						 | 
					@ -122,12 +137,102 @@ const Profile = () => {
 | 
				
			||||||
    return []
 | 
					    return []
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const [cv, setCv] = useState<File>()
 | 
				
			||||||
 | 
					  const [idCard, setIdCard] = useState<File>()
 | 
				
			||||||
 | 
					  const [transcript, setTranscript] = useState<File>()
 | 
				
			||||||
 | 
					  const [universityDiploma, setUniversityDiploma] = useState<File>()
 | 
				
			||||||
 | 
					  const [otherFiles, setOtherFiles] = useState([{ file: null, type: '' }])
 | 
				
			||||||
 | 
					  const [data, setData] = useState<TFileProfile[]>([])
 | 
				
			||||||
 | 
					  const [openedProfile, setOpenedProfile] = useState(false)
 | 
				
			||||||
 | 
					  const handleOtherFileChange = (
 | 
				
			||||||
 | 
					    index: number,
 | 
				
			||||||
 | 
					    field: string,
 | 
				
			||||||
 | 
					    value: File | string,
 | 
				
			||||||
 | 
					  ) => {
 | 
				
			||||||
 | 
					    const updatedFiles: any = [...otherFiles]
 | 
				
			||||||
 | 
					    updatedFiles[index][field] = value
 | 
				
			||||||
 | 
					    setOtherFiles(updatedFiles)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const addOtherFileInput = () => {
 | 
				
			||||||
 | 
					    setOtherFiles([...otherFiles, { file: null, type: '' }])
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleSubmit = async (e: any) => {
 | 
				
			||||||
 | 
					    e.preventDefault()
 | 
				
			||||||
 | 
					    const formData = new FormData()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Append each selected file to FormData
 | 
				
			||||||
 | 
					    for (let i = 0; i < otherFiles.length; i++) {
 | 
				
			||||||
 | 
					      if (otherFiles[i].file !== null && otherFiles[i].type !== '') {
 | 
				
			||||||
 | 
					        formData.append(
 | 
				
			||||||
 | 
					          'files[]',
 | 
				
			||||||
 | 
					          handleChangeFileName(otherFiles[i].file!, `__${otherFiles[i].type}`)!,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (cv) {
 | 
				
			||||||
 | 
					      formData.append('files[]', cv)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (idCard) {
 | 
				
			||||||
 | 
					      formData.append('files[]', idCard)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (transcript) {
 | 
				
			||||||
 | 
					      formData.append('files[]', transcript)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (universityDiploma) {
 | 
				
			||||||
 | 
					      formData.append('files[]', universityDiploma)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const token = await getAccessToken()
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const response = await axios.post(updateProfileFolder, formData, {
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					          'Content-Type': 'multipart/form-data',
 | 
				
			||||||
 | 
					          Authorization: `Bearer ${token}`,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if(response.status === 200){
 | 
				
			||||||
 | 
					        getAllFile()
 | 
				
			||||||
 | 
					        setOtherFiles([])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('Error uploading files', error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const getAllFile = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await get(getAllFilesInProfiles, {
 | 
				
			||||||
 | 
					        root_folder: '/storage/profiles/' + JSON.parse(getUser())?.user?.name,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      if (res.status === true) {
 | 
				
			||||||
 | 
					        setData(res.data)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const removeFile = async (url: string) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      await Xdelete(deleteFile, {file_url: url}, getAllFile)
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    const fetchData = async () => {
 | 
					    const fetchData = async () => {
 | 
				
			||||||
      const result = await getListProfilesData()
 | 
					      const result = await getListProfilesData()
 | 
				
			||||||
      setDataProfile(result ?? [])
 | 
					      setDataProfile(result ?? [])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fetchData()
 | 
					    fetchData()
 | 
				
			||||||
 | 
					    getAllFile()
 | 
				
			||||||
  }, [])
 | 
					  }, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleChangePassword = async () => {
 | 
					  const handleChangePassword = async () => {
 | 
				
			||||||
| 
						 | 
					@ -201,6 +306,24 @@ const Profile = () => {
 | 
				
			||||||
    dispatch(logout(navigate))
 | 
					    dispatch(logout(navigate))
 | 
				
			||||||
  }, [dispatch, navigate])
 | 
					  }, [dispatch, navigate])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const handleChangeFileName = (e: File, newName: string) => {
 | 
				
			||||||
 | 
					    const originalFile = e // Get the original file
 | 
				
			||||||
 | 
					    const extend = originalFile.name.split('.')[1]
 | 
				
			||||||
 | 
					    if (originalFile) {
 | 
				
			||||||
 | 
					      const newFileName = `${newName}.${extend}` // Create new file name
 | 
				
			||||||
 | 
					      const newFile = new File([originalFile], newFileName, {
 | 
				
			||||||
 | 
					        type: originalFile.type,
 | 
				
			||||||
 | 
					      }) // Create new file object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return newFile // Save the new file object for further processing
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const checkFileExist = (nameField: string) => {
 | 
				
			||||||
 | 
					    const file = data.find((f) => f.label.includes(nameField))
 | 
				
			||||||
 | 
					    return file
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
      <div className={classes.title}>
 | 
					      <div className={classes.title}>
 | 
				
			||||||
| 
						 | 
					@ -329,6 +452,15 @@ const Profile = () => {
 | 
				
			||||||
                  Your Technical
 | 
					                  Your Technical
 | 
				
			||||||
                </Button>
 | 
					                </Button>
 | 
				
			||||||
              )}
 | 
					              )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              <Button
 | 
				
			||||||
 | 
					                style={{ width: '50%' }}
 | 
				
			||||||
 | 
					                color="orange"
 | 
				
			||||||
 | 
					                onClick={() => setOpenedProfile(true)}
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                <IconFolder stroke={1.5} />
 | 
				
			||||||
 | 
					                Update profile
 | 
				
			||||||
 | 
					              </Button>
 | 
				
			||||||
            </Box>
 | 
					            </Box>
 | 
				
			||||||
          </Box>
 | 
					          </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -437,6 +569,173 @@ const Profile = () => {
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
          </Box>
 | 
					          </Box>
 | 
				
			||||||
        </Modal>
 | 
					        </Modal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <Modal
 | 
				
			||||||
 | 
					          size={'lg'}
 | 
				
			||||||
 | 
					          opened={openedProfile}
 | 
				
			||||||
 | 
					          onClose={() => {
 | 
				
			||||||
 | 
					            setOpenedProfile(false)
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <Box>
 | 
				
			||||||
 | 
					            <form onSubmit={handleSubmit}>
 | 
				
			||||||
 | 
					              <Stack>
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('cv') ? 'flex' : 'none' }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>CV</Text>
 | 
				
			||||||
 | 
					                  <a href={checkFileExist('cv')?.value} target="_blank">
 | 
				
			||||||
 | 
					                    <Text>{`: ${checkFileExist('cv')?.label}`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button variant="outline" size="xs" color="red" ml={'sm'} onClick={()=>{removeFile(checkFileExist('cv')?.value!)}}>
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label={'CV'}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên CV"
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('cv') ? 'none' : 'block' }}
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    0
 | 
				
			||||||
 | 
					                    setCv(handleChangeFileName(e!, 'cv'))
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".pdf,.doc,.docx"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('idCard') ? 'flex' : 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>CCCD</Text>
 | 
				
			||||||
 | 
					                  <a href={checkFileExist('idCard')?.value} target="_blank">
 | 
				
			||||||
 | 
					                    <Text>{`: ${checkFileExist('idCard')?.label}`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button variant="outline" size="xs" color="red" ml={'sm'} onClick={()=>{removeFile(checkFileExist('idCard')?.value!)}}>
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label="CCCD"
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('idCard') ? 'none' : 'block' }}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên CCCD"
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    setIdCard(handleChangeFileName(e!, 'idCard'))
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".jpg,.jpeg,.png,.pdf"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('transcript') ? 'flex' : 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>Bảng điểm</Text>
 | 
				
			||||||
 | 
					                  <a href={checkFileExist('transcript')?.value} target="_blank">
 | 
				
			||||||
 | 
					                    <Text>{`: ${checkFileExist('transcript')?.label}`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button variant="outline" size="xs" color="red" ml={'sm'} onClick={()=>{removeFile(checkFileExist('transcript')?.value!)}}>
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label="Bảng điểm"
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('transcript') ? 'none' : 'block' }}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên bảng điểm"
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    setTranscript(handleChangeFileName(e!, 'transcript'))
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".pdf"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Box
 | 
				
			||||||
 | 
					                  style={{
 | 
				
			||||||
 | 
					                    display: checkFileExist('universityDiploma')
 | 
				
			||||||
 | 
					                      ? 'flex'
 | 
				
			||||||
 | 
					                      : 'none',
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                  <Text>Bằng đại học</Text>
 | 
				
			||||||
 | 
					                  <a
 | 
				
			||||||
 | 
					                    href={checkFileExist('universityDiploma')?.value}
 | 
				
			||||||
 | 
					                    target="_blank"
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    <Text>{`: ${
 | 
				
			||||||
 | 
					                      checkFileExist('universityDiploma')?.label
 | 
				
			||||||
 | 
					                    }`}</Text>
 | 
				
			||||||
 | 
					                  </a>
 | 
				
			||||||
 | 
					                  <Button variant="outline" size="xs" color="red" ml={'sm'} onClick={()=>{removeFile(checkFileExist('universityDiploma')?.value!)}}>
 | 
				
			||||||
 | 
					                    Delete
 | 
				
			||||||
 | 
					                  </Button>
 | 
				
			||||||
 | 
					                </Box>
 | 
				
			||||||
 | 
					                <FileInput
 | 
				
			||||||
 | 
					                  label="Bằng đại học"
 | 
				
			||||||
 | 
					                  style={{ display: checkFileExist('universityDiploma') ? 'none' : 'block' }}
 | 
				
			||||||
 | 
					                  placeholder="Tải lên bằng đại học"
 | 
				
			||||||
 | 
					                  onChange={(e) => {
 | 
				
			||||||
 | 
					                    setUniversityDiploma(
 | 
				
			||||||
 | 
					                      handleChangeFileName(e!, 'universityDiploma'),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  accept=".pdf,.jpg,.jpeg,.png"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Text>Danh sách file khác:</Text>
 | 
				
			||||||
 | 
					                  <Box>
 | 
				
			||||||
 | 
					                    {data.find((f)=>f.label === 'others')?.children?.map((c, index)=>{
 | 
				
			||||||
 | 
					                      return <Box key={index}
 | 
				
			||||||
 | 
					                      style={{
 | 
				
			||||||
 | 
					                        display: 'flex',
 | 
				
			||||||
 | 
					                        marginBottom:'5px'
 | 
				
			||||||
 | 
					                      }}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                      <a
 | 
				
			||||||
 | 
					                        href={c?.value}
 | 
				
			||||||
 | 
					                        target="_blank"
 | 
				
			||||||
 | 
					                      >
 | 
				
			||||||
 | 
					                        <Text>{`${
 | 
				
			||||||
 | 
					                          c?.label
 | 
				
			||||||
 | 
					                        }`}</Text>
 | 
				
			||||||
 | 
					                      </a>
 | 
				
			||||||
 | 
					                      <Button variant="outline" size="xs" color="red" ml={'sm'} onClick={()=>{removeFile(c?.value!)}}>
 | 
				
			||||||
 | 
					                        Delete
 | 
				
			||||||
 | 
					                      </Button>
 | 
				
			||||||
 | 
					                    </Box>
 | 
				
			||||||
 | 
					                    })}
 | 
				
			||||||
 | 
					                  </Box>
 | 
				
			||||||
 | 
					                {otherFiles.map((fileInput, index) => (
 | 
				
			||||||
 | 
					                  <Group key={index}>
 | 
				
			||||||
 | 
					                    <FileInput
 | 
				
			||||||
 | 
					                      placeholder="Chọn file"
 | 
				
			||||||
 | 
					                      onChange={(file) =>
 | 
				
			||||||
 | 
					                        handleOtherFileChange(index, 'file', file!)
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                      w={'30%'}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                    <TextInput
 | 
				
			||||||
 | 
					                      placeholder="Nhập tên file (không cần phần mở rộng)"
 | 
				
			||||||
 | 
					                      value={fileInput.type}
 | 
				
			||||||
 | 
					                      w={'65%'}
 | 
				
			||||||
 | 
					                      onChange={(e) =>
 | 
				
			||||||
 | 
					                        handleOtherFileChange(
 | 
				
			||||||
 | 
					                          index,
 | 
				
			||||||
 | 
					                          'type',
 | 
				
			||||||
 | 
					                          e.currentTarget.value,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                  </Group>
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					                <Button type="button" onClick={addOtherFileInput}>
 | 
				
			||||||
 | 
					                  Thêm file khác
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Button type="submit" color="blue">
 | 
				
			||||||
 | 
					                  Save
 | 
				
			||||||
 | 
					                </Button>
 | 
				
			||||||
 | 
					              </Stack>
 | 
				
			||||||
 | 
					            </form>
 | 
				
			||||||
 | 
					          </Box>
 | 
				
			||||||
 | 
					        </Modal>
 | 
				
			||||||
      </Box>
 | 
					      </Box>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
import ResetPassword from '@/components/Authentication/ResetPassword'
 | 
					import ResetPassword from '@/components/Authentication/ResetPassword'
 | 
				
			||||||
import BasePage from '@/components/BasePage/BasePage'
 | 
					import BasePage from '@/components/BasePage/BasePage'
 | 
				
			||||||
import ProtectedRoute from '@/components/ProtectedRoute/ProtectedRoute'
 | 
					import ProtectedRoute from '@/components/ProtectedRoute/ProtectedRoute'
 | 
				
			||||||
 | 
					import AllProfiles from '@/pages/AllProfiles/AllProfiles'
 | 
				
			||||||
import Allocation from '@/pages/Allocation/Allocation'
 | 
					import Allocation from '@/pages/Allocation/Allocation'
 | 
				
			||||||
import PageLogin from '@/pages/Auth/Login/Login'
 | 
					import PageLogin from '@/pages/Auth/Login/Login'
 | 
				
			||||||
import LeaveManagement from '@/pages/LeaveManagement/LeaveManagement'
 | 
					import LeaveManagement from '@/pages/LeaveManagement/LeaveManagement'
 | 
				
			||||||
| 
						 | 
					@ -83,7 +84,7 @@ const mainRoutes = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    path: '/timekeeping',
 | 
					    path: '/timekeeping',
 | 
				
			||||||
    element: (
 | 
					    element: (
 | 
				
			||||||
      <ProtectedRoute mode="home" permission="staff">
 | 
					      <ProtectedRoute mode="home" permission="staff,accountant">
 | 
				
			||||||
        <BasePage
 | 
					        <BasePage
 | 
				
			||||||
          main={
 | 
					          main={
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
| 
						 | 
					@ -97,7 +98,7 @@ const mainRoutes = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    path: '/leave-management',
 | 
					    path: '/leave-management',
 | 
				
			||||||
    element: (
 | 
					    element: (
 | 
				
			||||||
      <ProtectedRoute mode="home" permission="staff">
 | 
					      <ProtectedRoute mode="home" permission="staff,accountant">
 | 
				
			||||||
        <BasePage
 | 
					        <BasePage
 | 
				
			||||||
          main={
 | 
					          main={
 | 
				
			||||||
            <>
 | 
					            <>
 | 
				
			||||||
| 
						 | 
					@ -234,6 +235,20 @@ const mainRoutes = [
 | 
				
			||||||
      </ProtectedRoute>
 | 
					      </ProtectedRoute>
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    path: '/profiles',
 | 
				
			||||||
 | 
					    element: (
 | 
				
			||||||
 | 
					      <ProtectedRoute mode="route" permission="admin,accountant">
 | 
				
			||||||
 | 
					        <BasePage
 | 
				
			||||||
 | 
					          main={
 | 
				
			||||||
 | 
					            <>
 | 
				
			||||||
 | 
					              <AllProfiles />
 | 
				
			||||||
 | 
					            </>
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ></BasePage>
 | 
				
			||||||
 | 
					      </ProtectedRoute>
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  // {
 | 
					  // {
 | 
				
			||||||
  //   path: '/packages',
 | 
					  //   path: '/packages',
 | 
				
			||||||
  //   element: (
 | 
					  //   element: (
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue