From abf53bac0331b3f95928bd6d48a6822f25edf81c Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Sat, 6 Dec 2025 17:48:16 +0330 Subject: [PATCH] feat: implement product creation and update features with form handling and routing --- .prettierrc.json | 3 +- .../products/components/form.component.ts | 6 +- .../fullForm/full-form.component.html | 55 ++++++++++++ .../fullForm/full-form.component.ts | 72 +++++++++++++++ .../products/constants/routes/index.ts | 20 ++++- src/app/modules/products/models/io.d.ts | 28 +++--- src/app/modules/products/models/others.ts | 8 ++ .../modules/products/services/main.service.ts | 3 + .../products/views/create.component.html | 1 + .../products/views/create.component.ts | 37 ++++++++ src/app/modules/products/views/index.ts | 2 + .../products/views/list.component.html | 1 + .../modules/products/views/list.component.ts | 40 +++++++-- .../products/views/single.component.html | 85 +++++++++++++++++- .../products/views/single.component.ts | 90 ++++++++++++++++++- .../products/views/update.component.html | 9 ++ .../products/views/update.component.ts | 57 ++++++++++++ .../barcodeInput/barcode-input.component.html | 14 +++ .../barcodeInput/barcode-input.component.ts | 27 ++++++ .../page-data-list.component.html | 4 +- .../pageDataList/page-data-list.component.ts | 21 +++-- src/presets.ts | 71 ++++++++++++--- 22 files changed, 606 insertions(+), 48 deletions(-) create mode 100644 src/app/modules/products/components/fullForm/full-form.component.html create mode 100644 src/app/modules/products/components/fullForm/full-form.component.ts create mode 100644 src/app/modules/products/models/others.ts create mode 100644 src/app/modules/products/views/update.component.html create mode 100644 src/app/modules/products/views/update.component.ts create mode 100644 src/app/shared/components/barcodeInput/barcode-input.component.html create mode 100644 src/app/shared/components/barcodeInput/barcode-input.component.ts diff --git a/.prettierrc.json b/.prettierrc.json index df8e4b0..4e6fc5a 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -33,7 +33,8 @@ } ], "plugins": [ - "prettier-plugin-organize-imports" + "prettier-plugin-organize-imports", + "prettier-plugin-tailwindcss" ], "printWidth": 100, "semi": true, diff --git a/src/app/modules/products/components/form.component.ts b/src/app/modules/products/components/form.component.ts index f1d4d38..38ffb8a 100644 --- a/src/app/modules/products/components/form.component.ts +++ b/src/app/modules/products/components/form.component.ts @@ -2,7 +2,6 @@ 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'; @@ -17,7 +16,6 @@ import { ProductsService } from '../services/main.service'; Dialog, InputComponent, FormFooterActionsComponent, - UikitFieldComponent, ProductBrandsSelectComponent, ProductCategoriesSelectComponent, ], @@ -49,8 +47,8 @@ export class ProductFormComponent { name: [this.initialValues?.name || 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], + brandId: [this.initialValues?.brand?.id || null, [Validators.required]], + categoryId: [this.initialValues?.category?.id || null], }); submitLoading = signal(false); diff --git a/src/app/modules/products/components/fullForm/full-form.component.html b/src/app/modules/products/components/fullForm/full-form.component.html new file mode 100644 index 0000000..7b64565 --- /dev/null +++ b/src/app/modules/products/components/fullForm/full-form.component.html @@ -0,0 +1,55 @@ +
+
+
+ +

{{ initialData ? "ویرایش محصول" : "ایجاد محصول جدید" }}

+
+
+ + +
+
+
+
+ +
+ + + + + + + + +
+
+
+
+ +
+ + +
+ +
+ + +
+
+
+
+
diff --git a/src/app/modules/products/components/fullForm/full-form.component.ts b/src/app/modules/products/components/fullForm/full-form.component.ts new file mode 100644 index 0000000..3572105 --- /dev/null +++ b/src/app/modules/products/components/fullForm/full-form.component.ts @@ -0,0 +1,72 @@ +import { ProductBrandsSelectComponent } from '@/modules/productBrands/components'; +import { ProductCategoriesSelectComponent } from '@/modules/productCategories/components'; +import { InputComponent } from '@/shared/components'; +import { BarcodeInputComponent } from '@/shared/components/barcodeInput/barcode-input.component'; +import { UikitFieldComponent } from '@/uikit'; +import { Component, effect, EventEmitter, inject, Input, Output } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { RouterLink } from '@angular/router'; +import { Button, ButtonDirective } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { TextareaModule } from 'primeng/textarea'; +import { IProductRequest, IProductResponse } from '../../models'; + +@Component({ + selector: 'product-full-form', + templateUrl: './full-form.component.html', + imports: [ + Button, + RouterLink, + ButtonDirective, + Card, + ReactiveFormsModule, + InputComponent, + UikitFieldComponent, + TextareaModule, + BarcodeInputComponent, + ProductBrandsSelectComponent, + ProductCategoriesSelectComponent, + ], +}) +export class ProductFullFormComponent { + @Input() initialData?: IProductResponse; + @Input() loading: boolean = false; + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor() { + effect(() => { + if (this.initialData) { + this.form.patchValue({ + ...this.initialData, + brandId: this.initialData.brand?.id || null, + categoryId: this.initialData.category?.id || null, + }); + } else { + this.form.reset(); + } + }); + } + + form = this.fb.group({ + name: [this.initialData?.name || '', [Validators.required]], + brandId: [this.initialData?.brand?.id || null, [Validators.required]], + sku: [this.initialData?.sku || null], + barcode: [this.initialData?.barcode || null], + description: [this.initialData?.description || null], + categoryId: [this.initialData?.category?.id || null], + imageUrl: [null], + basePrice: [this.initialData?.basePrice || 0, [Validators.required, Validators.min(0)]], + }); + + get backRoute() { + return this.initialData ? ['/products', this.initialData.id] : ['/products']; + } + + submit() { + this.form.markAsTouched(); + if (this.form.valid) { + this.onSubmit.emit(this.form.value as unknown as IProductRequest); + } + } +} diff --git a/src/app/modules/products/constants/routes/index.ts b/src/app/modules/products/constants/routes/index.ts index f63d3cb..7ea96de 100644 --- a/src/app/modules/products/constants/routes/index.ts +++ b/src/app/modules/products/constants/routes/index.ts @@ -1,7 +1,7 @@ import { NamedRoutes } from '@/core'; import { Routes } from '@angular/router'; -export type TProductsRouteNames = 'products' | 'product'; +export type TProductsRouteNames = 'products' | 'product' | 'create' | 'update'; export const productsNamedRoutes: NamedRoutes = { products: { @@ -12,6 +12,15 @@ export const productsNamedRoutes: NamedRoutes = { pagePath: () => '/products', }, }, + create: { + path: 'products/create', + loadComponent: () => + import('../../views/create.component').then((m) => m.CreateProductComponent), + meta: { + title: 'ایجاد محصول جدید', + pagePath: () => '/products/create', + }, + }, product: { path: 'products/:productId', loadComponent: () => import('../../views/single.component').then((m) => m.ProductComponent), @@ -20,6 +29,15 @@ export const productsNamedRoutes: NamedRoutes = { pagePath: () => '/products/:productId', }, }, + update: { + path: 'products/:productId/update', + loadComponent: () => + import('../../views/update.component').then((m) => m.UpdateProductComponent), + meta: { + title: 'محصول', + pagePath: () => '/products/:productId/update', + }, + }, }; export const PRODUCTS_ROUTES: Routes = Object.values(productsNamedRoutes); diff --git a/src/app/modules/products/models/io.d.ts b/src/app/modules/products/models/io.d.ts index 8c64896..e858a3c 100644 --- a/src/app/modules/products/models/io.d.ts +++ b/src/app/modules/products/models/io.d.ts @@ -1,23 +1,31 @@ +import { Maybe } from '@/core'; +import { IProductBrand, IProductCategory } from './others'; + export interface IProductRawResponse { id: number; name: string; - description: string; - productType: string; + description?: string; + barcode?: Maybe; + sku?: Maybe; + // productType: string; + brand?: IProductBrand; + category?: IProductCategory; + // supplierId: number; + basePrice?: string; createdAt: Date; updatedAt: Date; - deletedAt: Date; - brandId: number; - categoryId: number; - supplierId: number; + deletedAt?: Maybe; } export interface IProductResponse extends IProductRawResponse {} export interface IProductRequest { name: string; - description: string; - productType: string; + // productType: string; brandId: number; - categoryId: number; - supplierId: number; + description: Maybe; + barcode: Maybe; + sku: Maybe; + categoryId: Maybe; + // supplierId: number; } diff --git a/src/app/modules/products/models/others.ts b/src/app/modules/products/models/others.ts new file mode 100644 index 0000000..6e9b9fd --- /dev/null +++ b/src/app/modules/products/models/others.ts @@ -0,0 +1,8 @@ +export interface IProductCategory { + id: number; + name: string; +} +export interface IProductBrand { + id: number; + name: string; +} diff --git a/src/app/modules/products/services/main.service.ts b/src/app/modules/products/services/main.service.ts index 90cb791..2855d4a 100644 --- a/src/app/modules/products/services/main.service.ts +++ b/src/app/modules/products/services/main.service.ts @@ -22,4 +22,7 @@ export class ProductsService { create(data: IProductRequest): Observable { return this.http.post(this.apiRoutes.list(), data); } + update(productId: string, data: Partial): Observable { + return this.http.patch(this.apiRoutes.single(productId), data); + } } diff --git a/src/app/modules/products/views/create.component.html b/src/app/modules/products/views/create.component.html index e69de29..184b3bb 100644 --- a/src/app/modules/products/views/create.component.html +++ b/src/app/modules/products/views/create.component.html @@ -0,0 +1 @@ + diff --git a/src/app/modules/products/views/create.component.ts b/src/app/modules/products/views/create.component.ts index e69de29..0e04880 100644 --- a/src/app/modules/products/views/create.component.ts +++ b/src/app/modules/products/views/create.component.ts @@ -0,0 +1,37 @@ +import { ToastService } from '@/core/services/toast.service'; +import { Component, signal } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; +import { TextareaModule } from 'primeng/textarea'; +import { ProductFullFormComponent } from '../components/fullForm/full-form.component'; +import { IProductRequest } from '../models'; +import { ProductsService } from '../services/main.service'; + +@Component({ + selector: 'app-product-create', + templateUrl: './create.component.html', + imports: [TextareaModule, ReactiveFormsModule, ProductFullFormComponent], +}) +export class CreateProductComponent { + constructor( + private service: ProductsService, + private toastService: ToastService, + private router: Router, + ) {} + + submitLoading = signal(false); + + submit(payload: IProductRequest) { + this.submitLoading.set(true); + this.service.create(payload).subscribe({ + next: (res) => { + this.toastService.success({ text: `محصول ${payload.name} با موفقیت ایجاد شد` }); + this.router.navigate(['products', res.id]); + this.submitLoading.set(false); + }, + error: (err) => { + this.submitLoading.set(false); + }, + }); + } +} diff --git a/src/app/modules/products/views/index.ts b/src/app/modules/products/views/index.ts index bc9dfc9..1052f9e 100644 --- a/src/app/modules/products/views/index.ts +++ b/src/app/modules/products/views/index.ts @@ -1,2 +1,4 @@ +export * from './create.component'; export * from './list.component'; export * from './single.component'; +export * from './update.component'; diff --git a/src/app/modules/products/views/list.component.html b/src/app/modules/products/views/list.component.html index 48f9b55..1be9c7c 100644 --- a/src/app/modules/products/views/list.component.html +++ b/src/app/modules/products/views/list.component.html @@ -9,6 +9,7 @@ [loading]="loading()" [showDetails]="true" (onAdd)="openAddForm()" + (onDetails)="toDetails($event)" /> diff --git a/src/app/modules/products/views/list.component.ts b/src/app/modules/products/views/list.component.ts index 711166f..299d1ad 100644 --- a/src/app/modules/products/views/list.component.ts +++ b/src/app/modules/products/views/list.component.ts @@ -3,7 +3,9 @@ import { PageDataListComponent, } from '@/shared/components/pageDataList/page-data-list.component'; import { Component, signal } from '@angular/core'; +import { Router } from '@angular/router'; import { ProductFormComponent } from '../components/form.component'; +import { productsNamedRoutes } from '../constants'; import { IProductResponse } from '../models'; import { ProductsService } from '../services/main.service'; @@ -13,20 +15,32 @@ import { ProductsService } from '../services/main.service'; imports: [PageDataListComponent, ProductFormComponent], }) export class ProductsComponent { - constructor(private productService: ProductsService) { + constructor( + private productService: ProductsService, + private router: Router, + ) { this.getData(); } columns = [ { field: 'id', header: 'شناسه' }, { field: 'name', header: 'نام' }, - { field: 'description', header: 'توضیحات' }, - { field: 'productType', header: 'نوع محصول' }, - { field: 'brandId', header: 'برند' }, - { field: 'categoryId', header: 'دسته‌بندی' }, - { field: 'supplierId', header: 'تامین‌کننده' }, - { field: 'createdAt', header: 'تاریخ ایجاد' }, - { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, + { + field: 'brand', + header: 'برند', + customDataModel(item) { + return item.brand?.name || '-'; + }, + }, + { + field: 'category', + header: 'دسته‌بندی', + customDataModel(item) { + return item.category?.name || '-'; + }, + }, + { field: 'createdAt', header: 'تاریخ ایجاد', type: 'date' }, + { field: 'updatedAt', header: 'تاریخ به‌روزرسانی', type: 'date' }, ] as IColumn[]; loading = signal(false); @@ -46,6 +60,14 @@ export class ProductsComponent { } openAddForm() { - this.visibleForm.set(true); + const pagePathFn = productsNamedRoutes.create.meta.pagePath; + if (pagePathFn) { + this.router.navigateByUrl(pagePathFn({})); + } + // this.visibleForm.set(true); + } + + toDetails(product: IProductResponse) { + this.router.navigate(['/products', product.id]); } } diff --git a/src/app/modules/products/views/single.component.html b/src/app/modules/products/views/single.component.html index 7144e26..29e458a 100644 --- a/src/app/modules/products/views/single.component.html +++ b/src/app/modules/products/views/single.component.html @@ -1 +1,84 @@ -
+
+
+
+
+ +

{{ product()?.name }}

+
+
+
+ + +
+
+
+
+ +
+
+ +
+
+
+
+
+ @for (item of topBarCardDetails; track $index) { + +
+
+ +
+
+ {{ item.label }} + {{ item.value }} +
+
+
+ } +
+ + +
+
+ توضیحات +

{{ product()?.description || "بدون توضیحات" }}

+
+
+ +
+ دسته‌بندی +

{{ product()?.category?.name || "بدون دسته‌بندی" }}

+
+
+
+ برند +

{{ product()?.brand?.name || "بدون برند" }}

+
+
+
+
+
+
+ + + + + + + + +
+
diff --git a/src/app/modules/products/views/single.component.ts b/src/app/modules/products/views/single.component.ts index 85335d1..a9355af 100644 --- a/src/app/modules/products/views/single.component.ts +++ b/src/app/modules/products/views/single.component.ts @@ -1,9 +1,95 @@ -import { Component } from '@angular/core'; +import { Maybe } from '@/core'; +import { KeyValueComponent } from '@/shared/components'; +import { Component, inject, signal } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; +import { Button, ButtonDirective } from 'primeng/button'; +import { Card } from 'primeng/card'; +import { GalleriaModule } from 'primeng/galleria'; +import { IProductResponse } from '../models'; +import { ProductsService } from '../services/main.service'; @Component({ selector: 'app-product', templateUrl: './single.component.html', + imports: [ButtonDirective, RouterLink, GalleriaModule, KeyValueComponent, Card, Button], }) export class ProductComponent { - constructor() {} + private route = inject(ActivatedRoute); + constructor(private service: ProductsService) { + this.getData(); + } + + images = signal([ + { + itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria1.jpg', + alt: 'Description for Image 1', + title: 'Title 1', + }, + { + itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria2.jpg', + alt: 'Description for Image 2', + title: 'Title 2', + }, + { + itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria3.jpg', + alt: 'Description for Image 3', + title: 'Title 3', + }, + { + itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria4.jpg', + alt: 'Description for Image 4', + title: 'Title 4', + }, + { + itemImageSrc: 'https://primefaces.org/cdn/primeng/images/galleria/galleria5.jpg', + alt: 'Description for Image 5', + title: 'Title 5', + }, + ]); + + productId = this.route.snapshot.params['productId']; + loading = signal(false); + product = signal>(null); + + getData() { + this.loading.set(true); + this.service.getSingle(this.productId).subscribe({ + next: (res) => { + this.product.set(res); + this.loading.set(false); + }, + error: () => { + this.loading.set(false); + }, + }); + } + + get topBarCardDetails() { + return [ + { + icon: 'pi pi-box', + label: 'موجودی', + value: 0, + }, + { + icon: 'pi pi-dollar', + label: 'قیمت پایه', + value: this.product()?.basePrice?.toString() || 'تعیین نشده', + }, + { + icon: 'pi pi-calendar', + label: 'تاریخ ایجاد', + value: this.product()?.createdAt + ? new Date(this.product()!.createdAt!).toLocaleDateString() + : '-', + }, + { + icon: 'pi pi-clock', + label: 'تعداد فروش', + value: 1200, + }, + ]; + } + + openDeleteConfirm() {} } diff --git a/src/app/modules/products/views/update.component.html b/src/app/modules/products/views/update.component.html new file mode 100644 index 0000000..13db74e --- /dev/null +++ b/src/app/modules/products/views/update.component.html @@ -0,0 +1,9 @@ +@if (loading()) { + +} @else { + +} diff --git a/src/app/modules/products/views/update.component.ts b/src/app/modules/products/views/update.component.ts new file mode 100644 index 0000000..eda9421 --- /dev/null +++ b/src/app/modules/products/views/update.component.ts @@ -0,0 +1,57 @@ +import { Maybe } from '@/core'; +import { ToastService } from '@/core/services/toast.service'; +import { Component, inject, signal } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ProgressSpinner } from 'primeng/progressspinner'; +import { ProductFullFormComponent } from '../components/fullForm/full-form.component'; +import { IProductRequest, IProductResponse } from '../models'; +import { ProductsService } from '../services/main.service'; + +@Component({ + selector: 'app-product-update', + templateUrl: './update.component.html', + imports: [ProductFullFormComponent, ProgressSpinner], +}) +export class UpdateProductComponent { + private route = inject(ActivatedRoute); + constructor( + private service: ProductsService, + private toastService: ToastService, + private router: Router, + ) { + this.getData(); + } + + submitLoading = signal(false); + + productId = this.route.snapshot.params['productId']; + loading = signal(false); + product = signal>(null); + + getData() { + this.loading.set(true); + this.service.getSingle(this.productId).subscribe({ + next: (res) => { + this.product.set(res); + this.loading.set(false); + }, + error: () => { + this.loading.set(false); + }, + }); + } + + submit(payload: IProductRequest) { + this.submitLoading.set(true); + this.service.update(this.productId, payload).subscribe({ + next: (res) => { + this.toastService.success({ text: `محصول ${payload.name} با موفقیت ویرایش شد` }); + this.router.navigate(['products', this.productId]); + this.submitLoading.set(false); + }, + error: (err) => { + this.submitLoading.set(false); + }, + }); + } +} diff --git a/src/app/shared/components/barcodeInput/barcode-input.component.html b/src/app/shared/components/barcodeInput/barcode-input.component.html new file mode 100644 index 0000000..729a21a --- /dev/null +++ b/src/app/shared/components/barcodeInput/barcode-input.component.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/src/app/shared/components/barcodeInput/barcode-input.component.ts b/src/app/shared/components/barcodeInput/barcode-input.component.ts new file mode 100644 index 0000000..75ffa21 --- /dev/null +++ b/src/app/shared/components/barcodeInput/barcode-input.component.ts @@ -0,0 +1,27 @@ +import { Maybe } from '@/core'; +import { UikitFieldComponent } from '@/uikit'; +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { ButtonDirective } from 'primeng/button'; +import { InputGroup } from 'primeng/inputgroup'; +import { InputGroupAddon } from 'primeng/inputgroupaddon'; +import { InputText } from 'primeng/inputtext'; + +@Component({ + selector: 'barcode-input', + templateUrl: './barcode-input.component.html', + imports: [ + ReactiveFormsModule, + UikitFieldComponent, + InputGroup, + InputGroupAddon, + ButtonDirective, + InputText, + ], +}) +export class BarcodeInputComponent { + @Input() control!: FormControl>; + constructor() {} + + scanBarcode() {} +} diff --git a/src/app/shared/components/pageDataList/page-data-list.component.html b/src/app/shared/components/pageDataList/page-data-list.component.html index dff5a06..c78e438 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.html +++ b/src/app/shared/components/pageDataList/page-data-list.component.html @@ -8,7 +8,7 @@ columnResizeMode="fit" stripedRows [showFirstLastIcon]="false" - class="grow !flex flex-col overflow-hidden" + class="grow flex! flex-col overflow-hidden" [tableStyleClass]="!items.length && !loading ? 'h-full' : ''" > @if (pageTitle || showAdd || filter || caption) { @@ -102,7 +102,7 @@ --> - @for (i of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; track i) { + @for (i of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; track i) { @for (col of columns; track col) { diff --git a/src/app/shared/components/pageDataList/page-data-list.component.ts b/src/app/shared/components/pageDataList/page-data-list.component.ts index 2b6c01b..f599d4d 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.ts +++ b/src/app/shared/components/pageDataList/page-data-list.component.ts @@ -4,11 +4,9 @@ import { Component, computed, ContentChild, - ElementRef, EventEmitter, Input, Output, - Renderer2, signal, TemplateRef, } from '@angular/core'; @@ -25,6 +23,7 @@ export interface IColumn { width?: string; minWidth?: string; canCopy?: boolean; + type?: 'text' | 'price' | 'boolean' | 'date'; customDataModel?: TemplateRef | ((item: any) => string | number | boolean); } @@ -45,10 +44,7 @@ export interface IColumn { ], }) export class PageDataListComponent { - constructor( - private host: ElementRef, - private renderer: Renderer2, - ) {} + constructor() {} @Input() pageTitle!: string; @Input() addNewCtaLabel?: string; @@ -125,6 +121,19 @@ export class PageDataListComponent { return this.renderCustom(column, item); } const data = item[field]; + switch (column.type) { + case 'date': + if (!data) return '-'; + const date = new Date(data); + return isNaN(date.getTime()) ? '-' : date.toLocaleDateString(); + case 'boolean': + return data ? 'بله' : 'خیر'; + case 'price': + return typeof data === 'number' ? data.toLocaleString() : '-'; + default: + break; + } + if (data === undefined || data === null) { return '-'; } diff --git a/src/presets.ts b/src/presets.ts index f35fd46..c311f2d 100644 --- a/src/presets.ts +++ b/src/presets.ts @@ -1,4 +1,4 @@ -import { definePreset } from '@primeuix/themes'; +import { definePreset, updateSurfacePalette } from '@primeuix/themes'; import Aura from '@primeuix/themes/aura'; // Reusable exported accent color constant @@ -26,18 +26,65 @@ const MyPreset = definePreset(Aura, { }, semantic: { primary: { - 50: '{indigo.50}', - 100: '{indigo.100}', - 200: '{indigo.200}', - 300: '{indigo.300}', - 400: '{indigo.400}', - 500: '{indigo.500}', - 600: '{indigo.600}', - 700: '{indigo.700}', - 800: '{indigo.800}', - 900: '{indigo.900}', - 950: '{indigo.950}', + 50: '{surface.50}', + 100: '{surface.100}', + 200: '{surface.200}', + 300: '{surface.300}', + 400: '{surface.400}', + 500: '{surface.500}', + 600: '{surface.600}', + 700: '{surface.700}', + 800: '{surface.800}', + 900: '{surface.900}', + 950: '{surface.950}', }, + + colorScheme: { + light: { + primary: { + color: '{primary.950}', + contrastColor: '#ffffff', + hoverColor: '{primary.800}', + activeColor: '{primary.700}', + }, + highlight: { + background: '{primary.950}', + focusBackground: '{primary.700}', + color: '#ffffff', + focusColor: '#ffffff', + }, + }, + dark: { + primary: { + color: '{primary.50}', + contrastColor: '{primary.950}', + hoverColor: '{primary.200}', + activeColor: '{primary.300}', + }, + highlight: { + background: '{primary.50}', + focusBackground: '{primary.300}', + color: '{primary.950}', + focusColor: '{primary.950}', + }, + }, + }, + }, + primitive: { + ...updateSurfacePalette({ + 0: '#ffffff', + 50: '#f8fafc', + 100: '#f1f5f9', + 200: '#e2e8f0', + 300: '#cbd5e1', + 400: '#94a3b8', + 500: '#64748b', + 600: '#475569', + 700: '#334155', + 800: '#1e293b', + 900: '#0f172a', + 950: '#020617', + }), }, });