ng-ttc-frontend: sep userrole, added roleGuard
Some checks failed
Build Docker Image / Restart Docker Compose (push) Has been cancelled
Build Docker Image / Build Docker Image (push) Has been cancelled

Signed-off-by: supphakitd <67319010028@technictrang.ac.th>
This commit is contained in:
2025-12-03 06:54:07 +07:00
parent 278bfe80ec
commit 60be3f7890
8 changed files with 104 additions and 272 deletions

View File

@@ -54,7 +54,15 @@
<span class="text-gray-700">เอกสารฟอร์มวิทยาลัย</span>
</button> -->
<button class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer" (click)="navigate('main/manager')">
<button *ngIf="userData.role === 'U'" class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer" (click)="navigate('main/project')">
<!-- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.992 9.992 0 0112 21c-3.142 0-6.186-1.042-8.625-2.999M21 12a9.992 9.992 0 00-3-6.945M3 12a9.992 9.992 0 013-6.945M12 15a3 3 0 100-6 3 3 0 000 6z" />
</svg> -->
<img src="chart-simple.png" alt="" class="h-6 w-6 text-gray-500 opacity-50">
<span class="text-gray-700">ยืนโครงการ</span>
</button>
<button *ngIf="userData.role === 'D'" class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer" (click)="navigate('main/manager')">
<!-- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.992 9.992 0 0112 21c-3.142 0-6.186-1.042-8.625-2.999M21 12a9.992 9.992 0 00-3-6.945M3 12a9.992 9.992 0 013-6.945M12 15a3 3 0 100-6 3 3 0 000 6z" />
</svg> -->
@@ -62,7 +70,7 @@
<span class="text-gray-700">จัดสรรงบประมาณ</span>
</button>
<button class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer" (click)="navigate('main/report')">
<button *ngIf="userData.role === 'D'" class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition duration-150 cursor-pointer" (click)="navigate('main/report')">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>

View File

@@ -13,6 +13,12 @@ export class MainLandingComponent implements OnInit {
private router: Router
) {}
userData: any = {
name: localStorage.getItem('usrthinam') + ' ' + localStorage.getItem('usrthilstnam'),
role: localStorage.getItem('usrrol'),
avatar: ''
};
ngOnInit() {
}

View File

@@ -11,13 +11,16 @@
<!-- ถ้าจะเอาออกด้วย ให้ลบส่วนนี้ทิ้งครับ แต่ถ้ายังอยากเก็บไว้ ก็ปล่อยไว้ -->
<div *ngIf="!isCollapsed" class="p-4 border-b border-gray-50 bg-gray-50/50 shrink-0 transition-all duration-300">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-red-100 flex items-center justify-center text-red-500 font-bold border border-red-200 shadow-sm shrink-0">
<div
class="w-10 h-10 rounded-full bg-red-100 flex items-center justify-center text-red-500 font-bold border border-red-200 shadow-sm shrink-0">
<span *ngIf="!userData?.avatar">{{ (userData?.name?.charAt(0) || 'U') | uppercase }}</span>
</div>
<div class="overflow-hidden">
<p class="text-sm font-semibold text-gray-700 truncate">{{ userData?.name || 'User Name' }}</p>
<p class="text-xs text-gray-500 truncate">{{ userData?.role || 'Administrator' }}</p>
</div>
</div>
<div class="overflow-hidden">
<p class="text-sm font-semibold text-gray-700 truncate">{{ userData?.name || 'User Name' }}</p>
<p class="text-xs text-gray-500 truncate">
{{ userData?.role === 'U' ? 'ผู้ยืนโครงการ' : 'ผู้อนุมัติโครงการ' }}
</p>
</div>
</div>
</div>
@@ -26,9 +29,9 @@
<!-- Dashboard -->
<a routerLink="/main" routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
[routerLinkActiveOptions]="{exact: true}"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Dashboard' : ''">
[routerLinkActiveOptions]="{exact: true}"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Dashboard' : ''">
<div class="w-6 flex justify-center shrink-0">
<i class="fas fa-th-large text-lg group-hover:scale-110 transition-transform"></i>
</div>
@@ -36,23 +39,24 @@
</a>
<!-- Section Header -->
<div *ngIf="!isCollapsed" class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider whitespace-nowrap">
<div *ngIf="!isCollapsed"
class="pt-4 pb-2 px-3 text-xs font-semibold text-gray-400 uppercase tracking-wider whitespace-nowrap">
Management
</div>
<div *ngIf="isCollapsed" class="h-4"></div>
<!-- Projects -->
<a routerLink="/main/project" routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Projects' : ''">
<a *ngIf="userData?.role === 'U'" routerLink="/main/project" routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Projects' : ''">
<div class="w-6 flex justify-center shrink-0">
<i class="fas fa-folder text-lg group-hover:scale-110 transition-transform"></i>
</div>
<span *ngIf="!isCollapsed" class="font-medium whitespace-nowrap">โครงการของฉัน</span>
</a>
<!-- Budgets -->
<!-- <a routerLink="/main/budget" routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
<!-- Budgets -->
<!-- <a routerLink="/main/budget" routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Budgets' : ''">
<div class="w-6 flex justify-center shrink-0">
@@ -62,23 +66,24 @@
</a> -->
<!-- Users -->
<a routerLink="/main/manager" routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Users' : ''">
<div class="w-6 flex justify-center shrink-0">
<i class="fas fa-users-cog text-lg group-hover:scale-110 transition-transform"></i>
</div>
<span *ngIf="!isCollapsed" class="font-medium whitespace-nowrap">จัดสรรงบประมาณ</span>
</a>
<a *ngIf="userData?.role === 'D'" routerLink="/main/manager"
routerLinkActive="bg-red-50 text-red-600 shadow-sm ring-1 ring-red-100"
class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-all group"
[title]="isCollapsed ? 'Users' : ''">
<div class="w-6 flex justify-center shrink-0">
<i class="fas fa-users-cog text-lg group-hover:scale-110 transition-transform"></i>
</div>
<span *ngIf="!isCollapsed" class="font-medium whitespace-nowrap">จัดสรรงบประมาณ</span>
</a>
</nav>
<!-- Footer / Logout -->
<div class="p-4 border-t border-gray-200 bg-gray-50 shrink-0">
<button (click)="logout()"
class="flex items-center justify-center gap-2 w-full px-4 py-2 text-sm font-medium text-white bg-red-500 rounded-lg hover:bg-red-600 transition-colors shadow-sm hover:shadow">
class="flex items-center justify-center gap-2 w-full px-4 py-2 text-sm font-medium text-white bg-red-500 rounded-lg hover:bg-red-600 transition-colors shadow-sm hover:shadow">
<i class="fas fa-sign-out-alt"></i>
<span *ngIf="!isCollapsed">Logout</span>
</button>
</div>
</div>
</div>

View File

@@ -12,7 +12,7 @@ export class SidebarComponent implements OnInit {
userData: any = {
name: localStorage.getItem('usrthinam') + ' ' + localStorage.getItem('usrthilstnam'),
role: '',
role: localStorage.getItem('usrrol'),
avatar: ''
};

View File

@@ -62,6 +62,7 @@ export class LoginContentComponent implements OnInit {
localStorage.setItem('id', result.data.usrseq);
localStorage.setItem('usrthinam', result.data.usrthinam);
localStorage.setItem('usrthilstnam', result.data.usrthilstnam);
localStorage.setItem('usrrol', result.data.usrrol);
this.jwtService.restartCountdown();
this.router.navigate(['main']);
} else {

View File

@@ -7,6 +7,7 @@ import { BudgetAproval } from '../../component/budget-aproval/budget-aproval';
import { MainLandingComponent } from '../../component/main-landing/main-landing.component';
import { MainProjectContent } from '../../content/main-project-content/main-project-content';
import { BudgetAprovalContent } from '../../content/budget-aproval-content/budget-aproval-content';
import { RoleGuard } from '../../services/role.guard.ts';
// import { MainReportComponent } from '../../component/main-report/main-report.component';
@@ -15,26 +16,28 @@ const routes: Routes = [
{ path: 'landing', component: MainLandingComponent },
{ path: 'dashboard', component: MainDashboardContentComponent },
{ path: 'report', component: MainReportComponent },
{ path: 'manager', component: MainManagerContentComponent },
{ path: 'project', component: MainProjectContent },
{ path: 'project/:mode', component: MainProjectContent },
// { path: 'budget/approve/:seq', component: BudgetAprovalContent},
{
{
path: 'project',
canActivate: [RoleGuard],
data: { denyRole: 'D' },
component: MainProjectContent
},
{
path: 'project/:mode',
canActivate: [RoleGuard],
data: { denyRole: 'D' },
component: MainProjectContent
},
//manager route blocked for users with role "D" via RoleGuard
{
path: 'manager',
canActivate: [RoleGuard],
data: { denyRole: 'U' },
children: [
{ path: '', component: MainManagerContentComponent }, // รายการโครงการ
{ path: 'aproval', component: BudgetAprovalContent }, // จัดการงบประมาณ
{ path: 'aproval', component: BudgetAprovalContent }, // จัดการงบประมาณ
]
},
// children: [
// {
// path: 'dashboard',
// // loadChildren: () => import('./controls/profile-control/profile-control.module').then(m => m.ProfileControlModule)
// },
// { path: 'report', component: MainReportComponent },
// { path: '', redirectTo: 'profile', pathMatch: 'full' }
// ]
{ path: '', redirectTo: 'landing', pathMatch: 'full' },
{ path: '**', redirectTo: 'landing' }
];