diff --git a/src/app.routes.ts b/src/app.routes.ts index e4d32e9..7a89d43 100644 --- a/src/app.routes.ts +++ b/src/app.routes.ts @@ -2,6 +2,7 @@ import { CUSTOMERS_ROUTES } from '@/modules/customers/constants'; import { INVENTORIES_ROUTES } from '@/modules/inventories/constants'; import { PRODUCT_BRANDS_ROUTES } from '@/modules/productBrands/constants'; import { PRODUCT_CATEGORIES_ROUTES } from '@/modules/productCategories/constants'; +import { PRODUCTS_ROUTES } from '@/modules/products/constants'; import { STORES_ROUTES } from '@/modules/stores/constants'; import { SUPPLIERS_ROUTES } from '@/modules/suppliers/constants'; import { USERS_ROUTES } from '@/modules/users/constants'; @@ -24,6 +25,7 @@ export const appRoutes: Routes = [ ...PRODUCT_CATEGORIES_ROUTES, ...CUSTOMERS_ROUTES, ...INVENTORIES_ROUTES, + ...PRODUCTS_ROUTES, { path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') }, { path: 'documentation', component: Documentation }, { path: 'pages', loadChildren: () => import('./app/pages/pages.routes') }, diff --git a/src/app/core/constants/index.ts b/src/app/core/constants/index.ts index c826a2c..118f2a2 100644 --- a/src/app/core/constants/index.ts +++ b/src/app/core/constants/index.ts @@ -1,3 +1,2 @@ export * from './defaultData.const'; export * from './menuItems.const'; -export * from './roles.const'; diff --git a/src/app/core/constants/roles.const.ts b/src/app/core/constants/roles.const.ts deleted file mode 100644 index 49d81d7..0000000 --- a/src/app/core/constants/roles.const.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TRoles } from '../models'; - -export default { - ADMIN: { - title: 'مدیر سیستم', - key: 'ADMIN', - }, - SCHOOL: { - title: 'مرکز آموزشی', - key: 'SCHOOL', - }, - TEACHER: { - title: 'دبیر', - key: 'TEACHER', - }, - STUDENTS: { - title: 'دانش‌آموز', - key: 'STUDENTS', - }, -} as Record; diff --git a/src/app/modules/auth/constants/central-auth-roles.const.ts b/src/app/modules/auth/constants/central-auth-roles.const.ts deleted file mode 100644 index ffe3be6..0000000 --- a/src/app/modules/auth/constants/central-auth-roles.const.ts +++ /dev/null @@ -1,5 +0,0 @@ -import rolesConst from '@/core/constants/roles.const'; - -export const CENTRAL_AUTH_ROLES = [ - ...Object.values(rolesConst).filter((role) => role.key !== 'ADMIN'), -]; diff --git a/src/app/modules/auth/pages/login/login.component.html b/src/app/modules/auth/pages/login/login.component.html index 499a1dd..b516201 100644 --- a/src/app/modules/auth/pages/login/login.component.html +++ b/src/app/modules/auth/pages/login/login.component.html @@ -1,15 +1,4 @@
- @if (!defaultRole) { -
- @for (role of centralAuthRoles; track role.key) { -
- - {{ role.title }} -
- } -
- } - ('SCHOOL'); diff --git a/src/app/modules/customers/components/form.component.ts b/src/app/modules/customers/components/form.component.ts index dbdb04d..22d2c82 100644 --- a/src/app/modules/customers/components/form.component.ts +++ b/src/app/modules/customers/components/form.component.ts @@ -1,3 +1,4 @@ +import { ToastService } from '@/core/services/toast.service'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; @@ -27,7 +28,10 @@ export class CustomerFormComponent { @Output() onSubmit = new EventEmitter(); private fb = inject(FormBuilder); - constructor(private service: CustomersService) { + constructor( + private service: CustomersService, + private toastService: ToastService, + ) { effect(() => { const v = this.visibleSignal(); if (!v) this.form.reset(); @@ -55,6 +59,10 @@ export class CustomerFormComponent { this.submitLoading.set(true); this.service.create(this.form.value as ICustomerRequest).subscribe({ next: (res) => { + this.toastService.success({ + text: `مشتری ${this.form.value.firstName} با موفقیت ایجاد شد`, + }); + this.close(); this.submitLoading.set(false); this.form.enable(); this.form.reset(); diff --git a/src/app/modules/customers/views/list.component.ts b/src/app/modules/customers/views/list.component.ts index 54eaf52..0ceed74 100644 --- a/src/app/modules/customers/views/list.component.ts +++ b/src/app/modules/customers/views/list.component.ts @@ -30,7 +30,6 @@ export class CustomersComponent { { field: 'isActive', header: 'فعال' }, { field: 'createdAt', header: 'تاریخ ایجاد' }, { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/inventories/components/form.component.ts b/src/app/modules/inventories/components/form.component.ts index 245f311..becc566 100644 --- a/src/app/modules/inventories/components/form.component.ts +++ b/src/app/modules/inventories/components/form.component.ts @@ -1,3 +1,4 @@ +import { ToastService } from '@/core/services/toast.service'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; @@ -27,7 +28,10 @@ export class InventoryFormComponent { @Output() onSubmit = new EventEmitter(); private fb = inject(FormBuilder); - constructor(private service: InventoriesService) { + constructor( + private service: InventoriesService, + private toastService: ToastService, + ) { effect(() => { const v = this.visibleSignal(); if (!v) this.form.reset(); @@ -49,6 +53,10 @@ export class InventoryFormComponent { this.submitLoading.set(true); this.service.create(this.form.value as IInventoryRequest).subscribe({ next: (res) => { + this.toastService.success({ + text: `انبار ${this.form.value.name} با موفقیت ایجاد شد`, + }); + this.close(); this.submitLoading.set(false); this.form.enable(); this.form.reset(); diff --git a/src/app/modules/inventories/views/list.component.ts b/src/app/modules/inventories/views/list.component.ts index 47780da..249e85a 100644 --- a/src/app/modules/inventories/views/list.component.ts +++ b/src/app/modules/inventories/views/list.component.ts @@ -22,7 +22,6 @@ export class InventoriesComponent { { field: 'name', header: 'نام' }, { field: 'location', header: 'موقعیت' }, { field: 'isActive', header: 'فعال' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/productBrands/components/form.component.html b/src/app/modules/productBrands/components/form.component.html index 615cd93..5db6daf 100644 --- a/src/app/modules/productBrands/components/form.component.html +++ b/src/app/modules/productBrands/components/form.component.html @@ -1,4 +1,11 @@ - + diff --git a/src/app/modules/productBrands/components/form.component.ts b/src/app/modules/productBrands/components/form.component.ts index 333c134..f364f3f 100644 --- a/src/app/modules/productBrands/components/form.component.ts +++ b/src/app/modules/productBrands/components/form.component.ts @@ -1,3 +1,4 @@ +import { ToastService } from '@/core/services/toast.service'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; @@ -27,7 +28,10 @@ export class ProductBrandFormComponent { @Output() onSubmit = new EventEmitter(); private fb = inject(FormBuilder); - constructor(private service: ProductBrandsService) { + constructor( + private service: ProductBrandsService, + private toastService: ToastService, + ) { effect(() => { const v = this.visibleSignal(); if (!v) this.form.reset(); @@ -36,8 +40,8 @@ export class ProductBrandFormComponent { form = this.fb.group({ name: [this.initialValues?.name || null, [Validators.required]], - description: [this.initialValues?.description || null, [Validators.required]], - imageUrl: [this.initialValues?.imageUrl || null, [Validators.required]], + description: [this.initialValues?.description || null], + imageUrl: [this.initialValues?.imageUrl || null], }); submitLoading = signal(false); @@ -49,6 +53,10 @@ export class ProductBrandFormComponent { this.submitLoading.set(true); this.service.create(this.form.value as IProductBrandRequest).subscribe({ next: (res) => { + this.toastService.success({ + text: `برند ${this.form.value.name} با موفقیت ایجاد شد`, + }); + this.close(); this.submitLoading.set(false); this.form.enable(); this.form.reset(); diff --git a/src/app/modules/productBrands/components/index.ts b/src/app/modules/productBrands/components/index.ts new file mode 100644 index 0000000..4078bb8 --- /dev/null +++ b/src/app/modules/productBrands/components/index.ts @@ -0,0 +1,2 @@ +export * from './form.component'; +export * from './select/select.component'; diff --git a/src/app/modules/productBrands/components/select/select.component.html b/src/app/modules/productBrands/components/select/select.component.html new file mode 100644 index 0000000..7f8d4c4 --- /dev/null +++ b/src/app/modules/productBrands/components/select/select.component.html @@ -0,0 +1,31 @@ +
+ + + @if (canInsert) { + +
+ +
+
+ } +
+
+ +
diff --git a/src/app/modules/productBrands/components/select/select.component.ts b/src/app/modules/productBrands/components/select/select.component.ts new file mode 100644 index 0000000..a329970 --- /dev/null +++ b/src/app/modules/productBrands/components/select/select.component.ts @@ -0,0 +1,49 @@ +import { Maybe } from '@/core'; +import { UikitFieldComponent } from '@/uikit'; +import { Component, Input, signal } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { Button } from 'primeng/button'; +import { Select } from 'primeng/select'; +import { IProductBrandResponse } from '../../models'; +import { ProductBrandsService } from '../../services/main.service'; +import { ProductBrandFormComponent } from '../form.component'; + +@Component({ + selector: 'product-brands-select-field', + templateUrl: './select.component.html', + imports: [ReactiveFormsModule, Select, UikitFieldComponent, Button, ProductBrandFormComponent], +}) +export class ProductBrandsSelectComponent { + @Input() control!: FormControl>; + @Input() canInsert: boolean = false; + + constructor(private service: ProductBrandsService) { + this.getData(); + } + + loading = signal(false); + brands = signal>(null); + + isOpenFormDialog = signal(false); + + getData() { + this.loading.set(true); + this.service.getAll().subscribe({ + next: (res) => { + this.brands.set(res.data); + this.loading.set(false); + }, + error: () => { + this.loading.set(false); + }, + }); + } + + refresh() { + this.getData(); + } + + onOpenForm() { + this.isOpenFormDialog.set(true); + } +} diff --git a/src/app/modules/productBrands/index.ts b/src/app/modules/productBrands/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modules/productBrands/views/list.component.ts b/src/app/modules/productBrands/views/list.component.ts index 032bebb..c795f8d 100644 --- a/src/app/modules/productBrands/views/list.component.ts +++ b/src/app/modules/productBrands/views/list.component.ts @@ -24,7 +24,6 @@ export class ProductBrandsComponent { { field: 'imageUrl', header: 'تصویر' }, { field: 'createdAt', header: 'تاریخ ایجاد' }, { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/productCategories/components/form.component.ts b/src/app/modules/productCategories/components/form.component.ts index 580d2e5..520b098 100644 --- a/src/app/modules/productCategories/components/form.component.ts +++ b/src/app/modules/productCategories/components/form.component.ts @@ -1,3 +1,4 @@ +import { ToastService } from '@/core/services/toast.service'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; @@ -27,7 +28,10 @@ export class ProductCategoryFormComponent { @Output() onSubmit = new EventEmitter(); private fb = inject(FormBuilder); - constructor(private service: ProductCategoriesService) { + constructor( + private service: ProductCategoriesService, + private toastService: ToastService, + ) { effect(() => { const v = this.visibleSignal(); if (!v) this.form.reset(); @@ -36,8 +40,8 @@ export class ProductCategoryFormComponent { form = this.fb.group({ name: [this.initialValues?.name || null, [Validators.required]], - description: [this.initialValues?.description || null, [Validators.required]], - imageUrl: [this.initialValues?.imageUrl || null, [Validators.required]], + description: [this.initialValues?.description || null], + imageUrl: [this.initialValues?.imageUrl || null], }); submitLoading = signal(false); @@ -49,6 +53,10 @@ export class ProductCategoryFormComponent { this.submitLoading.set(true); this.service.create(this.form.value as IProductCategoryRequest).subscribe({ next: (res) => { + this.toastService.success({ + text: `دسته‌بندی ${this.form.value.name} با موفقیت ایجاد شد`, + }); + this.close(); this.submitLoading.set(false); this.form.enable(); this.form.reset(); diff --git a/src/app/modules/productCategories/components/index.ts b/src/app/modules/productCategories/components/index.ts new file mode 100644 index 0000000..4078bb8 --- /dev/null +++ b/src/app/modules/productCategories/components/index.ts @@ -0,0 +1,2 @@ +export * from './form.component'; +export * from './select/select.component'; diff --git a/src/app/modules/productCategories/components/select/select.component.html b/src/app/modules/productCategories/components/select/select.component.html new file mode 100644 index 0000000..4d17d8a --- /dev/null +++ b/src/app/modules/productCategories/components/select/select.component.html @@ -0,0 +1,31 @@ +
+ + + @if (canInsert) { + +
+ +
+
+ } +
+
+ +
diff --git a/src/app/modules/productCategories/components/select/select.component.ts b/src/app/modules/productCategories/components/select/select.component.ts new file mode 100644 index 0000000..7fb2bae --- /dev/null +++ b/src/app/modules/productCategories/components/select/select.component.ts @@ -0,0 +1,49 @@ +import { Maybe } from '@/core'; +import { UikitFieldComponent } from '@/uikit'; +import { Component, Input, signal } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { Button } from 'primeng/button'; +import { Select } from 'primeng/select'; +import { IProductCategoryResponse } from '../../models'; +import { ProductCategoriesService } from '../../services/main.service'; +import { ProductCategoryFormComponent } from '../form.component'; + +@Component({ + selector: 'product-categories-select-field', + templateUrl: './select.component.html', + imports: [ReactiveFormsModule, Select, UikitFieldComponent, Button, ProductCategoryFormComponent], +}) +export class ProductCategoriesSelectComponent { + @Input() control!: FormControl>; + @Input() canInsert: boolean = false; + + constructor(private service: ProductCategoriesService) { + this.getData(); + } + + loading = signal(false); + brands = signal>(null); + + isOpenFormDialog = signal(false); + + getData() { + this.loading.set(true); + this.service.getAll().subscribe({ + next: (res) => { + this.brands.set(res.data); + this.loading.set(false); + }, + error: () => { + this.loading.set(false); + }, + }); + } + + refresh() { + this.getData(); + } + + onOpenForm() { + this.isOpenFormDialog.set(true); + } +} diff --git a/src/app/modules/productCategories/views/list.component.ts b/src/app/modules/productCategories/views/list.component.ts index 1b730e3..9630c82 100644 --- a/src/app/modules/productCategories/views/list.component.ts +++ b/src/app/modules/productCategories/views/list.component.ts @@ -23,7 +23,6 @@ export class ProductCategoriesComponent { { field: 'description', header: 'توضیحات' }, { field: 'imageUrl', header: 'تصویر' }, { field: 'createdAt', header: 'تاریخ ایجاد' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/products/components/form.component.html b/src/app/modules/products/components/form.component.html index 2caa167..b39c873 100644 --- a/src/app/modules/products/components/form.component.html +++ b/src/app/modules/products/components/form.component.html @@ -1,8 +1,10 @@ + + - + diff --git a/src/app/modules/products/components/form.component.ts b/src/app/modules/products/components/form.component.ts index 03b4697..f1d4d38 100644 --- a/src/app/modules/products/components/form.component.ts +++ b/src/app/modules/products/components/form.component.ts @@ -1,5 +1,8 @@ +import { ProductBrandsSelectComponent } from '@/modules/productBrands/components'; +import { ProductCategoriesSelectComponent } from '@/modules/productCategories/components'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { UikitFieldComponent } from '@/uikit'; import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { Dialog } from 'primeng/dialog'; @@ -9,7 +12,15 @@ import { ProductsService } from '../services/main.service'; @Component({ selector: 'product-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ + ReactiveFormsModule, + Dialog, + InputComponent, + FormFooterActionsComponent, + UikitFieldComponent, + ProductBrandsSelectComponent, + ProductCategoriesSelectComponent, + ], }) export class ProductFormComponent { @Input() initialValues?: IProductResponse; @@ -36,11 +47,10 @@ export class ProductFormComponent { form = this.fb.group({ name: [this.initialValues?.name || null, [Validators.required]], - description: [this.initialValues?.description || null, [Validators.required]], - productType: [this.initialValues?.productType || null, [Validators.required]], + description: [this.initialValues?.description || null], + // productType: [this.initialValues?.productType || null, [Validators.required]], brandId: [this.initialValues?.brandId || null, [Validators.required]], - categoryId: [this.initialValues?.categoryId || null, [Validators.required]], - supplierId: [this.initialValues?.supplierId || null, [Validators.required]], + categoryId: [this.initialValues?.categoryId || null], }); submitLoading = signal(false); diff --git a/src/app/modules/products/views/create.component.html b/src/app/modules/products/views/create.component.html new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modules/products/views/create.component.ts b/src/app/modules/products/views/create.component.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modules/products/views/list.component.ts b/src/app/modules/products/views/list.component.ts index d5442f1..711166f 100644 --- a/src/app/modules/products/views/list.component.ts +++ b/src/app/modules/products/views/list.component.ts @@ -27,7 +27,6 @@ export class ProductsComponent { { field: 'supplierId', header: 'تامین‌کننده' }, { field: 'createdAt', header: 'تاریخ ایجاد' }, { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/stores/views/list.component.ts b/src/app/modules/stores/views/list.component.ts index 37da2dd..476e7d7 100644 --- a/src/app/modules/stores/views/list.component.ts +++ b/src/app/modules/stores/views/list.component.ts @@ -24,7 +24,6 @@ export class StoresComponent { { field: 'isActive', header: 'فعال' }, { field: 'createdAt', header: 'تاریخ ایجاد' }, { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/suppliers/components/form.component.ts b/src/app/modules/suppliers/components/form.component.ts index aa2ee49..fcdbdb1 100644 --- a/src/app/modules/suppliers/components/form.component.ts +++ b/src/app/modules/suppliers/components/form.component.ts @@ -1,3 +1,4 @@ +import { ToastService } from '@/core/services/toast.service'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; @@ -27,7 +28,10 @@ export class SupplierFormComponent { @Output() onSubmit = new EventEmitter(); private fb = inject(FormBuilder); - constructor(private service: SuppliersService) { + constructor( + private service: SuppliersService, + private toastService: ToastService, + ) { effect(() => { const v = this.visibleSignal(); if (!v) this.form.reset(); @@ -55,6 +59,10 @@ export class SupplierFormComponent { this.submitLoading.set(true); this.service.create(this.form.value as ISupplierRequest).subscribe({ next: (res) => { + this.toastService.success({ + text: `تأمین‌کننده ${this.form.value.firstName} ${this.form.value.lastName} با موفقیت ایجاد شد`, + }); + this.close(); this.submitLoading.set(false); this.form.enable(); this.form.reset(); diff --git a/src/app/modules/suppliers/views/list.component.ts b/src/app/modules/suppliers/views/list.component.ts index 422e17b..1cd7c84 100644 --- a/src/app/modules/suppliers/views/list.component.ts +++ b/src/app/modules/suppliers/views/list.component.ts @@ -30,7 +30,6 @@ export class SuppliersComponent { { field: 'isActive', header: 'فعال' }, { field: 'createdAt', header: 'تاریخ ایجاد' }, { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, - { field: 'actions' }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/modules/users/components/form.component.html b/src/app/modules/users/components/form.component.html index 759f6b1..1d42f5a 100644 --- a/src/app/modules/users/components/form.component.html +++ b/src/app/modules/users/components/form.component.html @@ -3,7 +3,35 @@ - + + + + + رمز باید حداقل ۸ کاراکتر بوده و شامل حداقل یک حرف بزرگ، یک حرف کوچک، یک عدد و یک کاراکتر ویژه باشد. + + + + + + diff --git a/src/app/modules/users/components/form.component.ts b/src/app/modules/users/components/form.component.ts index f40c8c9..22b9a4f 100644 --- a/src/app/modules/users/components/form.component.ts +++ b/src/app/modules/users/components/form.component.ts @@ -1,10 +1,13 @@ -import { mobileValidator } from '@/core/validators'; +import { ToastService } from '@/core/services/toast.service'; +import { mobileValidator, MustMatch, password } from '@/core/validators'; +import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { UikitFieldComponent } from '@/uikit'; -import { Component, effect, EventEmitter, inject, Input, Output, signal } from '@angular/core'; +import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; import { Dialog } from 'primeng/dialog'; +import { Password } from 'primeng/password'; import { IUserRequest, IUserResponse } from '../models'; import { UsersService } from '../services/main.service'; @@ -14,9 +17,11 @@ import { UsersService } from '../services/main.service'; imports: [ ReactiveFormsModule, Dialog, - UikitFieldComponent, InputComponent, FormFooterActionsComponent, + CatalogRolesComponent, + UikitFieldComponent, + Password, ], }) export class UserFormComponent { @@ -35,23 +40,33 @@ export class UserFormComponent { @Output() onSubmit = new EventEmitter(); private fb = inject(FormBuilder); - constructor(private service: UsersService) { - effect(() => { - const v = this.visibleSignal(); - // this.visibleChange.emit(v); - if (!v) this.form.reset(); - }); + constructor( + private service: UsersService, + private toastService: ToastService, + ) { + // effect(() => { + // const v = this.visibleSignal(); + // // this.visibleChange.emit(v); + // if (!v) this.form.reset(); + // }); } - form = this.fb.group({ - firstName: [this.initialValues?.firstName || null, [Validators.required]], - lastName: [this.initialValues?.lastName || null, [Validators.required]], - mobileNumber: [ - this.initialValues?.mobileNumber || null, - [Validators.required, mobileValidator()], - ], - roleId: [this.initialValues?.roleId || null, [Validators.required]], - }); + form = this.fb.group( + { + firstName: [this.initialValues?.firstName || '', [Validators.required]], + lastName: [this.initialValues?.lastName || '', [Validators.required]], + mobileNumber: [ + this.initialValues?.mobileNumber || '', + [Validators.required, mobileValidator()], + ], + roleId: [this.initialValues?.role.id || null, [Validators.required]], + password: ['', [Validators.required, password()]], + confirmPassword: ['', [Validators.required]], + }, + { + validators: [MustMatch('password', 'confirmPassword')], + }, + ); submitLoading = signal(false); @@ -60,8 +75,13 @@ export class UserFormComponent { if (this.form.valid) { this.form.disable(); this.submitLoading.set(true); - this.service.create(this.form.value as IUserRequest).subscribe({ + const { confirmPassword, ...rest } = this.form.value; + this.service.create(rest as IUserRequest).subscribe({ next: (res) => { + this.toastService.success({ + text: `کاربر ${this.form.value.firstName} با موفقیت ایجاد شد`, + }); + this.close(); this.submitLoading.set(false); this.form.enable(); this.form.reset(); diff --git a/src/app/modules/users/models/io.d.ts b/src/app/modules/users/models/io.d.ts index dad19ee..4d4677a 100644 --- a/src/app/modules/users/models/io.d.ts +++ b/src/app/modules/users/models/io.d.ts @@ -1,9 +1,11 @@ +import { IRoleResponse } from '@/shared/catalog/roles'; + export interface IUserRawResponse { mobileNumber: string; firstName: string; lastName: string; id: number; - roleId: number; + role: IRoleResponse; } export interface IUserResponse extends IUserRawResponse {} diff --git a/src/app/modules/users/views/list.component.html b/src/app/modules/users/views/list.component.html index 5b6bfaf..5e9afbd 100644 --- a/src/app/modules/users/views/list.component.html +++ b/src/app/modules/users/views/list.component.html @@ -10,6 +10,9 @@ [showDetails]="true" [showAdd]="true" (onAdd)="openAddForm()" -/> - +> + + + + diff --git a/src/app/modules/users/views/list.component.ts b/src/app/modules/users/views/list.component.ts index 8c972ab..5985482 100644 --- a/src/app/modules/users/views/list.component.ts +++ b/src/app/modules/users/views/list.component.ts @@ -1,8 +1,9 @@ +import { CatalogRoleTagComponent } from '@/shared/catalog/roles'; import { IColumn, PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; -import { Component, signal } from '@angular/core'; +import { Component, signal, TemplateRef, ViewChild } from '@angular/core'; import { UserFormComponent } from '../components/form.component'; import { IUserResponse } from '../models'; import { UsersService } from '../services/main.service'; @@ -10,20 +11,25 @@ import { UsersService } from '../services/main.service'; @Component({ selector: 'app-users', templateUrl: './list.component.html', - imports: [PageDataListComponent, UserFormComponent], + imports: [PageDataListComponent, UserFormComponent, CatalogRoleTagComponent], }) export class UsersComponent { constructor(private userService: UsersService) { this.getData(); } + @ViewChild('role', { static: true }) roleTpl!: TemplateRef; columns = [ { field: 'id', header: 'شناسه' }, { field: 'firstName', header: 'نام' }, { field: 'lastName', header: 'نام خانوادگی' }, { field: 'mobileNumber', header: 'شماره موبایل' }, - { field: 'roleId', header: 'نقش' }, - { field: 'actions' }, + // { field: 'role', header: 'نقش', customDataModel: this.roleTpl }, + { + field: 'role', + header: 'نقش', + customDataModel: (item: IUserResponse) => item.role.name ?? '-', + }, ] as IColumn[]; loading = signal(false); diff --git a/src/app/shared/catalog/roles/components/index.ts b/src/app/shared/catalog/roles/components/index.ts new file mode 100644 index 0000000..ee993c0 --- /dev/null +++ b/src/app/shared/catalog/roles/components/index.ts @@ -0,0 +1,2 @@ +export * from './select.component'; +export * from './tag.component'; diff --git a/src/app/shared/catalog/roles/components/select.component.html b/src/app/shared/catalog/roles/components/select.component.html new file mode 100644 index 0000000..be80c62 --- /dev/null +++ b/src/app/shared/catalog/roles/components/select.component.html @@ -0,0 +1,12 @@ + + + diff --git a/src/app/shared/catalog/roles/components/select.component.ts b/src/app/shared/catalog/roles/components/select.component.ts new file mode 100644 index 0000000..cae8ba1 --- /dev/null +++ b/src/app/shared/catalog/roles/components/select.component.ts @@ -0,0 +1,38 @@ +import { Maybe } from '@/core'; +import { UikitFieldComponent } from '@/uikit'; +import { Component, Input, signal } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { Select } from 'primeng/select'; +import { IRoleResponse } from '../models'; +import { RolesService } from '../services'; + +@Component({ + selector: 'catalog-roles-select', + templateUrl: './select.component.html', + imports: [UikitFieldComponent, Select, ReactiveFormsModule], +}) +export class CatalogRolesComponent { + @Input() control!: FormControl>; + + constructor(private service: RolesService) { + this.getData(); + } + + roles = signal([]); + loading = signal(false); + + private getData() { + this.loading.set(true); + return this.service.getAll().subscribe({ + next: (res) => { + this.roles.set(res); + }, + error: () => { + this.roles.set([]); + }, + complete: () => { + this.loading.set(false); + }, + }); + } +} diff --git a/src/app/shared/catalog/roles/components/tag.component.html b/src/app/shared/catalog/roles/components/tag.component.html new file mode 100644 index 0000000..0ea9250 --- /dev/null +++ b/src/app/shared/catalog/roles/components/tag.component.html @@ -0,0 +1 @@ +{{ roleTitle() }} diff --git a/src/app/shared/catalog/roles/components/tag.component.ts b/src/app/shared/catalog/roles/components/tag.component.ts new file mode 100644 index 0000000..451711d --- /dev/null +++ b/src/app/shared/catalog/roles/components/tag.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from '@angular/core'; +import { IRoleResponse } from '../models'; + +@Component({ + selector: 'catalog-role-tag', + templateUrl: './tag.component.html', +}) +export class CatalogRoleTagComponent { + @Input() role!: IRoleResponse; + + constructor() {} + + roleTitle() { + return this.role.name; + } +} diff --git a/src/app/shared/catalog/roles/constants/apiRoutes/index.ts b/src/app/shared/catalog/roles/constants/apiRoutes/index.ts new file mode 100644 index 0000000..77d6a49 --- /dev/null +++ b/src/app/shared/catalog/roles/constants/apiRoutes/index.ts @@ -0,0 +1,5 @@ +const baseUrl = '/api/v1/roles'; + +export const ROLES_API_ROUTES = { + list: () => `${baseUrl}`, +}; diff --git a/src/app/shared/catalog/roles/constants/index.ts b/src/app/shared/catalog/roles/constants/index.ts new file mode 100644 index 0000000..b853783 --- /dev/null +++ b/src/app/shared/catalog/roles/constants/index.ts @@ -0,0 +1 @@ +export * from './apiRoutes'; diff --git a/src/app/shared/catalog/roles/index.ts b/src/app/shared/catalog/roles/index.ts new file mode 100644 index 0000000..5872a53 --- /dev/null +++ b/src/app/shared/catalog/roles/index.ts @@ -0,0 +1,4 @@ +export * from './components'; +export * from './constants'; +export * from './models'; +export * from './services'; diff --git a/src/app/shared/catalog/roles/models/index.ts b/src/app/shared/catalog/roles/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/shared/catalog/roles/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/shared/catalog/roles/models/io.ts b/src/app/shared/catalog/roles/models/io.ts new file mode 100644 index 0000000..2670050 --- /dev/null +++ b/src/app/shared/catalog/roles/models/io.ts @@ -0,0 +1,6 @@ +export interface IRoleRawResponse { + id: number; + name: string; + description: string; +} +export interface IRoleResponse extends IRoleRawResponse {} diff --git a/src/app/shared/catalog/roles/services/index.ts b/src/app/shared/catalog/roles/services/index.ts new file mode 100644 index 0000000..32ea052 --- /dev/null +++ b/src/app/shared/catalog/roles/services/index.ts @@ -0,0 +1 @@ +export * from './main.service'; diff --git a/src/app/shared/catalog/roles/services/main.service.ts b/src/app/shared/catalog/roles/services/main.service.ts new file mode 100644 index 0000000..e1da5fb --- /dev/null +++ b/src/app/shared/catalog/roles/services/main.service.ts @@ -0,0 +1,19 @@ +import { IPaginatedResponse } from '@/core/models/service.model'; +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { ROLES_API_ROUTES } from '../constants'; +import { IRoleRawResponse, IRoleResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class RolesService { + constructor(private http: HttpClient) {} + + private apiRoutes = ROLES_API_ROUTES; + + getAll(): Observable { + return this.http + .get>(this.apiRoutes.list()) + .pipe(map((response) => response.data as IRoleResponse[])); + } +}