Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/core/interceptors/handle/error-handler.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export class ErrorHandlerService {
sendNotification(
this.store,
'Hết tiền!',
'Bạn cần nạp tiền vào tài khoản của mình trước nhé!',
'Bạn cần nạp tiền vào tài khoản của mình trước để kích hoạt ví nhé!',
'error'
);
} else {
Expand Down
18 changes: 18 additions & 0 deletions src/app/core/models/notice.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type ReadStatusNotice = 'ALL' | 'READ' | 'UNREAD';

export type DeliveryStatus = 'PENDING' | 'SENT' | 'FAILED';

export type GetAllNoticeResponse = {
id: string;
recipient: string;
channel: ReadStatusNotice; // "SOCKET" | "EMAIL" | "ALL"...
templateCode: string;
subject: string;
body: string;
param: { class: string; exercise: string };
readStatus: string;
readAt: null;
deliveryStatus: DeliveryStatus; //// PENDING | SENT | FAILED
deliveredAt: string;
createdAt: string;
};
17 changes: 17 additions & 0 deletions src/app/core/models/user.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,20 @@ export type RequestForgotPasswordResponse = {
email: string;
message: string;
};

export type CreateAccoutByAdmin = {
username: string;
email: string;
password: string;
firstName: string;
lastName: string;
dob: string;
bio: string;
gender: boolean;
displayName: string;
education: number;
links: string[];
city: string;
organizationId: string;
organizationMemberRole: 'ADMIN' | 'TEACHER' | 'STUDENT';
};
34 changes: 34 additions & 0 deletions src/app/core/services/api-service/notification-list.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';

import { ApiMethod } from '../config-service/api.methods';
import { ApiResponse, IPaginationResponse } from '../../models/api-response';
import { API_CONFIG } from '../config-service/api.enpoints';
import {
GetAllNoticeResponse,
ReadStatusNotice,
} from '../../models/notice.model';

@Injectable({
providedIn: 'root',
})
export class NotificationListService {
constructor(private api: ApiMethod) {}
getAllMyNotification(
page: number,
size: number,
statusRead: ReadStatusNotice
) {
return this.api.get<
ApiResponse<IPaginationResponse<GetAllNoticeResponse[]>>
>(
API_CONFIG.ENDPOINTS.GET.GET_ALL_MY_NOTIFICATIONS(page, size, statusRead)
);
}

markAsReadNotification(Ids: string[]) {
return this.api.post<ApiResponse<null>>(
API_CONFIG.ENDPOINTS.POST.MARK_AS_READ_NOTIFICATION,
Ids
);
}
}
18 changes: 18 additions & 0 deletions src/app/core/services/api-service/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ApiMethod } from '../config-service/api.methods';
import { EnumType } from '../../models/data-handle';
import { ApiResponse, IPaginationResponse } from '../../models/api-response';
import {
CreateAccoutByAdmin,
SearchingUser,
SearchUserProfileResponse,
User,
Expand Down Expand Up @@ -45,4 +46,21 @@ export class UserService {
{}
);
}

createAccountUser(
role: 'ADMIN' | 'STUDENT' | 'TEACHER',
data: CreateAccoutByAdmin
) {
let enpoint = '';

if (role === 'ADMIN') {
enpoint = API_CONFIG.ENDPOINTS.POST.ADD_ADMIN;
} else if (role === 'STUDENT') {
enpoint = API_CONFIG.ENDPOINTS.POST.ADD_STUDENT;
} else if (role === 'TEACHER') {
enpoint = API_CONFIG.ENDPOINTS.POST.ADD_TEACHER;
}

return this.api.post<ApiResponse<null>>(enpoint, data);
}
}
13 changes: 12 additions & 1 deletion src/app/core/services/config-service/api.enpoints.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// import { environment } from '../../../../environments/environment';
import { EnumType } from '../../models/data-handle';
import { ReadStatusNotice } from '../../models/notice.model';
import {
FilterOrgs,
ParamGetAllBlockOfOrg,
Expand Down Expand Up @@ -200,12 +201,18 @@ export const API_CONFIG = {
data: { membersPage: number; membersSize: number; activeOnly: boolean }
) =>
`/org/block/${blockId}?membersPage=${data.membersPage}&membersSize=${data.membersSize}&activeOnly=${data.activeOnly}`,
GET_ALL_MY_NOTIFICATIONS: (
page: number,
size: number,
readStatus: ReadStatusNotice
) =>
`/notification/my?page=${page}&size=${size}&readStatus=${readStatus}`,
},
POST: {
LOGIN: '/identity/auth/login',
REGISTER: '/identity/auth/register',
LOGOUT: '/identity/auth/logout',
CREATE_FIRST_PASSWORD: '/identity/auth/user/create-password',
CREATE_FIRST_PASSWORD: '/identity/user/create-password',
REQUEST_FORGOT_PASSWORD: '/identity/auth/forgot-password/request',
RESET_PASSWORD: `/identity/auth/forgot-password/reset`,
REFRESH_TOKEN: '/identity/auth/refresh',
Expand Down Expand Up @@ -271,6 +278,10 @@ export const API_CONFIG = {
BULK_ADD_TO_BLOCK: (blockId: string) =>
`/org/block/${blockId}/members:bulk`,
IMPORT_EXCEL_ADD_MEMBER: '/identity/users/import',
ADD_ADMIN: '/identity/admin',
ADD_STUDENT: '/identity/teacher',
ADD_TEACHER: '/identity/user',
MARK_AS_READ_NOTIFICATION: '/my/mark-read',
},
PUT: {
EDIT_FILE: (id: string) => `/file/api/FileDocument/edit/${id}`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<div class="modal-overlay" [class.active]="isOpen" (click)="closeModal()">
<div
class="modal-content"
[class.active]="isOpen"
(click)="$event.stopPropagation()"
>
<div class="modal-header">
<h2>Tạo người dùng mới</h2>
<button class="close-btn" (click)="closeModal()">&times;</button>
</div>

<div class="modal-body">
<form [formGroup]="createUserForm" (ngSubmit)="onSubmit()">
<div class="steps-container">
<!-- STEP 1 -->
<div class="form-step" [class.active]="currentStep === 1">
<div class="form-group">
<label>Vai trò hệ thống *</label>
<select formControlName="role">
<option value="STUDENT">Học sinh</option>
<option value="TEACHER">Giáo viên</option>
<option value="ADMIN">Quản trị viên</option>
</select>
</div>

<div class="form-group">
<label>Giới tính *</label>
<select formControlName="gender">
<option [ngValue]="true">Nam</option>
<option [ngValue]="false">Nữ</option>
</select>
</div>

<div class="form-grid-2">
<div class="form-group">
<label for="firstName">Họ *</label>
<input type="text" id="firstName" formControlName="firstName" />
</div>
<div class="form-group">
<label for="lastName">Tên *</label>
<input type="text" id="lastName" formControlName="lastName" />
</div>
</div>

<div class="form-group">
<label for="username">User Name *</label>
<input type="text" id="username" formControlName="username" />
</div>

<div class="form-group">
<label for="email">Email *</label>
<input type="email" id="email" formControlName="email" />
</div>

<div class="form-group">
<label for="password">Mật khẩu *</label>
<input type="password" id="password" formControlName="password" />
</div>

<div class="form-group">
<label for="displayName">Tên hiển thị *</label>
<input
type="text"
id="displayName"
formControlName="displayName"
/>
</div>

<!-- Ngày sinh -->
<div class="form-group">
<label for="dob">Ngày sinh *</label>
<input type="date" id="dob" formControlName="dob" />
</div>
</div>

<!-- STEP 2 -->
<div class="form-step" [class.active]="currentStep === 2">
<div class="form-group">
<label for="bio">Tiểu sử</label>
<textarea id="bio" rows="3" formControlName="bio"></textarea>
</div>

<div class="form-group">
<label for="education">Trình độ học vấn</label>
<select id="education" formControlName="education">
<option
*ngFor="let option of educationOptions"
[value]="option.value"
>
{{ option.label }}
</option>
</select>
</div>

<div class="form-group">
<label for="links">Liên kết cá nhân</label>
<input
type="text"
id="links"
formControlName="links"
placeholder="https://..."
/>
</div>

<div class="form-group">
<label for="city">Thành phố</label>
<input type="text" id="city" formControlName="city" />
</div>

<div class="form-group org-search-group">
<label for="organization">Tổ chức *</label>
<input
type="text"
id="organization"
placeholder="Tìm kiếm theo tên tổ chức..."
[value]="selectedOrganizationName || ''"
(input)="onOrgSearchInput($event)"
(focus)="showOrgDropdown = true"
/>

<div class="org-dropdown" *ngIf="showOrgDropdown">
<div *ngIf="isSearchingOrgs" class="dropdown-item loading">
Đang tìm...
</div>
<div
*ngIf="!isSearchingOrgs && searchedOrganizations.length === 0"
class="dropdown-item not-found"
>
Không tìm thấy kết quả.
</div>
<div
*ngFor="let org of searchedOrganizations"
class="dropdown-item"
(click)="selectOrganization(org)"
>
{{ org.name }}
</div>
</div>
</div>

<div class="form-group">
<label>Vai trò trong tổ chức *</label>
<select formControlName="organizationMemberRole">
<option value="STUDENT">Học sinh</option>
<option value="TEACHER">Giáo viên</option>
<option value="ADMIN">Quản trị viên</option>
</select>
</div>
</div>
</div>
</form>
</div>

<div class="modal-footer">
<button
class="btn btn-secondary"
*ngIf="currentStep === 2"
(click)="prevStep()"
>
Quay lại
</button>

<button
class="btn btn-primary"
*ngIf="currentStep === 1"
(click)="nextStep()"
>
Tiếp theo
</button>

<button
class="btn btn-primary"
*ngIf="currentStep === 2"
[disabled]="isSubmitting"
(click)="onSubmit()"
>
<span *ngIf="!isSubmitting">Tạo người dùng</span>
<span *ngIf="isSubmitting" class="spinner"></span>
</button>
</div>
</div>
</div>
Loading
Loading