This commit is contained in:
2026-03-10 13:36:45 +03:30
parent 45d48f79dc
commit b379787002
208 changed files with 2328 additions and 2510 deletions
@@ -0,0 +1,23 @@
import { Component, EventEmitter, Input, Output, signal } from '@angular/core';
@Component({
selector: 'abstract-dialog',
template: '',
})
export abstract class AbstractDialog {
@Input()
set visible(v: boolean) {
this.visibleSignal.set(!!v);
this.visibleChange.emit(!!v);
}
get visible() {
return this.visibleSignal();
}
private visibleSignal = signal(false);
close() {
this.visible = false;
}
@Output() visibleChange = new EventEmitter<boolean>();
}
@@ -0,0 +1,42 @@
import { Component, effect, EventEmitter, Input, Output, signal } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AbstractForm } from './abstract-form';
@Component({
selector: 'abstract-form-dialog',
template: '',
imports: [ReactiveFormsModule],
})
export abstract class AbstractFormDialog<Request, Response> extends AbstractForm<
Request,
Response
> {
@Input()
set visible(v: boolean) {
this.visibleSignal.set(!!v);
this.visibleChange.emit(!!v);
}
get visible() {
return this.visibleSignal();
}
private visibleSignal = signal(false);
@Output() visibleChange = new EventEmitter<boolean>();
constructor() {
super();
effect(() => {
const v = this.visibleSignal();
if (!v) this.form.reset(this.defaultValues);
});
}
override onSuccess(response: Response): void {
this.close();
}
override close() {
this.onClose.emit();
this.visibleChange.emit(false);
}
}
@@ -0,0 +1,77 @@
import { ToastService } from '@/core/services/toast.service';
import { Component, EventEmitter, inject, Input, Output, signal } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { finalize, Observable } from 'rxjs';
@Component({
selector: 'abstract-form-dialog',
template: '',
imports: [ReactiveFormsModule],
})
export abstract class AbstractForm<
FormInterface,
Response,
InitialValue = Response,
RequestPayload extends FormInterface = FormInterface,
> {
@Input() initialValues?: InitialValue;
@Input() editMode?: boolean;
@Output() onSubmit = new EventEmitter<Response>();
@Output() onClose = new EventEmitter<void>();
protected readonly fb = inject(FormBuilder);
private readonly toastService = inject(ToastService);
constructor() {}
abstract form: ReturnType<FormBuilder['group']>;
showSuccessMessage: boolean = true;
successMessage = 'عملیات با موفقیت انجام شد';
defaultValues: Partial<FormInterface> = {};
abstract submitForm(payload: FormInterface): Observable<Response> | void;
prepareRequestPayload(payload: FormInterface): RequestPayload {
// @ts-ignore
return payload;
}
ngOnChanges() {
this.form.patchValue(this.initialValues ?? {});
}
onSuccess(response: Response): void {}
submitLoading = signal(false);
submit() {
this.form.markAllAsTouched();
if (this.form.valid) {
// this.form.disable();
this.submitLoading.set(true);
this.submitForm(this.prepareRequestPayload(this.form.value))
?.pipe(
finalize(() => {
this.submitLoading.set(false);
// this.form.enable();
}),
)
.subscribe((res) => {
this.onSuccess(res);
if (this.showSuccessMessage) {
this.toastService.success({
text: this.successMessage,
});
}
this.onSubmit.emit(res);
// this.form.reset();
});
}
}
close() {
this.onClose.emit();
}
}
@@ -0,0 +1,65 @@
import { Maybe } from '@/core';
import { IPaginatedResponse } from '@/core/models/service.model';
import { Component, signal } from '@angular/core';
import { Observable } from 'rxjs';
import { IColumn } from '../components/pageDataList/page-data-list.component';
@Component({
selector: 'abstract-list',
template: '',
})
export abstract class AbstractList<ResponseModel, RequestParams = null> {
columns = [] as IColumn[];
loading = signal(false);
items = signal<ResponseModel[]>([]);
visibleForm = signal(false);
requestPayload = signal<Maybe<RequestParams>>(null);
selectedItemForEdit = signal<Maybe<ResponseModel>>(null);
editMode = signal(false);
ngOnInit() {
this.setColumns();
this.getData();
}
refresh() {
this.getData();
}
getData() {
this.loading.set(true);
this.getDataRequest(this.requestPayload())?.subscribe((res) => {
this.loading.set(false);
this.items.set(res.data);
});
}
openAddForm() {
this.selectedItemForEdit.set(null);
this.editMode.set(false);
this.visibleForm.set(true);
}
abstract getDataRequest(
payload: Maybe<RequestParams>,
): Observable<IPaginatedResponse<ResponseModel>> | void;
abstract setColumns(): void;
// constructor() {
// if (this.editable) {
// if (!this.ed) {
// throw new Error('onEdit output must be bound when editable is true');
// }
// }
// }
onEditClick(selectedItem: ResponseModel) {
this.selectedItemForEdit.set(selectedItem);
this.editMode.set(true);
this.visibleForm.set(true);
}
onCloseEdit() {
this.visibleForm.set(false);
}
}
@@ -0,0 +1,99 @@
import { Maybe } from '@/core';
import { Component, EventEmitter, Input, model, Output, signal } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
export interface ISelectItem {
id: number;
name: string;
}
@Component({
selector: 'abstract-select-field',
template: '',
})
export abstract class AbstractSelectComponent<T = ISelectItem, serviceResponse = T[]> {
@Input() control = new FormControl<Maybe<number | T>>(null);
@Input() canInsert: boolean = false;
@Input() showLabel: boolean = true;
@Input() showErrors: boolean = true;
@Input() showClear: boolean = true;
@Input() isFullDataOptionValue: boolean = false;
@Input() optionValue = 'id';
@Output() onChange = new EventEmitter<Maybe<T>>();
@Output() onSetDefaultDataItem = new EventEmitter<T>();
loading = signal(false);
items = signal<Maybe<T[]>>(null);
isOpenFormDialog = signal(false);
value = model<Maybe<T>>(null);
constructor() {
if (this.value()) {
this.control.setValue(this.value());
}
this.control.valueChanges.subscribe((val) => {
this.value.set(val as Maybe<T>);
});
}
abstract getDataService(): Observable<serviceResponse>;
getData() {
this.loading.set(true);
this.getDataService().subscribe({
next: (res) => {
if (Array.isArray(res)) {
this.items.set(res);
// @ts-ignore
} else if ('data' in res) {
// @ts-ignore
this.items.set(res.data);
}
const defaultValue = this.control.value;
if (defaultValue) {
const selectedItem = this.items()?.find(
(item) => (item as Record<string, any>)[this.optionValue] == defaultValue,
) as T;
if (selectedItem) {
this.onSetDefaultDataItem.emit(selectedItem);
}
}
this.loading.set(false);
},
error: () => {
this.loading.set(false);
},
});
}
setDefaultDataItem(item: T) {
this.onSetDefaultDataItem.emit(item);
}
refresh() {
this.getData();
}
onOpenForm() {
this.isOpenFormDialog.set(true);
}
get selectOptionValue() {
if (this.isFullDataOptionValue) {
return undefined;
}
return this.optionValue;
}
change(value: Maybe<T>) {
if (typeof value !== 'object') {
// @ts-ignore
this.onChange.emit(this.items()?.find((item) => item[this.optionValue] === value));
} else {
this.onChange.emit(value);
}
}
}
+4
View File
@@ -0,0 +1,4 @@
export * from './abstract-form';
export * from './abstract-form-dialog.component';
export * from './abstract-list';
export * from './abstract-select.component';