feat: add logo image and RTL support styles

- Added logo image to assets.
- Implemented RTL support styles in rtlSupport.scss for various components including breadcrumb, datatable, and tree node toggle button.

chore: configure environment files for production, staging, and development

- Created environment.prod.ts for production settings.
- Created environment.staging.ts for staging settings.
- Created environment.ts for local development settings.

feat: define custom theme preset

- Added presets.ts to define a custom theme preset using PrimeUIX with specific component styles and semantic colors.

chore: set up proxy configuration for local development
This commit is contained in:
2025-12-04 21:07:18 +03:30
parent c58210cdbd
commit 07fec02ea1
164 changed files with 20175 additions and 735 deletions
@@ -0,0 +1,33 @@
<div class="flex flex-col gap-3">
<uikit-field [label]="label" [control]="control" class="w-full">
<p-inputGroup>
<input
pInputText
[formControl]="control"
[placeholder]="placeholder"
[readonly]="searchedLoading()"
[minLength]="6"
[maxLength]="6"
[invalid]="control.touched && (control.invalid || !searchedItem())"
/>
@if (searchedLoading()) {
<p-inputGroupAddon>
<p-progressSpinner class="!w-4 !h-4"></p-progressSpinner>
</p-inputGroupAddon>
}
</p-inputGroup>
</uikit-field>
@if (searchedLoading()) {
<div class="flex items-center justify-center">
<p-skeleton />
</div>
} @else if (searchedItem()) {
<p-message severity="success" [closable]="false" variant="text" icon="pi pi-check-circle">
{{ foundedMessage(searchedItem()!) }}
</p-message>
} @else if (control.value!.length === 6 && itemNotFound()) {
<p-message severity="error" [closable]="false" variant="text" icon="pi pi-times-circle">
{{ notFoundMessage }}
</p-message>
}
</div>
@@ -0,0 +1,67 @@
import { Maybe } from '@/core';
import { Component, EventEmitter, Input, Output, signal } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { InputGroup } from 'primeng/inputgroup';
import { InputGroupAddon } from 'primeng/inputgroupaddon';
import { InputTextModule } from 'primeng/inputtext';
import { Message } from 'primeng/message';
import { ProgressSpinner } from 'primeng/progressspinner';
import { Skeleton } from 'primeng/skeleton';
import { debounceTime, filter, Observable } from 'rxjs';
import { UikitFieldComponent } from '../uikit-field.component';
@Component({
selector: 'uikit-search-field',
templateUrl: './search-field.component.html',
imports: [
UikitFieldComponent,
InputGroup,
InputGroupAddon,
ProgressSpinner,
Skeleton,
Message,
InputTextModule,
ReactiveFormsModule,
],
})
export class UikitSearchFieldComponent<T> {
@Input() label!: string;
@Input() placeholder: string = 'جستجو...';
@Input() debounceTime: number = 300;
@Input() notFoundMessage: string = 'موردی یافت نشد.';
@Input() foundedMessage: (item: T) => string = () => 'مورد یافت شد.';
@Input() search!: (query: string) => Observable<Maybe<T>>;
@Output() searchResult = new EventEmitter<Maybe<T>>();
control = new FormControl<string>('');
constructor() {
this.control.valueChanges
.pipe(
debounceTime(this.debounceTime),
filter((val) => !!val && val.length === 6),
)
.subscribe(() => {
this.searchedLoading.set(true);
this.search(this.control.value!).subscribe({
next: (item) => {
this.searchedItem.set(item);
this.searchedLoading.set(false);
this.itemNotFound.set(!item);
this.searchResult.emit(item);
},
error: () => {
this.searchedLoading.set(false);
this.itemNotFound.set(true);
this.searchResult.emit(null);
},
});
});
}
submitLoading = signal(false);
searchedLoading = signal(false);
searchedItem = signal<Maybe<T>>(null);
itemNotFound = signal(false);
}