From 1e604f9a8b7aa724ef0efd5160f5b077cf5199ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=B4=20Quang=20=C4=90=E1=BB=A9c?= Date: Tue, 2 Sep 2025 23:40:28 +0700 Subject: [PATCH] create block details and stuff --- .../guards/router-protected/role.guard.ts | 11 +- src/app/core/models/organization.model.ts | 11 +- .../exercise-vetical-menu.ts | 8 +- .../vetical-menu-dynamic/org-vertical-menu.ts | 2 +- .../payment-vertical-menu.ts | 2 +- .../api-service/organization.service.ts | 52 ++- .../services/config-service/api.enpoints.ts | 41 +- .../pages/chat/chat.component.html | 16 +- .../exercise-layout.component.ts | 4 +- .../assign-exercise.component.ts | 7 +- .../layout-organization.component.ts | 13 +- .../details-organization.component.html | 98 ++++- .../details-organization.component.scss | 414 ++++++++++-------- .../details-organization.component.ts | 129 ++++-- .../organization-routing.module.ts | 30 +- .../in-a-organization.component.html | 6 + .../in-a-organization.component.scss | 11 + .../in-a-organization.component.ts | 11 + .../list-organizations.component.html | 61 --- .../list-organizations.component.scss | 140 ------ .../list-organizations.component.ts | 180 -------- .../org-blocks/org-blocks.component.html | 111 +++++ .../org-blocks/org-blocks.component.scss | 174 ++++++++ .../pages/org-blocks/org-blocks.component.ts | 175 ++++++++ .../org-details/org-details.component.html | 1 + .../org-details/org-details.component.scss | 0 .../org-details/org-details.component.ts | 11 + .../organization-management.component.html | 8 +- .../organization-management.component.ts | 8 +- .../resource-create/resource-create.html | 15 +- .../resource-create/resource-create.scss | 2 +- .../pages/resource-create/resource-create.ts | 9 +- src/app/styles/themes/_variables.scss | 2 +- 33 files changed, 1067 insertions(+), 696 deletions(-) create mode 100644 src/app/features/organization/pages/in-a-organization/in-a-organization.component.html create mode 100644 src/app/features/organization/pages/in-a-organization/in-a-organization.component.scss create mode 100644 src/app/features/organization/pages/in-a-organization/in-a-organization.component.ts delete mode 100644 src/app/features/organization/pages/list-organizations/list-organizations.component.html delete mode 100644 src/app/features/organization/pages/list-organizations/list-organizations.component.scss delete mode 100644 src/app/features/organization/pages/list-organizations/list-organizations.component.ts create mode 100644 src/app/features/organization/pages/org-blocks/org-blocks.component.html create mode 100644 src/app/features/organization/pages/org-blocks/org-blocks.component.scss create mode 100644 src/app/features/organization/pages/org-blocks/org-blocks.component.ts create mode 100644 src/app/features/organization/pages/org-details/org-details.component.html create mode 100644 src/app/features/organization/pages/org-details/org-details.component.scss create mode 100644 src/app/features/organization/pages/org-details/org-details.component.ts diff --git a/src/app/core/guards/router-protected/role.guard.ts b/src/app/core/guards/router-protected/role.guard.ts index 68d96b6b..61648aed 100644 --- a/src/app/core/guards/router-protected/role.guard.ts +++ b/src/app/core/guards/router-protected/role.guard.ts @@ -11,12 +11,17 @@ import { Observable } from 'rxjs'; import { decodeJWT } from '../../../shared/utils/stringProcess'; import { sendNotification } from '../../../shared/utils/notification'; import { Store } from '@ngrx/store'; +import { Location } from '@angular/common'; @Injectable({ providedIn: 'root', }) export class RoleGuard implements CanActivate { - constructor(private router: Router, private store: Store) {} + constructor( + private router: Router, + private store: Store, + private location: Location + ) {} canActivate( next: ActivatedRouteSnapshot, @@ -44,7 +49,9 @@ export class RoleGuard implements CanActivate { 'warning' ); // nếu không đủ quyền, redirect sang trang 403 hoặc trang chủ - return this.router.createUrlTree(['/']); + this.location.back(); + // return this.router.createUrlTree(['/']); // về trang chủ + return false; } } } diff --git a/src/app/core/models/organization.model.ts b/src/app/core/models/organization.model.ts index 9e9c2fb6..9684cadb 100644 --- a/src/app/core/models/organization.model.ts +++ b/src/app/core/models/organization.model.ts @@ -74,6 +74,15 @@ export type EditOrgRequest = { email?: string; phone?: string; address?: string; - status?: string; + status?: number; logo?: File; }; + +export type ParamGetAllBlockOfOrg = { + blocksPage?: number; + blocksSize?: number; + membersPage?: number; + membersSize?: number; + activeOnlyMembers?: boolean; + includeUnassigned?: boolean; +}; diff --git a/src/app/core/router-manager/vetical-menu-dynamic/exercise-vetical-menu.ts b/src/app/core/router-manager/vetical-menu-dynamic/exercise-vetical-menu.ts index 82ff41d8..d70cabfa 100644 --- a/src/app/core/router-manager/vetical-menu-dynamic/exercise-vetical-menu.ts +++ b/src/app/core/router-manager/vetical-menu-dynamic/exercise-vetical-menu.ts @@ -1,13 +1,7 @@ import { SidebarItem } from '../../models/data-handle'; -export function sidebarExercises(role: string): SidebarItem[] { +export function sidebarExercises(roles: string[]): SidebarItem[] { return [ - // { - // id: 'exam', - // path: 'exercise/exam-list', - // label: 'Bài thi', - // icon: 'fas fa-file-alt', - // }, { id: 'exercise', path: '/exercise/exercise-layout/my-assign-list', diff --git a/src/app/core/router-manager/vetical-menu-dynamic/org-vertical-menu.ts b/src/app/core/router-manager/vetical-menu-dynamic/org-vertical-menu.ts index 4d3b284c..a00787b2 100644 --- a/src/app/core/router-manager/vetical-menu-dynamic/org-vertical-menu.ts +++ b/src/app/core/router-manager/vetical-menu-dynamic/org-vertical-menu.ts @@ -5,7 +5,7 @@ export function sidebarOrgRouter(roles: string[]): SidebarItem[] { { id: 'list-orgs', path: '/organization/orgs-list', - label: 'Nạp tiền', + label: 'Danh sách tổ chức', icon: 'fa-solid fa-tasks', }, ]; diff --git a/src/app/core/router-manager/vetical-menu-dynamic/payment-vertical-menu.ts b/src/app/core/router-manager/vetical-menu-dynamic/payment-vertical-menu.ts index df7d44ec..8d7c4ee9 100644 --- a/src/app/core/router-manager/vetical-menu-dynamic/payment-vertical-menu.ts +++ b/src/app/core/router-manager/vetical-menu-dynamic/payment-vertical-menu.ts @@ -17,7 +17,7 @@ export function sidebarPaymentRouter(role: string): SidebarItem[] { { id: 'purchase-history', path: '/service-and-payment/purchase-history', - label: 'Lịch sử giao dịch', + label: 'Lịch sử đã mua', icon: 'fa-solid fa-cart-shopping', }, ]; diff --git a/src/app/core/services/api-service/organization.service.ts b/src/app/core/services/api-service/organization.service.ts index 89e0ba6e..13df2e73 100644 --- a/src/app/core/services/api-service/organization.service.ts +++ b/src/app/core/services/api-service/organization.service.ts @@ -3,11 +3,13 @@ import { ApiMethod } from '../config-service/api.methods'; import { ApiResponse, IPaginationResponse } from '../../models/api-response'; import { API_CONFIG } from '../config-service/api.enpoints'; import { + BlockResponse, CreateOrgRequest, EditOrgRequest, FilterOrgs, OrganizationInfo, OrganizationResponse, + ParamGetAllBlockOfOrg, } from '../../models/organization.model'; import { PostResponse } from '../../models/post.models'; @@ -45,9 +47,12 @@ export class OrganizationService { } editOrg(orgId: string, data: EditOrgRequest) { - return this.api.put>( - API_CONFIG.ENDPOINTS.PUT.EDIT_ORG(orgId), - data + const { logo, ...otherData } = data; + + return this.api.patchWithFormData>( + API_CONFIG.ENDPOINTS.PATCH.EDIT_ORG(orgId), + otherData, + logo ); } @@ -56,4 +61,45 @@ export class OrganizationService { API_CONFIG.ENDPOINTS.DELETE.DELETE_ORG(orgId) ); } + + createBlockInOrg( + orgId: string, + data: { name: string; code: string; description: string } + ) { + return this.api.post>( + API_CONFIG.ENDPOINTS.POST.CREATE_BLOCK_IN_ORG(orgId), + data + ); + } + + getAllBlockOfOrg(orgId: string, params: ParamGetAllBlockOfOrg) { + return this.api.get>>( + API_CONFIG.ENDPOINTS.GET.SEACH_ALL_BLOCKS(orgId, params) + ); + } + + updateBlock( + id: string, + data: { name?: string; code?: string; description?: string } + ) { + return this.api.patch>( + API_CONFIG.ENDPOINTS.PATCH.EDIT_BLOCK(id), + data + ); + } + + deleteBlock(id: string) { + return this.api.delete>( + API_CONFIG.ENDPOINTS.DELETE.DELETE_BLOCK(id) + ); + } + + getBlockDetails( + blockId: string, + data: { membersPage: number; membersSize: number; activeOnly: boolean } + ) { + return this.api.get>( + API_CONFIG.ENDPOINTS.GET.GET_BLOCK_DETAILS(blockId, data) + ); + } } diff --git a/src/app/core/services/config-service/api.enpoints.ts b/src/app/core/services/config-service/api.enpoints.ts index c3fb9f09..8a5073f7 100644 --- a/src/app/core/services/config-service/api.enpoints.ts +++ b/src/app/core/services/config-service/api.enpoints.ts @@ -1,6 +1,9 @@ import { environment } from '../../../../environments/environment'; import { EnumType } from '../../models/data-handle'; -import { FilterOrgs } from '../../models/organization.model'; +import { + FilterOrgs, + ParamGetAllBlockOfOrg, +} from '../../models/organization.model'; import { SearchingUser } from '../../models/user.models'; export const version = '/v1'; @@ -168,8 +171,35 @@ export const API_CONFIG = { return query; }, - GET_ORG_DETAILS_BY_ID: (orgId: string) => - `/org/api/Organization/${orgId}`, + GET_ORG_DETAILS_BY_ID: (orgId: string) => `/org/organization/${orgId}`, + SEACH_ALL_BLOCKS: (orgId: string, params: ParamGetAllBlockOfOrg) => { + let query = `/org/${orgId}/blocks?`; + if ( + params?.blocksPage !== undefined && + params?.blocksSize !== undefined + ) { + query += `blocksPage=${params.blocksPage}&blocksSize=${params.blocksSize}&`; + } + if ( + params?.membersPage !== undefined && + params?.membersSize !== undefined + ) { + query += `membersPage=${params.membersPage}&membersSize=${params.membersSize}&`; + } + if (params?.activeOnlyMembers !== undefined) { + query += `activeOnlyMembers=${params.activeOnlyMembers}&`; + } + if (params?.includeUnassigned !== undefined) { + query += `includeUnassigned=${params.includeUnassigned}&`; + } + // Xóa dấu `&` hoặc `?` cuối cùng nếu có + return query.replace(/[&?]$/, ''); + }, + GET_BLOCK_DETAILS: ( + blockId: string, + data: { membersPage: number; membersSize: number; activeOnly: boolean } + ) => + `/org/block/${blockId}?membersPage=${data.membersPage}&membersSize=${data.membersSize}&activeOnly=${data.activeOnly}`, }, POST: { LOGIN: '/identity/auth/login', @@ -236,10 +266,10 @@ export const API_CONFIG = { TOPUP: '/payment/topup', PURCHASE: '/payment/purchase', CREATE_ORGANIZATION: '/org/organization', + CREATE_BLOCK_IN_ORG: (orgId: string) => `/org/${orgId}/block`, }, PUT: { EDIT_FILE: (id: string) => `/file/api/FileDocument/edit/${id}`, - EDIT_ORG: (id: string) => `/org/api/Organization/${id}`, }, PATCH: { UPDATE_EXERCISE: (exerciseId: string) => @@ -257,6 +287,8 @@ export const API_CONFIG = { `/submission/coding/exercise/${exerciseId}/coding-detail`, RENAME_THREAD: (threadId: string) => `/ai/chat/thread/${threadId}`, CHANGE_MY_PASSWORD: `/identity/user/password`, + EDIT_ORG: (id: string) => `/org/organization/${id}`, + EDIT_BLOCK: (blockId: string) => `/org/block/${blockId}`, }, DELETE: { DELETE_QUESTION: (exerciseId: string, questionId: string) => @@ -277,6 +309,7 @@ export const API_CONFIG = { UNSAVE_EXERCISE: (exerciseId: string) => `/profile/exercise/${exerciseId}/save`, DELETE_ORG: (orgId: string) => `/org/organization/${orgId}`, + DELETE_BLOCK: (blockId: string) => `/org/block/${blockId}`, }, }, HEADERS: { diff --git a/src/app/features/conversation-chat/pages/chat/chat.component.html b/src/app/features/conversation-chat/pages/chat/chat.component.html index 54b7d440..99c3077a 100644 --- a/src/app/features/conversation-chat/pages/chat/chat.component.html +++ b/src/app/features/conversation-chat/pages/chat/chat.component.html @@ -177,14 +177,6 @@

{{ selectedConversation.conversationName }}

- +
diff --git a/src/app/features/excercise/exercise-layout/exercise-layout.component.ts b/src/app/features/excercise/exercise-layout/exercise-layout.component.ts index a5884fef..33aeda41 100644 --- a/src/app/features/excercise/exercise-layout/exercise-layout.component.ts +++ b/src/app/features/excercise/exercise-layout/exercise-layout.component.ts @@ -30,8 +30,8 @@ export class ExerciseLayoutComponent implements OnInit, OnDestroy { private routerSubscription!: Subscription; constructor(private router: Router) { - const role = decodeJWT(localStorage.getItem('token') ?? '')?.payload.scope; - this.sidebarData = sidebarExercises(role); + const roles = decodeJWT(localStorage.getItem('token') ?? '')?.payload.roles; + this.sidebarData = sidebarExercises(roles); } ngOnInit() { diff --git a/src/app/features/excercise/exercise-pages/assign-exercise/assign-exercise.component.ts b/src/app/features/excercise/exercise-pages/assign-exercise/assign-exercise.component.ts index 25ef5725..8132815e 100644 --- a/src/app/features/excercise/exercise-pages/assign-exercise/assign-exercise.component.ts +++ b/src/app/features/excercise/exercise-pages/assign-exercise/assign-exercise.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { SearchUserProfileResponse, User, @@ -18,7 +18,6 @@ import { import { forkJoin } from 'rxjs/internal/observable/forkJoin'; import { catchError } from 'rxjs/internal/operators/catchError'; import { of } from 'rxjs/internal/observable/of'; -import { Subscription } from 'rxjs/internal/Subscription'; import { AssignedStudentsListResponse, MySubmissionsHistoryResponse, @@ -33,10 +32,6 @@ import { CodingService } from '../../../../core/services/api-service/coding.serv }) export class AssignExerciseComponent implements OnInit { private debounceTimer: ReturnType | null = null; - private checkTypeExercise: string[] = [ - '/assign-exercise-code', - '/assign-exercise-quiz', - ]; avatarUrlDefault: string = avatarUrlDefault; exerciseTypeCall: MySubmissionsHistoryResponse['exerciseType'] = 'QUIZ'; diff --git a/src/app/features/organization/layout-organization/layout-organization.component.ts b/src/app/features/organization/layout-organization/layout-organization.component.ts index 3dac60f5..6841d96d 100644 --- a/src/app/features/organization/layout-organization/layout-organization.component.ts +++ b/src/app/features/organization/layout-organization/layout-organization.component.ts @@ -1,9 +1,11 @@ import { Component } from '@angular/core'; import { MainSidebarComponent } from '../../../shared/components/fxdonad-shared/main-sidebar/main-sidebar.component'; -import { sidebarOrganizations } from '../../../core/constants/menu-router.data'; import { CommonModule } from '@angular/common'; import { AdminRoutingModule } from '../../dashboard/dashboard-routing.module'; -import { RouterOutlet } from '@angular/router'; +import { Router, RouterOutlet } from '@angular/router'; +import { sidebarOrgRouter } from '../../../core/router-manager/vetical-menu-dynamic/org-vertical-menu'; +import { SidebarItem } from '../../../core/models/data-handle'; +import { decodeJWT } from '../../../shared/utils/stringProcess'; @Component({ selector: 'app-layout-organization', @@ -18,7 +20,12 @@ import { RouterOutlet } from '@angular/router'; }) export class LayoutOrganizationComponent { isSidebarCollapsed = true; - sidebarData = sidebarOrganizations; + sidebarData: SidebarItem[] = []; showSidebar = true; + + constructor(private router: Router) { + const roles = decodeJWT(localStorage.getItem('token') ?? '')?.payload.roles; + this.sidebarData = sidebarOrgRouter(roles); + } } diff --git a/src/app/features/organization/organization-component/details-organization/details-organization.component.html b/src/app/features/organization/organization-component/details-organization/details-organization.component.html index bb2dc0e1..bb3821c9 100644 --- a/src/app/features/organization/organization-component/details-organization/details-organization.component.html +++ b/src/app/features/organization/organization-component/details-organization/details-organization.component.html @@ -13,13 +13,23 @@
+
diff --git a/src/app/features/organization/pages/organization-management/organization-management.component.ts b/src/app/features/organization/pages/organization-management/organization-management.component.ts index 8bd7ab7f..b4ad9b40 100644 --- a/src/app/features/organization/pages/organization-management/organization-management.component.ts +++ b/src/app/features/organization/pages/organization-management/organization-management.component.ts @@ -20,6 +20,7 @@ import { PaginationComponent } from '../../../../shared/components/fxdonad-share import { lottieOptions2 } from '../../../../core/constants/value.constant'; import { LottieComponent } from 'ngx-lottie'; import { OrganizationCreateModalComponent } from '../../organization-component/organization-create-modal/organization-create-modal.component'; +import { Router } from '@angular/router'; @Component({ selector: 'app-organization-management', @@ -57,7 +58,8 @@ export class OrganizationManagementComponent implements OnInit, OnDestroy { constructor( private orgService: OrganizationService, - private fb: FormBuilder + private fb: FormBuilder, + private router: Router ) {} ngOnInit() { @@ -171,6 +173,10 @@ export class OrganizationManagementComponent implements OnInit, OnDestroy { } } + goToOrganization(id: string) { + this.router.navigate([`/organization/in-org/${id}/org-details`]); + } + // Helper function để tính tổng thành viên getTotalMembers(org: OrganizationResponse): number { if (!org.blocks?.data) return 0; diff --git a/src/app/features/resource-learning/pages/resource-create/resource-create.html b/src/app/features/resource-learning/pages/resource-create/resource-create.html index 326b6537..f5e72664 100644 --- a/src/app/features/resource-learning/pages/resource-create/resource-create.html +++ b/src/app/features/resource-learning/pages/resource-create/resource-create.html @@ -1,4 +1,4 @@ -
+
@@ -29,22 +29,15 @@ [variant]="'cancel'" [width]="'116px'" [height]="'50px'" - (onClick)="cancelPost()" + (onClick)="cancelResource()" >Hủy - Lưu Nháp Đăng BàiTải lên
diff --git a/src/app/features/resource-learning/pages/resource-create/resource-create.scss b/src/app/features/resource-learning/pages/resource-create/resource-create.scss index c298f7b0..b9c64df3 100644 --- a/src/app/features/resource-learning/pages/resource-create/resource-create.scss +++ b/src/app/features/resource-learning/pages/resource-create/resource-create.scss @@ -12,7 +12,7 @@ --border-radius: 12px; } -.post-create-container { +.resource-create-container { display: flex; flex-wrap: wrap; gap: 2rem; diff --git a/src/app/features/resource-learning/pages/resource-create/resource-create.ts b/src/app/features/resource-learning/pages/resource-create/resource-create.ts index 3ba77356..638a9692 100644 --- a/src/app/features/resource-learning/pages/resource-create/resource-create.ts +++ b/src/app/features/resource-learning/pages/resource-create/resource-create.ts @@ -106,11 +106,8 @@ export class ResourceCreatePageComponent { // Nếu bạn muốn chỉ mở 1 dropdown tại một thời điểm this.activeDropdown = this.activeDropdown === id ? null : id; } - saveDraftPost(): void { - // Logic to save the draft post - console.log('Draft post saved:', this.thumbnail); - } - createPost(): void { + + createResource(): void { if (!this.selectedFile) { sendNotification( this.store, @@ -164,7 +161,7 @@ export class ResourceCreatePageComponent { }); } - cancelPost(): void { + cancelResource(): void { // Logic to cancel the post creation console.log('Post creation cancelled'); } diff --git a/src/app/styles/themes/_variables.scss b/src/app/styles/themes/_variables.scss index 9d66de60..cec07ad2 100644 --- a/src/app/styles/themes/_variables.scss +++ b/src/app/styles/themes/_variables.scss @@ -103,7 +103,7 @@ $success-color: #4caf50; --tags-color: #456178; - --button-color: #{$blue-dark}; + --button-color: #61a9e4; --button-color-hover: #{$blue-main}; --input-primary-color: #757575;