From 3bc1202c772484419a09791ead88731946b6901e Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Thu, 4 Dec 2025 23:34:00 +0330 Subject: [PATCH] feat: Implement product categories, stores, suppliers, and users management features - Added ProductCategoriesService for handling product category API interactions. - Created components for listing and viewing product categories. - Implemented UI for managing product categories with a data list component. - Developed StoresService for managing store-related API calls. - Created components for listing and viewing stores with appropriate UI. - Added SuppliersService for handling supplier API interactions. - Implemented components for managing suppliers with a data list component. - Developed UsersService for managing user-related API calls. - Created components for listing and viewing users with appropriate UI. - Enhanced not found page with improved layout and navigation options. --- public/favicon.ico | Bin 0 -> 15086 bytes public/favicon/apple-touch-icon.png | Bin 0 -> 10086 bytes public/favicon/favicon-96x96.png | Bin 0 -> 5249 bytes public/favicon/favicon.ico | Bin 0 -> 15086 bytes public/favicon/favicon.svg | 3 + public/favicon/site.webmanifest | 21 + public/favicon/web-app-manifest-192x192.png | Bin 0 -> 10464 bytes public/favicon/web-app-manifest-512x512.png | Bin 0 -> 26810 bytes src/app.routes.ts | 46 +- src/app/core/constants/index.ts | 1 + src/app/core/constants/menuItems.const.ts | 71 +++ .../component/app.configurator.component.html | 49 ++ .../component/app.configurator.component.ts | 424 +++++++++++++++++ src/app/layout/component/app.configurator.ts | 446 ------------------ .../app.floatingconfigurator.component.html | 7 + .../app.floatingconfigurator.component.ts | 23 + .../component/app.floatingconfigurator.ts | 32 -- .../component/app.footer.component.html | 1 + .../layout/component/app.footer.component.ts | 8 + src/app/layout/component/app.footer.ts | 11 - .../component/app.layout.component.html | 31 ++ .../layout/component/app.layout.component.ts | 153 ++++++ src/app/layout/component/app.layout.ts | 111 ----- .../layout/component/app.menu.component.html | 10 + .../layout/component/app.menu.component.ts | 19 + src/app/layout/component/app.menu.ts | 157 ------ .../component/app.menuitem.component.html | 58 +++ .../component/app.menuitem.component.ts | 166 +++++++ src/app/layout/component/app.menuitem.ts | 170 ------- .../component/app.sidebar.component.html | 3 + .../layout/component/app.sidebar.component.ts | 12 + src/app/layout/component/app.sidebar.ts | 14 - .../component/app.topbar.component.html | 40 ++ .../layout/component/app.topbar.component.ts | 53 +++ src/app/layout/component/app.topbar.ts | 120 ----- src/app/layout/service/layout.service.ts | 25 +- .../modules/auth/pages/auth.component.html | 4 +- .../customers/components/form.component.html | 14 + .../customers/components/form.component.ts | 74 +++ .../customers/constants/apiRoutes/index.ts | 6 + src/app/modules/customers/constants/index.ts | 2 + .../customers/constants/routes/index.ts | 25 + src/app/modules/customers/models/index.ts | 1 + src/app/modules/customers/models/io.d.ts | 29 ++ .../customers/services/main.service.ts | 24 + src/app/modules/customers/views/index.ts | 2 + .../customers/views/list.component.html | 12 + .../modules/customers/views/list.component.ts | 45 ++ .../customers/views/single.component.html | 1 + .../customers/views/single.component.ts | 9 + .../components/form.component.html | 8 + .../inventories/components/form.component.ts | 68 +++ .../inventories/constants/apiRoutes/index.ts | 6 + .../modules/inventories/constants/index.ts | 2 + .../inventories/constants/routes/index.ts | 25 + src/app/modules/inventories/models/index.ts | 1 + src/app/modules/inventories/models/io.d.ts | 14 + .../inventories/services/main.service.ts | 24 + src/app/modules/inventories/views/index.ts | 2 + .../inventories/views/list.component.html | 12 + .../inventories/views/list.component.ts | 37 ++ .../inventories/views/single.component.html | 1 + .../inventories/views/single.component.ts | 9 + .../components/form.component.html | 8 + .../components/form.component.ts | 68 +++ .../constants/apiRoutes/index.ts | 6 + .../modules/productBrands/constants/index.ts | 2 + .../productBrands/constants/routes/index.ts | 26 + src/app/modules/productBrands/models/index.ts | 1 + src/app/modules/productBrands/models/io.d.ts | 17 + .../productBrands/services/main.service.ts | 24 + src/app/modules/productBrands/views/index.ts | 2 + .../productBrands/views/list.component.html | 12 + .../productBrands/views/list.component.ts | 39 ++ .../productBrands/views/single.component.html | 1 + .../productBrands/views/single.component.ts | 9 + .../components/form.component.html | 14 + .../components/form.component.ts | 68 +++ .../constants/apiRoutes/index.ts | 6 + .../productCategories/constants/index.ts | 2 + .../constants/routes/index.ts | 27 ++ .../modules/productCategories/models/index.ts | 1 + .../modules/productCategories/models/io.d.ts | 15 + .../services/main.service.ts | 28 ++ .../modules/productCategories/views/index.ts | 2 + .../views/list.component.html | 12 + .../productCategories/views/list.component.ts | 38 ++ .../views/single.component.html | 1 + .../views/single.component.ts | 9 + .../stores/components/form.component.html | 8 + .../stores/components/form.component.ts | 68 +++ .../stores/constants/apiRoutes/index.ts | 6 + src/app/modules/stores/constants/index.ts | 2 + .../modules/stores/constants/routes/index.ts | 25 + src/app/modules/stores/models/index.ts | 1 + src/app/modules/stores/models/io.d.ts | 16 + .../modules/stores/services/main.service.ts | 24 + src/app/modules/stores/views/index.ts | 2 + .../modules/stores/views/list.component.html | 12 + .../modules/stores/views/list.component.ts | 39 ++ .../stores/views/single.component.html | 1 + .../modules/stores/views/single.component.ts | 9 + .../suppliers/components/form.component.html | 14 + .../suppliers/components/form.component.ts | 74 +++ .../suppliers/constants/apiRoutes/index.ts | 6 + src/app/modules/suppliers/constants/index.ts | 2 + .../suppliers/constants/routes/index.ts | 25 + src/app/modules/suppliers/models/index.ts | 1 + src/app/modules/suppliers/models/io.d.ts | 29 ++ .../suppliers/services/main.service.ts | 24 + src/app/modules/suppliers/views/index.ts | 2 + .../suppliers/views/list.component.html | 12 + .../modules/suppliers/views/list.component.ts | 45 ++ .../suppliers/views/single.component.html | 1 + .../suppliers/views/single.component.ts | 9 + .../users/components/form.component.html | 9 + .../users/components/form.component.ts | 81 ++++ .../users/constants/apiRoutes/index.ts | 6 + src/app/modules/users/constants/index.ts | 2 + .../modules/users/constants/routes/index.ts | 25 + src/app/modules/users/models/index.ts | 1 + src/app/modules/users/models/io.d.ts | 15 + .../modules/users/services/main.service.ts | 27 ++ src/app/modules/users/views/index.ts | 2 + .../modules/users/views/list.component.html | 15 + src/app/modules/users/views/list.component.ts | 48 ++ .../modules/users/views/single.component.html | 1 + .../modules/users/views/single.component.ts | 9 + src/app/pages/auth/access.ts | 63 ++- src/app/pages/auth/error.ts | 61 ++- src/app/pages/auth/login.ts | 169 ++++--- .../landing/components/featureswidget.ts | 139 ------ .../pages/landing/components/footerwidget.ts | 73 --- .../pages/landing/components/herowidget.ts | 25 - .../landing/components/highlightswidget.ts | 46 -- .../pages/landing/components/pricingwidget.ts | 119 ----- .../components/topbarwidget.component.ts | 68 --- src/app/pages/landing/landing.ts | 31 -- .../pages/notfound/notfound.component.html | 52 ++ src/app/pages/notfound/notfound.component.ts | 29 ++ src/app/pages/notfound/notfound.ts | 68 --- .../components/input/input.component.html | 34 +- .../components/input/input.component.ts | 6 +- .../page-data-list.component.html | 7 +- .../pageDataList/page-data-list.component.ts | 6 +- src/app/uikit/uikit-emptystate.component.html | 2 +- src/app/uikit/uikit-emptystate.component.ts | 46 +- src/assets/images/index.ts | 2 +- src/assets/images/logo.png | Bin 56774 -> 26810 bytes src/assets/images/logo.webp | Bin 4724 -> 0 bytes src/assets/layout/_main.scss | 2 +- src/environments/environment.ts | 12 +- src/index.html | 5 +- src/proxy.conf.js | 26 +- 154 files changed, 3149 insertions(+), 1820 deletions(-) create mode 100644 public/favicon.ico create mode 100644 public/favicon/apple-touch-icon.png create mode 100644 public/favicon/favicon-96x96.png create mode 100644 public/favicon/favicon.ico create mode 100644 public/favicon/favicon.svg create mode 100644 public/favicon/site.webmanifest create mode 100644 public/favicon/web-app-manifest-192x192.png create mode 100644 public/favicon/web-app-manifest-512x512.png create mode 100644 src/app/core/constants/menuItems.const.ts create mode 100644 src/app/layout/component/app.configurator.component.html create mode 100644 src/app/layout/component/app.configurator.component.ts delete mode 100644 src/app/layout/component/app.configurator.ts create mode 100644 src/app/layout/component/app.floatingconfigurator.component.html create mode 100644 src/app/layout/component/app.floatingconfigurator.component.ts delete mode 100644 src/app/layout/component/app.floatingconfigurator.ts create mode 100644 src/app/layout/component/app.footer.component.html create mode 100644 src/app/layout/component/app.footer.component.ts delete mode 100644 src/app/layout/component/app.footer.ts create mode 100644 src/app/layout/component/app.layout.component.html create mode 100644 src/app/layout/component/app.layout.component.ts delete mode 100644 src/app/layout/component/app.layout.ts create mode 100644 src/app/layout/component/app.menu.component.html create mode 100644 src/app/layout/component/app.menu.component.ts delete mode 100644 src/app/layout/component/app.menu.ts create mode 100644 src/app/layout/component/app.menuitem.component.html create mode 100644 src/app/layout/component/app.menuitem.component.ts delete mode 100644 src/app/layout/component/app.menuitem.ts create mode 100644 src/app/layout/component/app.sidebar.component.html create mode 100644 src/app/layout/component/app.sidebar.component.ts delete mode 100644 src/app/layout/component/app.sidebar.ts create mode 100644 src/app/layout/component/app.topbar.component.html create mode 100644 src/app/layout/component/app.topbar.component.ts delete mode 100644 src/app/layout/component/app.topbar.ts create mode 100644 src/app/modules/customers/components/form.component.html create mode 100644 src/app/modules/customers/components/form.component.ts create mode 100644 src/app/modules/customers/constants/apiRoutes/index.ts create mode 100644 src/app/modules/customers/constants/index.ts create mode 100644 src/app/modules/customers/constants/routes/index.ts create mode 100644 src/app/modules/customers/models/index.ts create mode 100644 src/app/modules/customers/models/io.d.ts create mode 100644 src/app/modules/customers/services/main.service.ts create mode 100644 src/app/modules/customers/views/index.ts create mode 100644 src/app/modules/customers/views/list.component.html create mode 100644 src/app/modules/customers/views/list.component.ts create mode 100644 src/app/modules/customers/views/single.component.html create mode 100644 src/app/modules/customers/views/single.component.ts create mode 100644 src/app/modules/inventories/components/form.component.html create mode 100644 src/app/modules/inventories/components/form.component.ts create mode 100644 src/app/modules/inventories/constants/apiRoutes/index.ts create mode 100644 src/app/modules/inventories/constants/index.ts create mode 100644 src/app/modules/inventories/constants/routes/index.ts create mode 100644 src/app/modules/inventories/models/index.ts create mode 100644 src/app/modules/inventories/models/io.d.ts create mode 100644 src/app/modules/inventories/services/main.service.ts create mode 100644 src/app/modules/inventories/views/index.ts create mode 100644 src/app/modules/inventories/views/list.component.html create mode 100644 src/app/modules/inventories/views/list.component.ts create mode 100644 src/app/modules/inventories/views/single.component.html create mode 100644 src/app/modules/inventories/views/single.component.ts create mode 100644 src/app/modules/productBrands/components/form.component.html create mode 100644 src/app/modules/productBrands/components/form.component.ts create mode 100644 src/app/modules/productBrands/constants/apiRoutes/index.ts create mode 100644 src/app/modules/productBrands/constants/index.ts create mode 100644 src/app/modules/productBrands/constants/routes/index.ts create mode 100644 src/app/modules/productBrands/models/index.ts create mode 100644 src/app/modules/productBrands/models/io.d.ts create mode 100644 src/app/modules/productBrands/services/main.service.ts create mode 100644 src/app/modules/productBrands/views/index.ts create mode 100644 src/app/modules/productBrands/views/list.component.html create mode 100644 src/app/modules/productBrands/views/list.component.ts create mode 100644 src/app/modules/productBrands/views/single.component.html create mode 100644 src/app/modules/productBrands/views/single.component.ts create mode 100644 src/app/modules/productCategories/components/form.component.html create mode 100644 src/app/modules/productCategories/components/form.component.ts create mode 100644 src/app/modules/productCategories/constants/apiRoutes/index.ts create mode 100644 src/app/modules/productCategories/constants/index.ts create mode 100644 src/app/modules/productCategories/constants/routes/index.ts create mode 100644 src/app/modules/productCategories/models/index.ts create mode 100644 src/app/modules/productCategories/models/io.d.ts create mode 100644 src/app/modules/productCategories/services/main.service.ts create mode 100644 src/app/modules/productCategories/views/index.ts create mode 100644 src/app/modules/productCategories/views/list.component.html create mode 100644 src/app/modules/productCategories/views/list.component.ts create mode 100644 src/app/modules/productCategories/views/single.component.html create mode 100644 src/app/modules/productCategories/views/single.component.ts create mode 100644 src/app/modules/stores/components/form.component.html create mode 100644 src/app/modules/stores/components/form.component.ts create mode 100644 src/app/modules/stores/constants/apiRoutes/index.ts create mode 100644 src/app/modules/stores/constants/index.ts create mode 100644 src/app/modules/stores/constants/routes/index.ts create mode 100644 src/app/modules/stores/models/index.ts create mode 100644 src/app/modules/stores/models/io.d.ts create mode 100644 src/app/modules/stores/services/main.service.ts create mode 100644 src/app/modules/stores/views/index.ts create mode 100644 src/app/modules/stores/views/list.component.html create mode 100644 src/app/modules/stores/views/list.component.ts create mode 100644 src/app/modules/stores/views/single.component.html create mode 100644 src/app/modules/stores/views/single.component.ts create mode 100644 src/app/modules/suppliers/components/form.component.html create mode 100644 src/app/modules/suppliers/components/form.component.ts create mode 100644 src/app/modules/suppliers/constants/apiRoutes/index.ts create mode 100644 src/app/modules/suppliers/constants/index.ts create mode 100644 src/app/modules/suppliers/constants/routes/index.ts create mode 100644 src/app/modules/suppliers/models/index.ts create mode 100644 src/app/modules/suppliers/models/io.d.ts create mode 100644 src/app/modules/suppliers/services/main.service.ts create mode 100644 src/app/modules/suppliers/views/index.ts create mode 100644 src/app/modules/suppliers/views/list.component.html create mode 100644 src/app/modules/suppliers/views/list.component.ts create mode 100644 src/app/modules/suppliers/views/single.component.html create mode 100644 src/app/modules/suppliers/views/single.component.ts create mode 100644 src/app/modules/users/components/form.component.html create mode 100644 src/app/modules/users/components/form.component.ts create mode 100644 src/app/modules/users/constants/apiRoutes/index.ts create mode 100644 src/app/modules/users/constants/index.ts create mode 100644 src/app/modules/users/constants/routes/index.ts create mode 100644 src/app/modules/users/models/index.ts create mode 100644 src/app/modules/users/models/io.d.ts create mode 100644 src/app/modules/users/services/main.service.ts create mode 100644 src/app/modules/users/views/index.ts create mode 100644 src/app/modules/users/views/list.component.html create mode 100644 src/app/modules/users/views/list.component.ts create mode 100644 src/app/modules/users/views/single.component.html create mode 100644 src/app/modules/users/views/single.component.ts delete mode 100644 src/app/pages/landing/components/featureswidget.ts delete mode 100644 src/app/pages/landing/components/footerwidget.ts delete mode 100644 src/app/pages/landing/components/herowidget.ts delete mode 100644 src/app/pages/landing/components/highlightswidget.ts delete mode 100644 src/app/pages/landing/components/pricingwidget.ts delete mode 100644 src/app/pages/landing/components/topbarwidget.component.ts delete mode 100644 src/app/pages/landing/landing.ts create mode 100644 src/app/pages/notfound/notfound.component.html create mode 100644 src/app/pages/notfound/notfound.component.ts delete mode 100644 src/app/pages/notfound/notfound.ts delete mode 100644 src/assets/images/logo.webp diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..04bc49eb6dc703cfb675c640d9688bc4a2907c81 GIT binary patch literal 15086 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 TA5Y-DUU!ntRz+M^>X82f-&#H| literal 0 HcmV?d00001 diff --git a/public/favicon/apple-touch-icon.png b/public/favicon/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..107a39090bcd33af853b0d03ad29148897001b77 GIT binary patch literal 10086 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 literal 0 HcmV?d00001 diff --git a/public/favicon/favicon-96x96.png b/public/favicon/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..9cc83f1a8f934370fda22c7da70f7f81d76ef5eb GIT binary patch literal 5249 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 TA5Y-DUU!ntRz+M^>X82f-&#H| literal 0 HcmV?d00001 diff --git a/public/favicon/favicon.svg b/public/favicon/favicon.svg new file mode 100644 index 0000000..9d8298e --- /dev/null +++ b/public/favicon/favicon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest new file mode 100644 index 0000000..edc6ed1 --- /dev/null +++ b/public/favicon/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "صندوق فروشگاهی", + "short_name": "صندوق فروشگاهی", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} \ No newline at end of file diff --git a/public/favicon/web-app-manifest-192x192.png b/public/favicon/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..56bb138b4decc738793b5a3f1d6d9f8f2f4a6a56 GIT binary patch literal 10464 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 OK$PU}%Vyp)di8&-EZUI( literal 0 HcmV?d00001 diff --git a/src/app.routes.ts b/src/app.routes.ts index 96e5ba3..e4d32e9 100644 --- a/src/app.routes.ts +++ b/src/app.routes.ts @@ -1,23 +1,35 @@ +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 { STORES_ROUTES } from '@/modules/stores/constants'; +import { SUPPLIERS_ROUTES } from '@/modules/suppliers/constants'; +import { USERS_ROUTES } from '@/modules/users/constants'; import { Routes } from '@angular/router'; -import { AppLayout } from './app/layout/component/app.layout'; +import { AppLayout } from './app/layout/component/app.layout.component'; import { Dashboard } from './app/pages/dashboard/dashboard'; import { Documentation } from './app/pages/documentation/documentation'; -import { Landing } from './app/pages/landing/landing'; -import { Notfound } from './app/pages/notfound/notfound'; +import { Notfound } from './app/pages/notfound/notfound.component'; export const appRoutes: Routes = [ - { - path: '', - component: AppLayout, - children: [ - { path: '', component: Dashboard }, - { path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') }, - { path: 'documentation', component: Documentation }, - { path: 'pages', loadChildren: () => import('./app/pages/pages.routes') } - ] - }, - { path: 'landing', component: Landing }, - { path: 'notfound', component: Notfound }, - { path: 'auth', loadChildren: () => import('./app/pages/auth/auth.routes') }, - { path: '**', redirectTo: '/notfound' } + { + path: '', + component: AppLayout, + children: [ + { path: '', component: Dashboard }, + ...USERS_ROUTES, + ...STORES_ROUTES, + ...SUPPLIERS_ROUTES, + ...PRODUCT_BRANDS_ROUTES, + ...PRODUCT_CATEGORIES_ROUTES, + ...CUSTOMERS_ROUTES, + ...INVENTORIES_ROUTES, + { path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') }, + { path: 'documentation', component: Documentation }, + { path: 'pages', loadChildren: () => import('./app/pages/pages.routes') }, + ], + }, + { path: 'notfound', component: Notfound }, + { path: 'auth', loadChildren: () => import('./app/pages/auth/auth.routes') }, + { path: '**', redirectTo: '/notfound' }, ]; diff --git a/src/app/core/constants/index.ts b/src/app/core/constants/index.ts index eb1aa70..c826a2c 100644 --- a/src/app/core/constants/index.ts +++ b/src/app/core/constants/index.ts @@ -1,2 +1,3 @@ export * from './defaultData.const'; +export * from './menuItems.const'; export * from './roles.const'; diff --git a/src/app/core/constants/menuItems.const.ts b/src/app/core/constants/menuItems.const.ts new file mode 100644 index 0000000..d616106 --- /dev/null +++ b/src/app/core/constants/menuItems.const.ts @@ -0,0 +1,71 @@ +import { MenuItem } from 'primeng/api'; +export const MENU_ITEMS = [ + { + items: [ + { + label: 'داشبورد', + icon: 'pi pi-fw pi-home', + routerLink: ['/dashboard'], + }, + ], + }, + { + label: 'موجودی‌ها', + icon: 'pi pi-fw pi-box', + items: [ + { + label: 'لیست موجودی‌ها', + icon: 'pi pi-fw pi-list', + routerLink: ['/inventories'], + }, + ], + }, + { + label: 'محصولات', + icon: 'pi pi-fw pi-tags', + items: [ + { + label: 'لیست محصولات', + icon: 'pi pi-fw pi-list', + routerLink: ['/products'], + }, + { + label: 'برندهای محصول', + icon: 'pi pi-fw pi-tags', + routerLink: ['/product-brands'], + }, + { + label: 'دسته‌بندی‌های محصول', + icon: 'pi pi-fw pi-list', + routerLink: ['/product-categories'], + }, + ], + }, + { + label: 'تامین‌کنندگان', + icon: 'pi pi-fw pi-users', + items: [ + { + label: 'لیست تامین‌کنندگان', + icon: 'pi pi-fw pi-list', + routerLink: ['/suppliers'], + }, + ], + }, + { + label: 'مدیریت سیستم', + icon: 'pi pi-fw pi-cog', + items: [ + { + label: 'فروشگاه‌ها', + icon: 'pi pi-fw pi-store', + routerLink: ['/stores'], + }, + { + label: 'کاربران', + icon: 'pi pi-fw pi-user', + routerLink: ['/users'], + }, + ], + }, +] as MenuItem[]; diff --git a/src/app/layout/component/app.configurator.component.html b/src/app/layout/component/app.configurator.component.html new file mode 100644 index 0000000..2ce1b60 --- /dev/null +++ b/src/app/layout/component/app.configurator.component.html @@ -0,0 +1,49 @@ +
+
+ Primary +
+ @for (primaryColor of primaryColors(); track primaryColor.name) { + + } +
+
+
+ Surface +
+ @for (surface of surfaces; track surface.name) { + + } +
+
+
+ Presets + +
+
+ Menu Mode + +
+
diff --git a/src/app/layout/component/app.configurator.component.ts b/src/app/layout/component/app.configurator.component.ts new file mode 100644 index 0000000..ce0a696 --- /dev/null +++ b/src/app/layout/component/app.configurator.component.ts @@ -0,0 +1,424 @@ +import { CommonModule, isPlatformBrowser } from '@angular/common'; +import { Component, computed, inject, PLATFORM_ID, signal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; +import { $t, updatePreset, updateSurfacePalette } from '@primeuix/themes'; +import Aura from '@primeuix/themes/aura'; +import Lara from '@primeuix/themes/lara'; +import Nora from '@primeuix/themes/nora'; +import { PrimeNG } from 'primeng/config'; +import { SelectButtonModule } from 'primeng/selectbutton'; +import { LayoutService } from '../service/layout.service'; + +const presets = { + Aura, + Lara, + Nora, +} as const; + +declare type KeyOfType = keyof T extends infer U ? U : never; + +declare type SurfacesType = { + name?: string; + palette?: { + 0?: string; + 50?: string; + 100?: string; + 200?: string; + 300?: string; + 400?: string; + 500?: string; + 600?: string; + 700?: string; + 800?: string; + 900?: string; + 950?: string; + }; +}; + +@Component({ + selector: 'app-configurator', + standalone: true, + imports: [CommonModule, FormsModule, SelectButtonModule], + templateUrl: './app.configurator.component.html', + host: { + class: + 'hidden absolute top-13 right-0 w-72 p-4 bg-surface-0 dark:bg-surface-900 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]', + }, +}) +export class AppConfigurator { + router = inject(Router); + + config: PrimeNG = inject(PrimeNG); + + layoutService: LayoutService = inject(LayoutService); + + platformId = inject(PLATFORM_ID); + + primeng = inject(PrimeNG); + + presets = Object.keys(presets); + + showMenuModeButton = signal(!this.router.url.includes('auth')); + + menuModeOptions = [ + { label: 'Static', value: 'static' }, + { label: 'Overlay', value: 'overlay' }, + ]; + + ngOnInit() { + if (isPlatformBrowser(this.platformId)) { + this.onPresetChange(this.layoutService.layoutConfig().preset); + } + } + + surfaces: SurfacesType[] = [ + { + name: 'slate', + palette: { + 0: '#ffffff', + 50: '#f8fafc', + 100: '#f1f5f9', + 200: '#e2e8f0', + 300: '#cbd5e1', + 400: '#94a3b8', + 500: '#64748b', + 600: '#475569', + 700: '#334155', + 800: '#1e293b', + 900: '#0f172a', + 950: '#020617', + }, + }, + { + name: 'gray', + palette: { + 0: '#ffffff', + 50: '#f9fafb', + 100: '#f3f4f6', + 200: '#e5e7eb', + 300: '#d1d5db', + 400: '#9ca3af', + 500: '#6b7280', + 600: '#4b5563', + 700: '#374151', + 800: '#1f2937', + 900: '#111827', + 950: '#030712', + }, + }, + { + name: 'zinc', + palette: { + 0: '#ffffff', + 50: '#fafafa', + 100: '#f4f4f5', + 200: '#e4e4e7', + 300: '#d4d4d8', + 400: '#a1a1aa', + 500: '#71717a', + 600: '#52525b', + 700: '#3f3f46', + 800: '#27272a', + 900: '#18181b', + 950: '#09090b', + }, + }, + { + name: 'neutral', + palette: { + 0: '#ffffff', + 50: '#fafafa', + 100: '#f5f5f5', + 200: '#e5e5e5', + 300: '#d4d4d4', + 400: '#a3a3a3', + 500: '#737373', + 600: '#525252', + 700: '#404040', + 800: '#262626', + 900: '#171717', + 950: '#0a0a0a', + }, + }, + { + name: 'stone', + palette: { + 0: '#ffffff', + 50: '#fafaf9', + 100: '#f5f5f4', + 200: '#e7e5e4', + 300: '#d6d3d1', + 400: '#a8a29e', + 500: '#78716c', + 600: '#57534e', + 700: '#44403c', + 800: '#292524', + 900: '#1c1917', + 950: '#0c0a09', + }, + }, + { + name: 'soho', + palette: { + 0: '#ffffff', + 50: '#ececec', + 100: '#dedfdf', + 200: '#c4c4c6', + 300: '#adaeb0', + 400: '#97979b', + 500: '#7f8084', + 600: '#6a6b70', + 700: '#55565b', + 800: '#3f4046', + 900: '#2c2c34', + 950: '#16161d', + }, + }, + { + name: 'viva', + palette: { + 0: '#ffffff', + 50: '#f3f3f3', + 100: '#e7e7e8', + 200: '#cfd0d0', + 300: '#b7b8b9', + 400: '#9fa1a1', + 500: '#87898a', + 600: '#6e7173', + 700: '#565a5b', + 800: '#3e4244', + 900: '#262b2c', + 950: '#0e1315', + }, + }, + { + name: 'ocean', + palette: { + 0: '#ffffff', + 50: '#fbfcfc', + 100: '#F7F9F8', + 200: '#EFF3F2', + 300: '#DADEDD', + 400: '#B1B7B6', + 500: '#828787', + 600: '#5F7274', + 700: '#415B61', + 800: '#29444E', + 900: '#183240', + 950: '#0c1920', + }, + }, + ]; + + selectedPrimaryColor = computed(() => { + return this.layoutService.layoutConfig().primary; + }); + + selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface); + + selectedPreset = computed(() => this.layoutService.layoutConfig().preset); + + menuMode = computed(() => this.layoutService.layoutConfig().menuMode); + + primaryColors = computed(() => { + const presetPalette = + presets[this.layoutService.layoutConfig().preset as KeyOfType].primitive; + const colors = [ + 'emerald', + 'green', + 'lime', + 'orange', + 'amber', + 'yellow', + 'teal', + 'cyan', + 'sky', + 'blue', + 'indigo', + 'violet', + 'purple', + 'fuchsia', + 'pink', + 'rose', + ]; + const palettes: SurfacesType[] = [{ name: 'noir', palette: {} }]; + + colors.forEach((color) => { + palettes.push({ + name: color, + palette: presetPalette?.[ + color as KeyOfType + ] as SurfacesType['palette'], + }); + }); + + return palettes; + }); + + getPresetExt() { + const color: SurfacesType = + this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {}; + const preset = this.layoutService.layoutConfig().preset; + + if (color.name === 'noir') { + return { + semantic: { + primary: { + 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}', + }, + }, + }, + }, + }; + } else { + if (preset === 'Nora') { + return { + semantic: { + primary: color.palette, + colorScheme: { + light: { + primary: { + color: '{primary.600}', + contrastColor: '#ffffff', + hoverColor: '{primary.700}', + activeColor: '{primary.800}', + }, + highlight: { + background: '{primary.600}', + focusBackground: '{primary.700}', + color: '#ffffff', + focusColor: '#ffffff', + }, + }, + dark: { + primary: { + color: '{primary.500}', + contrastColor: '{surface.900}', + hoverColor: '{primary.400}', + activeColor: '{primary.300}', + }, + highlight: { + background: '{primary.500}', + focusBackground: '{primary.400}', + color: '{surface.900}', + focusColor: '{surface.900}', + }, + }, + }, + }, + }; + } else { + return { + semantic: { + primary: color.palette, + colorScheme: { + light: { + primary: { + color: '{primary.500}', + contrastColor: '#ffffff', + hoverColor: '{primary.600}', + activeColor: '{primary.700}', + }, + highlight: { + background: '{primary.50}', + focusBackground: '{primary.100}', + color: '{primary.700}', + focusColor: '{primary.800}', + }, + }, + dark: { + primary: { + color: '{primary.400}', + contrastColor: '{surface.900}', + hoverColor: '{primary.300}', + activeColor: '{primary.200}', + }, + highlight: { + background: 'color-mix(in srgb, {primary.400}, transparent 84%)', + focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)', + color: 'rgba(255,255,255,.87)', + focusColor: 'rgba(255,255,255,.87)', + }, + }, + }, + }, + }; + } + } + } + + updateColors(event: any, type: string, color: any) { + if (type === 'primary') { + this.layoutService.layoutConfig.update((state) => ({ ...state, primary: color.name })); + } else if (type === 'surface') { + this.layoutService.layoutConfig.update((state) => ({ ...state, surface: color.name })); + } + this.applyTheme(type, color); + + event.stopPropagation(); + } + + applyTheme(type: string, color: any) { + if (type === 'primary') { + updatePreset(this.getPresetExt()); + } else if (type === 'surface') { + updateSurfacePalette(color.palette); + } + } + + onPresetChange(event: any) { + this.layoutService.layoutConfig.update((state) => ({ ...state, preset: event })); + const preset = presets[event as KeyOfType]; + const surfacePalette = this.surfaces.find( + (s) => s.name === this.selectedSurfaceColor(), + )?.palette; + $t() + .preset(preset) + .preset(this.getPresetExt()) + .surfacePalette(surfacePalette) + .use({ useDefaultOptions: true }); + } + + onMenuModeChange(event: string) { + this.layoutService.layoutConfig.update((prev) => ({ ...prev, menuMode: event })); + } +} diff --git a/src/app/layout/component/app.configurator.ts b/src/app/layout/component/app.configurator.ts deleted file mode 100644 index 49d0784..0000000 --- a/src/app/layout/component/app.configurator.ts +++ /dev/null @@ -1,446 +0,0 @@ -import { CommonModule, isPlatformBrowser } from '@angular/common'; -import { Component, computed, inject, PLATFORM_ID, signal } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { Router } from '@angular/router'; -import { $t, updatePreset, updateSurfacePalette } from '@primeuix/themes'; -import Aura from '@primeuix/themes/aura'; -import Lara from '@primeuix/themes/lara'; -import Nora from '@primeuix/themes/nora'; -import { PrimeNG } from 'primeng/config'; -import { SelectButtonModule } from 'primeng/selectbutton'; -import { LayoutService } from '../service/layout.service'; - -const presets = { - Aura, - Lara, - Nora -} as const; - -declare type KeyOfType = keyof T extends infer U ? U : never; - -declare type SurfacesType = { - name?: string; - palette?: { - 0?: string; - 50?: string; - 100?: string; - 200?: string; - 300?: string; - 400?: string; - 500?: string; - 600?: string; - 700?: string; - 800?: string; - 900?: string; - 950?: string; - }; -}; - -@Component({ - selector: 'app-configurator', - standalone: true, - imports: [CommonModule, FormsModule, SelectButtonModule], - template: ` -
-
- Primary -
- @for (primaryColor of primaryColors(); track primaryColor.name) { - - } -
-
-
- Surface -
- @for (surface of surfaces; track surface.name) { - - } -
-
-
- Presets - -
-
- Menu Mode - -
-
- `, - host: { - class: 'hidden absolute top-13 right-0 w-72 p-4 bg-surface-0 dark:bg-surface-900 border border-surface rounded-border origin-top shadow-[0px_3px_5px_rgba(0,0,0,0.02),0px_0px_2px_rgba(0,0,0,0.05),0px_1px_4px_rgba(0,0,0,0.08)]' - } -}) -export class AppConfigurator { - router = inject(Router); - - config: PrimeNG = inject(PrimeNG); - - layoutService: LayoutService = inject(LayoutService); - - platformId = inject(PLATFORM_ID); - - primeng = inject(PrimeNG); - - presets = Object.keys(presets); - - showMenuModeButton = signal(!this.router.url.includes('auth')); - - menuModeOptions = [ - { label: 'Static', value: 'static' }, - { label: 'Overlay', value: 'overlay' } - ]; - - ngOnInit() { - if (isPlatformBrowser(this.platformId)) { - this.onPresetChange(this.layoutService.layoutConfig().preset); - } - } - - surfaces: SurfacesType[] = [ - { - name: 'slate', - palette: { - 0: '#ffffff', - 50: '#f8fafc', - 100: '#f1f5f9', - 200: '#e2e8f0', - 300: '#cbd5e1', - 400: '#94a3b8', - 500: '#64748b', - 600: '#475569', - 700: '#334155', - 800: '#1e293b', - 900: '#0f172a', - 950: '#020617' - } - }, - { - name: 'gray', - palette: { - 0: '#ffffff', - 50: '#f9fafb', - 100: '#f3f4f6', - 200: '#e5e7eb', - 300: '#d1d5db', - 400: '#9ca3af', - 500: '#6b7280', - 600: '#4b5563', - 700: '#374151', - 800: '#1f2937', - 900: '#111827', - 950: '#030712' - } - }, - { - name: 'zinc', - palette: { - 0: '#ffffff', - 50: '#fafafa', - 100: '#f4f4f5', - 200: '#e4e4e7', - 300: '#d4d4d8', - 400: '#a1a1aa', - 500: '#71717a', - 600: '#52525b', - 700: '#3f3f46', - 800: '#27272a', - 900: '#18181b', - 950: '#09090b' - } - }, - { - name: 'neutral', - palette: { - 0: '#ffffff', - 50: '#fafafa', - 100: '#f5f5f5', - 200: '#e5e5e5', - 300: '#d4d4d4', - 400: '#a3a3a3', - 500: '#737373', - 600: '#525252', - 700: '#404040', - 800: '#262626', - 900: '#171717', - 950: '#0a0a0a' - } - }, - { - name: 'stone', - palette: { - 0: '#ffffff', - 50: '#fafaf9', - 100: '#f5f5f4', - 200: '#e7e5e4', - 300: '#d6d3d1', - 400: '#a8a29e', - 500: '#78716c', - 600: '#57534e', - 700: '#44403c', - 800: '#292524', - 900: '#1c1917', - 950: '#0c0a09' - } - }, - { - name: 'soho', - palette: { - 0: '#ffffff', - 50: '#ececec', - 100: '#dedfdf', - 200: '#c4c4c6', - 300: '#adaeb0', - 400: '#97979b', - 500: '#7f8084', - 600: '#6a6b70', - 700: '#55565b', - 800: '#3f4046', - 900: '#2c2c34', - 950: '#16161d' - } - }, - { - name: 'viva', - palette: { - 0: '#ffffff', - 50: '#f3f3f3', - 100: '#e7e7e8', - 200: '#cfd0d0', - 300: '#b7b8b9', - 400: '#9fa1a1', - 500: '#87898a', - 600: '#6e7173', - 700: '#565a5b', - 800: '#3e4244', - 900: '#262b2c', - 950: '#0e1315' - } - }, - { - name: 'ocean', - palette: { - 0: '#ffffff', - 50: '#fbfcfc', - 100: '#F7F9F8', - 200: '#EFF3F2', - 300: '#DADEDD', - 400: '#B1B7B6', - 500: '#828787', - 600: '#5F7274', - 700: '#415B61', - 800: '#29444E', - 900: '#183240', - 950: '#0c1920' - } - } - ]; - - selectedPrimaryColor = computed(() => { - return this.layoutService.layoutConfig().primary; - }); - - selectedSurfaceColor = computed(() => this.layoutService.layoutConfig().surface); - - selectedPreset = computed(() => this.layoutService.layoutConfig().preset); - - menuMode = computed(() => this.layoutService.layoutConfig().menuMode); - - primaryColors = computed(() => { - const presetPalette = presets[this.layoutService.layoutConfig().preset as KeyOfType].primitive; - const colors = ['emerald', 'green', 'lime', 'orange', 'amber', 'yellow', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose']; - const palettes: SurfacesType[] = [{ name: 'noir', palette: {} }]; - - colors.forEach((color) => { - palettes.push({ - name: color, - palette: presetPalette?.[color as KeyOfType] as SurfacesType['palette'] - }); - }); - - return palettes; - }); - - getPresetExt() { - const color: SurfacesType = this.primaryColors().find((c) => c.name === this.selectedPrimaryColor()) || {}; - const preset = this.layoutService.layoutConfig().preset; - - if (color.name === 'noir') { - return { - semantic: { - primary: { - 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}' - } - } - } - } - }; - } else { - if (preset === 'Nora') { - return { - semantic: { - primary: color.palette, - colorScheme: { - light: { - primary: { - color: '{primary.600}', - contrastColor: '#ffffff', - hoverColor: '{primary.700}', - activeColor: '{primary.800}' - }, - highlight: { - background: '{primary.600}', - focusBackground: '{primary.700}', - color: '#ffffff', - focusColor: '#ffffff' - } - }, - dark: { - primary: { - color: '{primary.500}', - contrastColor: '{surface.900}', - hoverColor: '{primary.400}', - activeColor: '{primary.300}' - }, - highlight: { - background: '{primary.500}', - focusBackground: '{primary.400}', - color: '{surface.900}', - focusColor: '{surface.900}' - } - } - } - } - }; - } else { - return { - semantic: { - primary: color.palette, - colorScheme: { - light: { - primary: { - color: '{primary.500}', - contrastColor: '#ffffff', - hoverColor: '{primary.600}', - activeColor: '{primary.700}' - }, - highlight: { - background: '{primary.50}', - focusBackground: '{primary.100}', - color: '{primary.700}', - focusColor: '{primary.800}' - } - }, - dark: { - primary: { - color: '{primary.400}', - contrastColor: '{surface.900}', - hoverColor: '{primary.300}', - activeColor: '{primary.200}' - }, - highlight: { - background: 'color-mix(in srgb, {primary.400}, transparent 84%)', - focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)', - color: 'rgba(255,255,255,.87)', - focusColor: 'rgba(255,255,255,.87)' - } - } - } - } - }; - } - } - } - - updateColors(event: any, type: string, color: any) { - if (type === 'primary') { - this.layoutService.layoutConfig.update((state) => ({ ...state, primary: color.name })); - } else if (type === 'surface') { - this.layoutService.layoutConfig.update((state) => ({ ...state, surface: color.name })); - } - this.applyTheme(type, color); - - event.stopPropagation(); - } - - applyTheme(type: string, color: any) { - if (type === 'primary') { - updatePreset(this.getPresetExt()); - } else if (type === 'surface') { - updateSurfacePalette(color.palette); - } - } - - onPresetChange(event: any) { - this.layoutService.layoutConfig.update((state) => ({ ...state, preset: event })); - const preset = presets[event as KeyOfType]; - const surfacePalette = this.surfaces.find((s) => s.name === this.selectedSurfaceColor())?.palette; - $t().preset(preset).preset(this.getPresetExt()).surfacePalette(surfacePalette).use({ useDefaultOptions: true }); - } - - onMenuModeChange(event: string) { - this.layoutService.layoutConfig.update((prev) => ({ ...prev, menuMode: event })); - } -} diff --git a/src/app/layout/component/app.floatingconfigurator.component.html b/src/app/layout/component/app.floatingconfigurator.component.html new file mode 100644 index 0000000..054c14c --- /dev/null +++ b/src/app/layout/component/app.floatingconfigurator.component.html @@ -0,0 +1,7 @@ +
+ +
+ + +
+
diff --git a/src/app/layout/component/app.floatingconfigurator.component.ts b/src/app/layout/component/app.floatingconfigurator.component.ts new file mode 100644 index 0000000..736a760 --- /dev/null +++ b/src/app/layout/component/app.floatingconfigurator.component.ts @@ -0,0 +1,23 @@ +import { CommonModule } from '@angular/common'; +import { Component, computed, inject, input } from '@angular/core'; +import { ButtonModule } from 'primeng/button'; +import { StyleClassModule } from 'primeng/styleclass'; +import { LayoutService } from '../service/layout.service'; +import { AppConfigurator } from './app.configurator.component'; + +@Component({ + selector: 'app-floating-configurator', + imports: [CommonModule, ButtonModule, StyleClassModule, AppConfigurator], + templateUrl: './app.floatingconfigurator.component.html', +}) +export class AppFloatingConfigurator { + LayoutService = inject(LayoutService); + + float = input(true); + + isDarkTheme = computed(() => this.LayoutService.layoutConfig().darkTheme); + + toggleDarkMode() { + this.LayoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); + } +} diff --git a/src/app/layout/component/app.floatingconfigurator.ts b/src/app/layout/component/app.floatingconfigurator.ts deleted file mode 100644 index 2754722..0000000 --- a/src/app/layout/component/app.floatingconfigurator.ts +++ /dev/null @@ -1,32 +0,0 @@ -import {Component, computed, inject, input} from '@angular/core'; -import { ButtonModule } from 'primeng/button'; -import { StyleClassModule } from 'primeng/styleclass'; -import { AppConfigurator } from './app.configurator'; -import { LayoutService } from '../service/layout.service'; -import {CommonModule} from "@angular/common"; - -@Component({ - selector: 'app-floating-configurator', - imports: [CommonModule, ButtonModule, StyleClassModule, AppConfigurator], - template: ` -
- -
- - -
-
- ` -}) -export class AppFloatingConfigurator { - LayoutService = inject(LayoutService); - - float = input(true); - - isDarkTheme = computed(() => this.LayoutService.layoutConfig().darkTheme); - - toggleDarkMode() { - this.LayoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); - } - -} diff --git a/src/app/layout/component/app.footer.component.html b/src/app/layout/component/app.footer.component.html new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/app/layout/component/app.footer.component.html @@ -0,0 +1 @@ + diff --git a/src/app/layout/component/app.footer.component.ts b/src/app/layout/component/app.footer.component.ts new file mode 100644 index 0000000..f182132 --- /dev/null +++ b/src/app/layout/component/app.footer.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + standalone: true, + selector: 'app-footer', + templateUrl: './app.footer.component.html', +}) +export class AppFooter {} diff --git a/src/app/layout/component/app.footer.ts b/src/app/layout/component/app.footer.ts deleted file mode 100644 index 5548818..0000000 --- a/src/app/layout/component/app.footer.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - standalone: true, - selector: 'app-footer', - template: `` -}) -export class AppFooter {} diff --git a/src/app/layout/component/app.layout.component.html b/src/app/layout/component/app.layout.component.html new file mode 100644 index 0000000..22150f8 --- /dev/null +++ b/src/app/layout/component/app.layout.component.html @@ -0,0 +1,31 @@ +
+ + @if (fullLoading) { +
+ +
+ } @else { + @if (showMenu()) { + + } +
+
+ @if (showBreadcrumb) { + + } +
+ +
+ @if (content) { + + } @else { + + } +
+
+
+ +
+ } +
+
diff --git a/src/app/layout/component/app.layout.component.ts b/src/app/layout/component/app.layout.component.ts new file mode 100644 index 0000000..567e98f --- /dev/null +++ b/src/app/layout/component/app.layout.component.ts @@ -0,0 +1,153 @@ +import { BreadcrumbService } from '@/core/services'; +import { BreadcrumbComponent } from '@/shared/components'; +import { CommonModule } from '@angular/common'; +import { + Component, + computed, + ContentChild, + inject, + Input, + Renderer2, + TemplateRef, + ViewChild, +} from '@angular/core'; +import { NavigationEnd, Router, RouterModule } from '@angular/router'; +import { ProgressSpinner } from 'primeng/progressspinner'; +import { filter, Subscription } from 'rxjs'; +import { LayoutService } from '../service/layout.service'; +import { AppFooter } from './app.footer.component'; +import { AppSidebar } from './app.sidebar.component'; +import { AppTopbar } from './app.topbar.component'; + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [ + CommonModule, + AppTopbar, + AppSidebar, + RouterModule, + AppFooter, + BreadcrumbComponent, + ProgressSpinner, + ], + templateUrl: './app.layout.component.html', +}) +export class AppLayout { + overlayMenuOpenSubscription: Subscription; + + menuOutsideClickListener: any; + + @Input() fullLoading: boolean = false; + + @ViewChild(AppSidebar) appSidebar!: AppSidebar; + + @ViewChild(AppTopbar) appTopBar!: AppTopbar; + @ContentChild('content', { static: true }) content?: TemplateRef | null; + + private breadcrumbService = inject(BreadcrumbService); + + constructor( + public layoutService: LayoutService, + public renderer: Renderer2, + public router: Router, + ) { + this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => { + if (!this.menuOutsideClickListener) { + this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => { + if (this.isOutsideClicked(event)) { + this.hideMenu(); + } + }); + } + + if (this.layoutService.layoutState().staticMenuMobileActive) { + this.blockBodyScroll(); + } + }); + + this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { + this.hideMenu(); + }); + } + + isOutsideClicked(event: MouseEvent) { + const sidebarEl = document.querySelector('.layout-sidebar'); + const topbarEl = document.querySelector('.layout-menu-button'); + const eventTarget = event.target as Node; + + return !( + sidebarEl?.isSameNode(eventTarget) || + sidebarEl?.contains(eventTarget) || + topbarEl?.isSameNode(eventTarget) || + topbarEl?.contains(eventTarget) + ); + } + + hideMenu() { + this.layoutService.layoutState.update((prev) => ({ + ...prev, + overlayMenuActive: false, + staticMenuMobileActive: false, + menuHoverActive: false, + })); + if (this.menuOutsideClickListener) { + this.menuOutsideClickListener(); + this.menuOutsideClickListener = null; + } + this.unblockBodyScroll(); + } + + blockBodyScroll(): void { + if (document.body.classList) { + document.body.classList.add('blocked-scroll'); + } else { + document.body.className += ' blocked-scroll'; + } + } + + unblockBodyScroll(): void { + if (document.body.classList) { + document.body.classList.remove('blocked-scroll'); + } else { + document.body.className = document.body.className.replace( + new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), + ' ', + ); + } + } + + get containerClass() { + return { + 'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay', + 'layout-static': this.layoutService.layoutConfig().menuMode === 'static', + 'layout-static-inactive': + this.layoutService.layoutState().staticMenuDesktopInactive && + this.layoutService.layoutConfig().menuMode === 'static', + 'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive, + 'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive, + }; + } + + get showBreadcrumb(): boolean { + return this.breadcrumbService.items.length > 0; + } + + get isFixedContentSize(): boolean { + return this.layoutService.isFixedContentSize() || false; + } + + showMenu = computed(() => { + return this.layoutService.menuItems().length > 0; + }); + + ngOnDestroy() { + if (this.overlayMenuOpenSubscription) { + this.overlayMenuOpenSubscription.unsubscribe(); + } + + if (this.menuOutsideClickListener) { + this.menuOutsideClickListener(); + } + } +} diff --git a/src/app/layout/component/app.layout.ts b/src/app/layout/component/app.layout.ts deleted file mode 100644 index de6142d..0000000 --- a/src/app/layout/component/app.layout.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { Component, Renderer2, ViewChild } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { NavigationEnd, Router, RouterModule } from '@angular/router'; -import { filter, Subscription } from 'rxjs'; -import { AppTopbar } from './app.topbar'; -import { AppSidebar } from './app.sidebar'; -import { AppFooter } from './app.footer'; -import { LayoutService } from '../service/layout.service'; - -@Component({ - selector: 'app-layout', - standalone: true, - imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppFooter], - template: `
- - -
-
- -
- -
-
-
` -}) -export class AppLayout { - overlayMenuOpenSubscription: Subscription; - - menuOutsideClickListener: any; - - @ViewChild(AppSidebar) appSidebar!: AppSidebar; - - @ViewChild(AppTopbar) appTopBar!: AppTopbar; - - constructor( - public layoutService: LayoutService, - public renderer: Renderer2, - public router: Router - ) { - this.overlayMenuOpenSubscription = this.layoutService.overlayOpen$.subscribe(() => { - if (!this.menuOutsideClickListener) { - this.menuOutsideClickListener = this.renderer.listen('document', 'click', (event) => { - if (this.isOutsideClicked(event)) { - this.hideMenu(); - } - }); - } - - if (this.layoutService.layoutState().staticMenuMobileActive) { - this.blockBodyScroll(); - } - }); - - this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { - this.hideMenu(); - }); - } - - isOutsideClicked(event: MouseEvent) { - const sidebarEl = document.querySelector('.layout-sidebar'); - const topbarEl = document.querySelector('.layout-menu-button'); - const eventTarget = event.target as Node; - - return !(sidebarEl?.isSameNode(eventTarget) || sidebarEl?.contains(eventTarget) || topbarEl?.isSameNode(eventTarget) || topbarEl?.contains(eventTarget)); - } - - hideMenu() { - this.layoutService.layoutState.update((prev) => ({ ...prev, overlayMenuActive: false, staticMenuMobileActive: false, menuHoverActive: false })); - if (this.menuOutsideClickListener) { - this.menuOutsideClickListener(); - this.menuOutsideClickListener = null; - } - this.unblockBodyScroll(); - } - - blockBodyScroll(): void { - if (document.body.classList) { - document.body.classList.add('blocked-scroll'); - } else { - document.body.className += ' blocked-scroll'; - } - } - - unblockBodyScroll(): void { - if (document.body.classList) { - document.body.classList.remove('blocked-scroll'); - } else { - document.body.className = document.body.className.replace(new RegExp('(^|\\b)' + 'blocked-scroll'.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); - } - } - - get containerClass() { - return { - 'layout-overlay': this.layoutService.layoutConfig().menuMode === 'overlay', - 'layout-static': this.layoutService.layoutConfig().menuMode === 'static', - 'layout-static-inactive': this.layoutService.layoutState().staticMenuDesktopInactive && this.layoutService.layoutConfig().menuMode === 'static', - 'layout-overlay-active': this.layoutService.layoutState().overlayMenuActive, - 'layout-mobile-active': this.layoutService.layoutState().staticMenuMobileActive - }; - } - - ngOnDestroy() { - if (this.overlayMenuOpenSubscription) { - this.overlayMenuOpenSubscription.unsubscribe(); - } - - if (this.menuOutsideClickListener) { - this.menuOutsideClickListener(); - } - } -} diff --git a/src/app/layout/component/app.menu.component.html b/src/app/layout/component/app.menu.component.html new file mode 100644 index 0000000..877cb1c --- /dev/null +++ b/src/app/layout/component/app.menu.component.html @@ -0,0 +1,10 @@ +
    + + @if (!item.separator) { +
  • + } + @if (item.separator) { + + } +
    +
diff --git a/src/app/layout/component/app.menu.component.ts b/src/app/layout/component/app.menu.component.ts new file mode 100644 index 0000000..08130ec --- /dev/null +++ b/src/app/layout/component/app.menu.component.ts @@ -0,0 +1,19 @@ +import { CommonModule } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { AppMenuitem } from './app.menuitem.component'; +import { LayoutService } from '../service/layout.service'; + +@Component({ + selector: 'app-menu', + standalone: true, + imports: [CommonModule, AppMenuitem, RouterModule], + templateUrl: './app.menu.component.html', +}) +export class AppMenu { + private layoutService = inject(LayoutService); + + get model() { + return this.layoutService.menuItems(); + } +} diff --git a/src/app/layout/component/app.menu.ts b/src/app/layout/component/app.menu.ts deleted file mode 100644 index ae77ce7..0000000 --- a/src/app/layout/component/app.menu.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { Component } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { RouterModule } from '@angular/router'; -import { MenuItem } from 'primeng/api'; -import { AppMenuitem } from './app.menuitem'; - -@Component({ - selector: 'app-menu', - standalone: true, - imports: [CommonModule, AppMenuitem, RouterModule], - template: `
    - -
  • - -
    -
` -}) -export class AppMenu { - model: MenuItem[] = []; - - ngOnInit() { - this.model = [ - { - label: 'Home', - items: [{ label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] }] - }, - { - label: 'UI Components', - items: [ - { label: 'Form Layout', icon: 'pi pi-fw pi-id-card', routerLink: ['/uikit/formlayout'] }, - { label: 'Input', icon: 'pi pi-fw pi-check-square', routerLink: ['/uikit/input'] }, - { label: 'Button', icon: 'pi pi-fw pi-mobile', class: 'rotated-icon', routerLink: ['/uikit/button'] }, - { label: 'Table', icon: 'pi pi-fw pi-table', routerLink: ['/uikit/table'] }, - { label: 'List', icon: 'pi pi-fw pi-list', routerLink: ['/uikit/list'] }, - { label: 'Tree', icon: 'pi pi-fw pi-share-alt', routerLink: ['/uikit/tree'] }, - { label: 'Panel', icon: 'pi pi-fw pi-tablet', routerLink: ['/uikit/panel'] }, - { label: 'Overlay', icon: 'pi pi-fw pi-clone', routerLink: ['/uikit/overlay'] }, - { label: 'Media', icon: 'pi pi-fw pi-image', routerLink: ['/uikit/media'] }, - { label: 'Menu', icon: 'pi pi-fw pi-bars', routerLink: ['/uikit/menu'] }, - { label: 'Message', icon: 'pi pi-fw pi-comment', routerLink: ['/uikit/message'] }, - { label: 'File', icon: 'pi pi-fw pi-file', routerLink: ['/uikit/file'] }, - { label: 'Chart', icon: 'pi pi-fw pi-chart-bar', routerLink: ['/uikit/charts'] }, - { label: 'Timeline', icon: 'pi pi-fw pi-calendar', routerLink: ['/uikit/timeline'] }, - { label: 'Misc', icon: 'pi pi-fw pi-circle', routerLink: ['/uikit/misc'] } - ] - }, - { - label: 'Pages', - icon: 'pi pi-fw pi-briefcase', - routerLink: ['/pages'], - items: [ - { - label: 'Landing', - icon: 'pi pi-fw pi-globe', - routerLink: ['/landing'] - }, - { - label: 'Auth', - icon: 'pi pi-fw pi-user', - items: [ - { - label: 'Login', - icon: 'pi pi-fw pi-sign-in', - routerLink: ['/auth/login'] - }, - { - label: 'Error', - icon: 'pi pi-fw pi-times-circle', - routerLink: ['/auth/error'] - }, - { - label: 'Access Denied', - icon: 'pi pi-fw pi-lock', - routerLink: ['/auth/access'] - } - ] - }, - { - label: 'Crud', - icon: 'pi pi-fw pi-pencil', - routerLink: ['/pages/crud'] - }, - { - label: 'Not Found', - icon: 'pi pi-fw pi-exclamation-circle', - routerLink: ['/pages/notfound'] - }, - { - label: 'Empty', - icon: 'pi pi-fw pi-circle-off', - routerLink: ['/pages/empty'] - } - ] - }, - { - label: 'Hierarchy', - items: [ - { - label: 'Submenu 1', - icon: 'pi pi-fw pi-bookmark', - items: [ - { - label: 'Submenu 1.1', - icon: 'pi pi-fw pi-bookmark', - items: [ - { label: 'Submenu 1.1.1', icon: 'pi pi-fw pi-bookmark' }, - { label: 'Submenu 1.1.2', icon: 'pi pi-fw pi-bookmark' }, - { label: 'Submenu 1.1.3', icon: 'pi pi-fw pi-bookmark' } - ] - }, - { - label: 'Submenu 1.2', - icon: 'pi pi-fw pi-bookmark', - items: [{ label: 'Submenu 1.2.1', icon: 'pi pi-fw pi-bookmark' }] - } - ] - }, - { - label: 'Submenu 2', - icon: 'pi pi-fw pi-bookmark', - items: [ - { - label: 'Submenu 2.1', - icon: 'pi pi-fw pi-bookmark', - items: [ - { label: 'Submenu 2.1.1', icon: 'pi pi-fw pi-bookmark' }, - { label: 'Submenu 2.1.2', icon: 'pi pi-fw pi-bookmark' } - ] - }, - { - label: 'Submenu 2.2', - icon: 'pi pi-fw pi-bookmark', - items: [{ label: 'Submenu 2.2.1', icon: 'pi pi-fw pi-bookmark' }] - } - ] - } - ] - }, - { - label: 'Get Started', - items: [ - { - label: 'Documentation', - icon: 'pi pi-fw pi-book', - routerLink: ['/documentation'] - }, - { - label: 'View Source', - icon: 'pi pi-fw pi-github', - url: 'https://github.com/primefaces/sakai-ng', - target: '_blank' - } - ] - } - ]; - } -} diff --git a/src/app/layout/component/app.menuitem.component.html b/src/app/layout/component/app.menuitem.component.html new file mode 100644 index 0000000..95b26a1 --- /dev/null +++ b/src/app/layout/component/app.menuitem.component.html @@ -0,0 +1,58 @@ + +
{{ item.label }}
+ + + + + + + + {{ item.label }} + + + + + + + {{ item.label }} + + + +
    + +
  • +
    +
+
diff --git a/src/app/layout/component/app.menuitem.component.ts b/src/app/layout/component/app.menuitem.component.ts new file mode 100644 index 0000000..8a68bf0 --- /dev/null +++ b/src/app/layout/component/app.menuitem.component.ts @@ -0,0 +1,166 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { CommonModule } from '@angular/common'; +import { Component, HostBinding, Input } from '@angular/core'; +import { NavigationEnd, Router, RouterModule } from '@angular/router'; +import { MenuItem } from 'primeng/api'; +import { RippleModule } from 'primeng/ripple'; +import { Subscription } from 'rxjs'; +import { filter } from 'rxjs/operators'; +import { LayoutService } from '../service/layout.service'; + +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: '[app-menuitem]', + imports: [CommonModule, RouterModule, RippleModule], + templateUrl: './app.menuitem.component.html', + animations: [ + trigger('children', [ + state( + 'collapsed', + style({ + height: '0', + }), + ), + state( + 'expanded', + style({ + height: '*', + }), + ), + transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')), + ]), + ], + + providers: [LayoutService], +}) +export class AppMenuitem { + @Input() item!: MenuItem; + + @Input() index!: number; + + @Input() @HostBinding('class.layout-root-menuitem') root!: boolean; + + @Input() parentKey!: string; + + active = false; + + menuSourceSubscription: Subscription; + + menuResetSubscription: Subscription; + + key: string = ''; + + constructor( + public router: Router, + private layoutService: LayoutService, + ) { + this.menuSourceSubscription = this.layoutService.menuSource$.subscribe((value) => { + Promise.resolve(null).then(() => { + if (value.routeEvent) { + this.active = + value.key === this.key || value.key.startsWith(this.key + '-') ? true : false; + } else { + if (value.key !== this.key && !value.key.startsWith(this.key + '-')) { + this.active = false; + } + } + }); + }); + + this.menuResetSubscription = this.layoutService.resetSource$.subscribe(() => { + this.active = false; + }); + + this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => { + // always update active state on navigation so parent items can + // detect if any descendant route is active + this.updateActiveStateFromRoute(); + }); + } + + ngOnInit() { + this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index); + + if (this.item.routerLink) { + this.updateActiveStateFromRoute(); + } + } + + updateActiveStateFromRoute() { + const opts = { + paths: 'subset' as any, + queryParams: 'ignored' as any, + matrixParams: 'subset' as any, + fragment: 'ignored' as any, + }; + + let activeRoute = false; + + if (this.item.routerLink) { + activeRoute = this.router.isActive(this.item.routerLink[0], opts); + } + + // if this item itself is not active, check its descendants + if (!activeRoute && this.item.items && this.item.items.length) { + const anyChildActive = (items: MenuItem[]): boolean => { + for (const it of items) { + if (it.routerLink && this.router.isActive(it.routerLink[0], opts)) { + return true; + } + if (it.items && it.items.length && anyChildActive(it.items)) { + return true; + } + } + return false; + }; + + activeRoute = anyChildActive(this.item.items); + } + + if (activeRoute) { + // notify layout that this menu (or its descendant) is active + this.layoutService.onMenuStateChange({ key: this.key, routeEvent: true }); + } + } + + itemClick(event: Event) { + // avoid processing disabled items + if (this.item.disabled) { + event.preventDefault(); + return; + } + + // execute command + if (this.item.command) { + this.item.command({ originalEvent: event, item: this.item }); + } + + // toggle active state + if (this.item.items) { + this.active = !this.active; + } + + this.layoutService.onMenuStateChange({ key: this.key }); + } + + get submenuAnimation() { + return this.root ? 'expanded' : this.active ? 'expanded' : 'collapsed'; + } + + @HostBinding('class.active-menuitem') + get activeClass() { + // mark item as active when itself or any of its submenu items are active + // include root items as well so parents highlight when a child route is active + return this.active; + } + + ngOnDestroy() { + if (this.menuSourceSubscription) { + this.menuSourceSubscription.unsubscribe(); + } + + if (this.menuResetSubscription) { + this.menuResetSubscription.unsubscribe(); + } + } +} diff --git a/src/app/layout/component/app.menuitem.ts b/src/app/layout/component/app.menuitem.ts deleted file mode 100644 index 3b70b84..0000000 --- a/src/app/layout/component/app.menuitem.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { Component, HostBinding, Input } from '@angular/core'; -import { NavigationEnd, Router, RouterModule } from '@angular/router'; -import { animate, state, style, transition, trigger } from '@angular/animations'; -import { Subscription } from 'rxjs'; -import { filter } from 'rxjs/operators'; -import { CommonModule } from '@angular/common'; -import { RippleModule } from 'primeng/ripple'; -import { MenuItem } from 'primeng/api'; -import { LayoutService } from '../service/layout.service'; - -@Component({ - // eslint-disable-next-line @angular-eslint/component-selector - selector: '[app-menuitem]', - imports: [CommonModule, RouterModule, RippleModule], - template: ` - -
{{ item.label }}
- - - {{ item.label }} - - - - - {{ item.label }} - - - -
    - -
  • -
    -
-
- `, - animations: [ - trigger('children', [ - state( - 'collapsed', - style({ - height: '0' - }) - ), - state( - 'expanded', - style({ - height: '*' - }) - ), - transition('collapsed <=> expanded', animate('400ms cubic-bezier(0.86, 0, 0.07, 1)')) - ]) - ], - providers: [LayoutService] -}) -export class AppMenuitem { - @Input() item!: MenuItem; - - @Input() index!: number; - - @Input() @HostBinding('class.layout-root-menuitem') root!: boolean; - - @Input() parentKey!: string; - - active = false; - - menuSourceSubscription: Subscription; - - menuResetSubscription: Subscription; - - key: string = ''; - - constructor( - public router: Router, - private layoutService: LayoutService - ) { - this.menuSourceSubscription = this.layoutService.menuSource$.subscribe((value) => { - Promise.resolve(null).then(() => { - if (value.routeEvent) { - this.active = value.key === this.key || value.key.startsWith(this.key + '-') ? true : false; - } else { - if (value.key !== this.key && !value.key.startsWith(this.key + '-')) { - this.active = false; - } - } - }); - }); - - this.menuResetSubscription = this.layoutService.resetSource$.subscribe(() => { - this.active = false; - }); - - this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe((params) => { - if (this.item.routerLink) { - this.updateActiveStateFromRoute(); - } - }); - } - - ngOnInit() { - this.key = this.parentKey ? this.parentKey + '-' + this.index : String(this.index); - - if (this.item.routerLink) { - this.updateActiveStateFromRoute(); - } - } - - updateActiveStateFromRoute() { - let activeRoute = this.router.isActive(this.item.routerLink[0], { paths: 'exact', queryParams: 'ignored', matrixParams: 'ignored', fragment: 'ignored' }); - - if (activeRoute) { - this.layoutService.onMenuStateChange({ key: this.key, routeEvent: true }); - } - } - - itemClick(event: Event) { - // avoid processing disabled items - if (this.item.disabled) { - event.preventDefault(); - return; - } - - // execute command - if (this.item.command) { - this.item.command({ originalEvent: event, item: this.item }); - } - - // toggle active state - if (this.item.items) { - this.active = !this.active; - } - - this.layoutService.onMenuStateChange({ key: this.key }); - } - - get submenuAnimation() { - return this.root ? 'expanded' : this.active ? 'expanded' : 'collapsed'; - } - - @HostBinding('class.active-menuitem') - get activeClass() { - return this.active && !this.root; - } - - ngOnDestroy() { - if (this.menuSourceSubscription) { - this.menuSourceSubscription.unsubscribe(); - } - - if (this.menuResetSubscription) { - this.menuResetSubscription.unsubscribe(); - } - } -} diff --git a/src/app/layout/component/app.sidebar.component.html b/src/app/layout/component/app.sidebar.component.html new file mode 100644 index 0000000..98e10e1 --- /dev/null +++ b/src/app/layout/component/app.sidebar.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/layout/component/app.sidebar.component.ts b/src/app/layout/component/app.sidebar.component.ts new file mode 100644 index 0000000..56fca6f --- /dev/null +++ b/src/app/layout/component/app.sidebar.component.ts @@ -0,0 +1,12 @@ +import { Component, ElementRef } from '@angular/core'; +import { AppMenu } from './app.menu.component'; + +@Component({ + selector: 'app-sidebar', + standalone: true, + imports: [AppMenu], + templateUrl: './app.sidebar.component.html', +}) +export class AppSidebar { + constructor(public el: ElementRef) {} +} diff --git a/src/app/layout/component/app.sidebar.ts b/src/app/layout/component/app.sidebar.ts deleted file mode 100644 index 0aba02f..0000000 --- a/src/app/layout/component/app.sidebar.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Component, ElementRef } from '@angular/core'; -import { AppMenu } from './app.menu'; - -@Component({ - selector: 'app-sidebar', - standalone: true, - imports: [AppMenu], - template: `
- -
` -}) -export class AppSidebar { - constructor(public el: ElementRef) {} -} diff --git a/src/app/layout/component/app.topbar.component.html b/src/app/layout/component/app.topbar.component.html new file mode 100644 index 0000000..7e7e58f --- /dev/null +++ b/src/app/layout/component/app.topbar.component.html @@ -0,0 +1,40 @@ +
+
+ + +
+ +
+
+ + +
+ +
+ + +
+
+
diff --git a/src/app/layout/component/app.topbar.component.ts b/src/app/layout/component/app.topbar.component.ts new file mode 100644 index 0000000..adb7293 --- /dev/null +++ b/src/app/layout/component/app.topbar.component.ts @@ -0,0 +1,53 @@ +import { AuthService } from '@/core/services/auth.service'; +import { CommonModule } from '@angular/common'; +import { Component, computed, inject } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { MenuItem } from 'primeng/api'; +import { Button } from 'primeng/button'; +import { MenuModule } from 'primeng/menu'; +import { StyleClassModule } from 'primeng/styleclass'; +import images from 'src/assets/images'; +import { LayoutService } from '../service/layout.service'; + +@Component({ + selector: 'app-topbar', + standalone: true, + imports: [RouterModule, CommonModule, StyleClassModule, MenuModule, Button], + templateUrl: './app.topbar.component.html', +}) +export class AppTopbar { + items!: MenuItem[]; + + constructor(public layoutService: LayoutService) {} + private authService: AuthService = inject(AuthService); + + readonly logo = images.logo; + + toggleDarkMode() { + this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); + } + + username = computed(() => this.authService.currentUser()?.fullName || 'کاربر ناشناس'); + + logout = () => { + this.authService.logout(); + }; + + profileMenuItems: MenuItem[] = [ + { + label: this.username(), + icon: 'pi pi-user', + disabled: true, + }, + { + label: 'راهنمای سامانه', + icon: 'pi pi-question-circle', + disabled: true, + }, + { + label: 'خروج', + icon: 'pi pi-sign-out', + command: this.logout, + }, + ]; +} diff --git a/src/app/layout/component/app.topbar.ts b/src/app/layout/component/app.topbar.ts deleted file mode 100644 index 62545da..0000000 --- a/src/app/layout/component/app.topbar.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { MenuItem } from 'primeng/api'; -import { StyleClassModule } from 'primeng/styleclass'; -import { LayoutService } from '../service/layout.service'; -import { AppConfigurator } from './app.configurator'; - -@Component({ - selector: 'app-topbar', - standalone: true, - imports: [RouterModule, CommonModule, StyleClassModule, AppConfigurator], - template: `
- - -
-
- -
- - -
-
- - - - -
-
`, -}) -export class AppTopbar { - items!: MenuItem[]; - - constructor(public layoutService: LayoutService) {} - - toggleDarkMode() { - this.layoutService.layoutConfig.update((state) => ({ ...state, darkTheme: !state.darkTheme })); - } -} diff --git a/src/app/layout/service/layout.service.ts b/src/app/layout/service/layout.service.ts index dc78b22..cf25a34 100644 --- a/src/app/layout/service/layout.service.ts +++ b/src/app/layout/service/layout.service.ts @@ -1,4 +1,6 @@ -import { Injectable, computed, effect, signal } from '@angular/core'; +import { MENU_ITEMS } from '@/core/constants'; +import { computed, effect, Injectable, signal } from '@angular/core'; +import { MenuItem } from 'primeng/api'; import { Subject } from 'rxjs'; export interface layoutConfig { @@ -15,6 +17,7 @@ interface LayoutState { configSidebarVisible?: boolean; staticMenuMobileActive?: boolean; menuHoverActive?: boolean; + isFixedContentSize?: boolean; } interface MenuChangeEvent { @@ -28,7 +31,7 @@ interface MenuChangeEvent { export class LayoutService { _config: layoutConfig = { preset: 'Aura', - primary: 'violet', + primary: 'blue', surface: null, darkTheme: localStorage.getItem('isDarkTheme') === 'true', menuMode: 'static', @@ -40,6 +43,7 @@ export class LayoutService { configSidebarVisible: false, staticMenuMobileActive: false, menuHoverActive: false, + isFixedContentSize: true, }; layoutConfig = signal(this._config); @@ -54,6 +58,8 @@ export class LayoutService { private resetSource = new Subject(); + public menuItems = signal(MENU_ITEMS); + menuSource$ = this.menuSource.asObservable(); resetSource$ = this.resetSource.asObservable(); @@ -76,11 +82,14 @@ export class LayoutService { isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay'); + isFixedContentSize = computed(() => this.layoutState().isFixedContentSize); + transitionComplete = signal(false); private initialized = false; constructor() { + this.handleDarkModeTransition(this._config); effect(() => { const config = this.layoutConfig(); if (config) { @@ -121,8 +130,16 @@ export class LayoutService { .catch(() => {}); } + changeIsFixedContentSize(status: boolean) { + this.layoutState.update((prev) => ({ + ...prev, + isFixedContentSize: status, + })); + } + toggleDarkMode(config?: layoutConfig): void { const _config = config || this.layoutConfig(); + localStorage.setItem('isDarkTheme', JSON.stringify(_config.darkTheme)); if (_config.darkTheme) { document.documentElement.classList.add('app-dark'); } else { @@ -186,4 +203,8 @@ export class LayoutService { reset() { this.resetSource.next(true); } + + setMenuItems(items: MenuItem[]) { + this.menuItems.set(items); + } } diff --git a/src/app/modules/auth/pages/auth.component.html b/src/app/modules/auth/pages/auth.component.html index 0a20d2d..a6eea8a 100644 --- a/src/app/modules/auth/pages/auth.component.html +++ b/src/app/modules/auth/pages/auth.component.html @@ -7,8 +7,8 @@ class="flex flex-col items-center justify-center px-12 py-10 h-auto bg-surface-card border border-surface-border rounded-lg shadow w-full max-w-md" >
- امتحانات شبه نهایی سنجش - به پنل امتحانات شبه نهایی سنجش خوش آمدید. + صندوق فروشگاهی + به پنل صندوق فروشگاهی خوش آمدید.
@if (activeStep() === "login") { diff --git a/src/app/modules/customers/components/form.component.html b/src/app/modules/customers/components/form.component.html new file mode 100644 index 0000000..e6ab161 --- /dev/null +++ b/src/app/modules/customers/components/form.component.html @@ -0,0 +1,14 @@ + +
+ + + + + + + + + + + +
diff --git a/src/app/modules/customers/components/form.component.ts b/src/app/modules/customers/components/form.component.ts new file mode 100644 index 0000000..dbdb04d --- /dev/null +++ b/src/app/modules/customers/components/form.component.ts @@ -0,0 +1,74 @@ +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'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { ICustomerRequest, ICustomerResponse } from '../models'; +import { CustomersService } from '../services/main.service'; + +@Component({ + selector: 'customer-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], +}) +export class CustomerFormComponent { + @Input() initialValues?: ICustomerResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor(private service: CustomersService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + form = this.fb.group({ + firstName: [this.initialValues?.firstName || null, [Validators.required]], + lastName: [this.initialValues?.lastName || null, [Validators.required]], + email: [this.initialValues?.email || null, [Validators.required, Validators.email]], + mobileNumber: [this.initialValues?.mobileNumber || null, [Validators.required]], + address: [this.initialValues?.address || null, [Validators.required]], + city: [this.initialValues?.city || null, [Validators.required]], + state: [this.initialValues?.state || null, [Validators.required]], + country: [this.initialValues?.country || null, [Validators.required]], + isActive: [this.initialValues?.isActive || false, [Validators.required]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as ICustomerRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/customers/constants/apiRoutes/index.ts b/src/app/modules/customers/constants/apiRoutes/index.ts new file mode 100644 index 0000000..bb3175d --- /dev/null +++ b/src/app/modules/customers/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/customers'; + +export const CUSTOMERS_API_ROUTES = { + list: () => `${baseUrl}`, + single: (customerId: string) => `${baseUrl}/${customerId}`, +}; diff --git a/src/app/modules/customers/constants/index.ts b/src/app/modules/customers/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/customers/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/customers/constants/routes/index.ts b/src/app/modules/customers/constants/routes/index.ts new file mode 100644 index 0000000..9213c5d --- /dev/null +++ b/src/app/modules/customers/constants/routes/index.ts @@ -0,0 +1,25 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TCustomersRouteNames = 'customers' | 'customer'; + +export const customersNamedRoutes: NamedRoutes = { + customers: { + path: 'customers', + loadComponent: () => import('../../views/list.component').then((m) => m.CustomersComponent), + meta: { + title: 'مشتریان', + pagePath: () => '/customers', + }, + }, + customer: { + path: 'customers/:customerId', + loadComponent: () => import('../../views/single.component').then((m) => m.CustomerComponent), + meta: { + title: 'مشتری', + pagePath: () => '/customers/:customerId', + }, + }, +}; + +export const CUSTOMERS_ROUTES: Routes = Object.values(customersNamedRoutes); diff --git a/src/app/modules/customers/models/index.ts b/src/app/modules/customers/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/customers/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/customers/models/io.d.ts b/src/app/modules/customers/models/io.d.ts new file mode 100644 index 0000000..7deef4d --- /dev/null +++ b/src/app/modules/customers/models/io.d.ts @@ -0,0 +1,29 @@ +export interface ICustomerRawResponse { + id: number; + firstName: string; + lastName: string; + email: string; + mobileNumber: string; + address: string; + city: string; + state: string; + country: string; + isActive: boolean; + createdAt: Date; + updatedAt: Date; + deletedAt: Date; +} + +export interface ICustomerResponse extends ICustomerRawResponse {} + +export interface ICustomerRequest { + firstName: string; + lastName: string; + email: string; + mobileNumber: string; + address: string; + city: string; + state: string; + country: string; + isActive: boolean; +} diff --git a/src/app/modules/customers/services/main.service.ts b/src/app/modules/customers/services/main.service.ts new file mode 100644 index 0000000..8cdbcaf --- /dev/null +++ b/src/app/modules/customers/services/main.service.ts @@ -0,0 +1,24 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { CUSTOMERS_API_ROUTES } from '../constants'; +import { ICustomerRawResponse, ICustomerRequest, ICustomerResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class CustomersService { + constructor(private http: HttpClient) {} + + private apiRoutes = CUSTOMERS_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + + getSingle(customerId: string): Observable { + return this.http.get(this.apiRoutes.single(customerId)); + } + + create(data: ICustomerRequest): Observable { + return this.http.post(this.apiRoutes.list(), data); + } +} diff --git a/src/app/modules/customers/views/index.ts b/src/app/modules/customers/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/customers/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/customers/views/list.component.html b/src/app/modules/customers/views/list.component.html new file mode 100644 index 0000000..60a32bf --- /dev/null +++ b/src/app/modules/customers/views/list.component.html @@ -0,0 +1,12 @@ + + diff --git a/src/app/modules/customers/views/list.component.ts b/src/app/modules/customers/views/list.component.ts new file mode 100644 index 0000000..5c305fb --- /dev/null +++ b/src/app/modules/customers/views/list.component.ts @@ -0,0 +1,45 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { ICustomerResponse } from '../models'; +import { CustomersService } from '../services/main.service'; + +@Component({ + selector: 'app-customers', + templateUrl: './list.component.html', + imports: [PageDataListComponent], +}) +export class CustomersComponent { + constructor(private customerService: CustomersService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'firstName', header: 'نام' }, + { field: 'lastName', header: 'نام خانوادگی' }, + { field: 'email', header: 'ایمیل' }, + { field: 'mobileNumber', header: 'شماره موبایل' }, + { field: 'address', header: 'آدرس' }, + { field: 'city', header: 'شهر' }, + { field: 'state', header: 'استان' }, + { field: 'country', header: 'کشور' }, + { field: 'isActive', header: 'فعال' }, + { field: 'createdAt', header: 'تاریخ ایجاد' }, + { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + + getData() { + this.loading.set(true); + this.customerService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } +} diff --git a/src/app/modules/customers/views/single.component.html b/src/app/modules/customers/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/customers/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/customers/views/single.component.ts b/src/app/modules/customers/views/single.component.ts new file mode 100644 index 0000000..77d906c --- /dev/null +++ b/src/app/modules/customers/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-customer', + templateUrl: './single.component.html', +}) +export class CustomerComponent { + constructor() {} +} diff --git a/src/app/modules/inventories/components/form.component.html b/src/app/modules/inventories/components/form.component.html new file mode 100644 index 0000000..2899af2 --- /dev/null +++ b/src/app/modules/inventories/components/form.component.html @@ -0,0 +1,8 @@ + +
+ + + + + +
diff --git a/src/app/modules/inventories/components/form.component.ts b/src/app/modules/inventories/components/form.component.ts new file mode 100644 index 0000000..245f311 --- /dev/null +++ b/src/app/modules/inventories/components/form.component.ts @@ -0,0 +1,68 @@ +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'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { IInventoryRequest, IInventoryResponse } from '../models'; +import { InventoriesService } from '../services/main.service'; + +@Component({ + selector: 'inventory-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], +}) +export class InventoryFormComponent { + @Input() initialValues?: IInventoryResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor(private service: InventoriesService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + form = this.fb.group({ + name: [this.initialValues?.name || null, [Validators.required]], + location: [this.initialValues?.location || null, [Validators.required]], + isActive: [this.initialValues?.isActive || false, [Validators.required]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as IInventoryRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/inventories/constants/apiRoutes/index.ts b/src/app/modules/inventories/constants/apiRoutes/index.ts new file mode 100644 index 0000000..7f00c77 --- /dev/null +++ b/src/app/modules/inventories/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/inventories'; + +export const INVENTORIES_API_ROUTES = { + list: () => `${baseUrl}`, + single: (inventoryId: string) => `${baseUrl}/${inventoryId}`, +}; diff --git a/src/app/modules/inventories/constants/index.ts b/src/app/modules/inventories/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/inventories/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/inventories/constants/routes/index.ts b/src/app/modules/inventories/constants/routes/index.ts new file mode 100644 index 0000000..188fc36 --- /dev/null +++ b/src/app/modules/inventories/constants/routes/index.ts @@ -0,0 +1,25 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TInventoriesRouteNames = 'inventories' | 'inventory'; + +export const inventoriesNamedRoutes: NamedRoutes = { + inventories: { + path: 'inventories', + loadComponent: () => import('../../views/list.component').then((m) => m.InventoriesComponent), + meta: { + title: 'انبارها', + pagePath: () => '/inventories', + }, + }, + inventory: { + path: 'inventories/:inventoryId', + loadComponent: () => import('../../views/single.component').then((m) => m.InventoryComponent), + meta: { + title: 'انبار', + pagePath: () => '/inventories/:inventoryId', + }, + }, +}; + +export const INVENTORIES_ROUTES: Routes = Object.values(inventoriesNamedRoutes); diff --git a/src/app/modules/inventories/models/index.ts b/src/app/modules/inventories/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/inventories/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/inventories/models/io.d.ts b/src/app/modules/inventories/models/io.d.ts new file mode 100644 index 0000000..579d4e4 --- /dev/null +++ b/src/app/modules/inventories/models/io.d.ts @@ -0,0 +1,14 @@ +export interface IInventoryRawResponse { + id: number; + name: string; + location: string; + isActive: boolean; +} + +export interface IInventoryResponse extends IInventoryRawResponse {} + +export interface IInventoryRequest { + name: string; + location: string; + isActive: boolean; +} diff --git a/src/app/modules/inventories/services/main.service.ts b/src/app/modules/inventories/services/main.service.ts new file mode 100644 index 0000000..df7b9f9 --- /dev/null +++ b/src/app/modules/inventories/services/main.service.ts @@ -0,0 +1,24 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { INVENTORIES_API_ROUTES } from '../constants'; +import { IInventoryRawResponse, IInventoryRequest, IInventoryResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class InventoriesService { + constructor(private http: HttpClient) {} + + private apiRoutes = INVENTORIES_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + + getSingle(inventoryId: string): Observable { + return this.http.get(this.apiRoutes.single(inventoryId)); + } + + create(data: IInventoryRequest): Observable { + return this.http.post(this.apiRoutes.list(), data); + } +} diff --git a/src/app/modules/inventories/views/index.ts b/src/app/modules/inventories/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/inventories/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/inventories/views/list.component.html b/src/app/modules/inventories/views/list.component.html new file mode 100644 index 0000000..f01f345 --- /dev/null +++ b/src/app/modules/inventories/views/list.component.html @@ -0,0 +1,12 @@ + + diff --git a/src/app/modules/inventories/views/list.component.ts b/src/app/modules/inventories/views/list.component.ts new file mode 100644 index 0000000..0cf6c14 --- /dev/null +++ b/src/app/modules/inventories/views/list.component.ts @@ -0,0 +1,37 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { IInventoryResponse } from '../models'; +import { InventoriesService } from '../services/main.service'; + +@Component({ + selector: 'app-inventories', + templateUrl: './list.component.html', + imports: [PageDataListComponent], +}) +export class InventoriesComponent { + constructor(private inventoryService: InventoriesService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'name', header: 'نام' }, + { field: 'location', header: 'موقعیت' }, + { field: 'isActive', header: 'فعال' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + + getData() { + this.loading.set(true); + this.inventoryService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } +} diff --git a/src/app/modules/inventories/views/single.component.html b/src/app/modules/inventories/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/inventories/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/inventories/views/single.component.ts b/src/app/modules/inventories/views/single.component.ts new file mode 100644 index 0000000..aaf9e1f --- /dev/null +++ b/src/app/modules/inventories/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-inventory', + templateUrl: './single.component.html', +}) +export class InventoryComponent { + constructor() {} +} diff --git a/src/app/modules/productBrands/components/form.component.html b/src/app/modules/productBrands/components/form.component.html new file mode 100644 index 0000000..615cd93 --- /dev/null +++ b/src/app/modules/productBrands/components/form.component.html @@ -0,0 +1,8 @@ + +
+ + + + + +
diff --git a/src/app/modules/productBrands/components/form.component.ts b/src/app/modules/productBrands/components/form.component.ts new file mode 100644 index 0000000..333c134 --- /dev/null +++ b/src/app/modules/productBrands/components/form.component.ts @@ -0,0 +1,68 @@ +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'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { IProductBrandRequest, IProductBrandResponse } from '../models'; +import { ProductBrandsService } from '../services/main.service'; + +@Component({ + selector: 'product-brand-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], +}) +export class ProductBrandFormComponent { + @Input() initialValues?: IProductBrandResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor(private service: ProductBrandsService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + 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]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as IProductBrandRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/productBrands/constants/apiRoutes/index.ts b/src/app/modules/productBrands/constants/apiRoutes/index.ts new file mode 100644 index 0000000..381bee9 --- /dev/null +++ b/src/app/modules/productBrands/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/product-brands'; + +export const PRODUCT_BRANDS_API_ROUTES = { + list: () => `${baseUrl}`, + single: (brandId: string) => `${baseUrl}/${brandId}`, +}; diff --git a/src/app/modules/productBrands/constants/index.ts b/src/app/modules/productBrands/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/productBrands/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/productBrands/constants/routes/index.ts b/src/app/modules/productBrands/constants/routes/index.ts new file mode 100644 index 0000000..cdffb33 --- /dev/null +++ b/src/app/modules/productBrands/constants/routes/index.ts @@ -0,0 +1,26 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TProductBrandsRouteNames = 'productBrands' | 'productBrand'; + +export const productBrandsNamedRoutes: NamedRoutes = { + productBrands: { + path: 'product-brands', + loadComponent: () => import('../../views/list.component').then((m) => m.ProductBrandsComponent), + meta: { + title: 'برندهای محصول', + pagePath: () => '/product-brands', + }, + }, + productBrand: { + path: 'product-brands/:brandId', + loadComponent: () => + import('../../views/single.component').then((m) => m.ProductBrandComponent), + meta: { + title: 'برند محصول', + pagePath: () => '/product-brands/:brandId', + }, + }, +}; + +export const PRODUCT_BRANDS_ROUTES: Routes = Object.values(productBrandsNamedRoutes); diff --git a/src/app/modules/productBrands/models/index.ts b/src/app/modules/productBrands/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/productBrands/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/productBrands/models/io.d.ts b/src/app/modules/productBrands/models/io.d.ts new file mode 100644 index 0000000..ed1e14e --- /dev/null +++ b/src/app/modules/productBrands/models/io.d.ts @@ -0,0 +1,17 @@ +export interface IProductBrandRawResponse { + id: number; + name: string; + description: string; + imageUrl: string; + createdAt: Date; + updatedAt: Date; + deletedAt: Date; +} + +export interface IProductBrandResponse extends IProductBrandRawResponse {} + +export interface IProductBrandRequest { + name: string; + description: string; + imageUrl: string; +} diff --git a/src/app/modules/productBrands/services/main.service.ts b/src/app/modules/productBrands/services/main.service.ts new file mode 100644 index 0000000..523349b --- /dev/null +++ b/src/app/modules/productBrands/services/main.service.ts @@ -0,0 +1,24 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { PRODUCT_BRANDS_API_ROUTES } from '../constants'; +import { IProductBrandRawResponse, IProductBrandRequest, IProductBrandResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class ProductBrandsService { + constructor(private http: HttpClient) {} + + private apiRoutes = PRODUCT_BRANDS_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + + getSingle(brandId: string): Observable { + return this.http.get(this.apiRoutes.single(brandId)); + } + + create(data: IProductBrandRequest): Observable { + return this.http.post(this.apiRoutes.list(), data); + } +} diff --git a/src/app/modules/productBrands/views/index.ts b/src/app/modules/productBrands/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/productBrands/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/productBrands/views/list.component.html b/src/app/modules/productBrands/views/list.component.html new file mode 100644 index 0000000..d827102 --- /dev/null +++ b/src/app/modules/productBrands/views/list.component.html @@ -0,0 +1,12 @@ + + diff --git a/src/app/modules/productBrands/views/list.component.ts b/src/app/modules/productBrands/views/list.component.ts new file mode 100644 index 0000000..c3025f1 --- /dev/null +++ b/src/app/modules/productBrands/views/list.component.ts @@ -0,0 +1,39 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { IProductBrandResponse } from '../models'; +import { ProductBrandsService } from '../services/main.service'; + +@Component({ + selector: 'app-product-brands', + templateUrl: './list.component.html', + imports: [PageDataListComponent], +}) +export class ProductBrandsComponent { + constructor(private productBrandService: ProductBrandsService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'name', header: 'نام' }, + { field: 'description', header: 'توضیحات' }, + { field: 'imageUrl', header: 'تصویر' }, + { field: 'createdAt', header: 'تاریخ ایجاد' }, + { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + + getData() { + this.loading.set(true); + this.productBrandService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } +} diff --git a/src/app/modules/productBrands/views/single.component.html b/src/app/modules/productBrands/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/productBrands/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/productBrands/views/single.component.ts b/src/app/modules/productBrands/views/single.component.ts new file mode 100644 index 0000000..9375bfe --- /dev/null +++ b/src/app/modules/productBrands/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-product-brand', + templateUrl: './single.component.html', +}) +export class ProductBrandComponent { + constructor() {} +} diff --git a/src/app/modules/productCategories/components/form.component.html b/src/app/modules/productCategories/components/form.component.html new file mode 100644 index 0000000..436e732 --- /dev/null +++ b/src/app/modules/productCategories/components/form.component.html @@ -0,0 +1,14 @@ + +
+ + + + + +
diff --git a/src/app/modules/productCategories/components/form.component.ts b/src/app/modules/productCategories/components/form.component.ts new file mode 100644 index 0000000..580d2e5 --- /dev/null +++ b/src/app/modules/productCategories/components/form.component.ts @@ -0,0 +1,68 @@ +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'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { IProductCategoryRequest, IProductCategoryResponse } from '../models'; +import { ProductCategoriesService } from '../services/main.service'; + +@Component({ + selector: 'product-category-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], +}) +export class ProductCategoryFormComponent { + @Input() initialValues?: IProductCategoryResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor(private service: ProductCategoriesService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + 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]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as IProductCategoryRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/productCategories/constants/apiRoutes/index.ts b/src/app/modules/productCategories/constants/apiRoutes/index.ts new file mode 100644 index 0000000..c24a2d2 --- /dev/null +++ b/src/app/modules/productCategories/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/product-categories'; + +export const PRODUCT_CATEGORIES_API_ROUTES = { + list: () => `${baseUrl}`, + single: (categoryId: string) => `${baseUrl}/${categoryId}`, +}; diff --git a/src/app/modules/productCategories/constants/index.ts b/src/app/modules/productCategories/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/productCategories/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/productCategories/constants/routes/index.ts b/src/app/modules/productCategories/constants/routes/index.ts new file mode 100644 index 0000000..72b0386 --- /dev/null +++ b/src/app/modules/productCategories/constants/routes/index.ts @@ -0,0 +1,27 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TProductCategoriesRouteNames = 'productCategories' | 'productCategory'; + +export const productCategoriesNamedRoutes: NamedRoutes = { + productCategories: { + path: 'product-categories', + loadComponent: () => + import('../../views/list.component').then((m) => m.ProductCategoriesComponent), + meta: { + title: 'دسته‌بندی محصولات', + pagePath: () => '/product-categories', + }, + }, + productCategory: { + path: 'product-categories/:categoryId', + loadComponent: () => + import('../../views/single.component').then((m) => m.ProductCategoryComponent), + meta: { + title: 'دسته‌بندی محصول', + pagePath: () => '/product-categories/:categoryId', + }, + }, +}; + +export const PRODUCT_CATEGORIES_ROUTES: Routes = Object.values(productCategoriesNamedRoutes); diff --git a/src/app/modules/productCategories/models/index.ts b/src/app/modules/productCategories/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/productCategories/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/productCategories/models/io.d.ts b/src/app/modules/productCategories/models/io.d.ts new file mode 100644 index 0000000..42ff440 --- /dev/null +++ b/src/app/modules/productCategories/models/io.d.ts @@ -0,0 +1,15 @@ +export interface IProductCategoryRawResponse { + id: number; + name: string; + description: string; + imageUrl: string; + createdAt: Date; +} + +export interface IProductCategoryResponse extends IProductCategoryRawResponse {} + +export interface IProductCategoryRequest { + name: string; + description: string; + imageUrl: string; +} diff --git a/src/app/modules/productCategories/services/main.service.ts b/src/app/modules/productCategories/services/main.service.ts new file mode 100644 index 0000000..741d9dd --- /dev/null +++ b/src/app/modules/productCategories/services/main.service.ts @@ -0,0 +1,28 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { PRODUCT_CATEGORIES_API_ROUTES } from '../constants'; +import { + IProductCategoryRawResponse, + IProductCategoryRequest, + IProductCategoryResponse, +} from '../models'; + +@Injectable({ providedIn: 'root' }) +export class ProductCategoriesService { + constructor(private http: HttpClient) {} + + private apiRoutes = PRODUCT_CATEGORIES_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + + getSingle(categoryId: string): Observable { + return this.http.get(this.apiRoutes.single(categoryId)); + } + + create(data: IProductCategoryRequest): Observable { + return this.http.post(this.apiRoutes.list(), data); + } +} diff --git a/src/app/modules/productCategories/views/index.ts b/src/app/modules/productCategories/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/productCategories/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/productCategories/views/list.component.html b/src/app/modules/productCategories/views/list.component.html new file mode 100644 index 0000000..0213263 --- /dev/null +++ b/src/app/modules/productCategories/views/list.component.html @@ -0,0 +1,12 @@ + + diff --git a/src/app/modules/productCategories/views/list.component.ts b/src/app/modules/productCategories/views/list.component.ts new file mode 100644 index 0000000..0a7fc7f --- /dev/null +++ b/src/app/modules/productCategories/views/list.component.ts @@ -0,0 +1,38 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { IProductCategoryResponse } from '../models'; +import { ProductCategoriesService } from '../services/main.service'; + +@Component({ + selector: 'app-product-categories', + templateUrl: './list.component.html', + imports: [PageDataListComponent], +}) +export class ProductCategoriesComponent { + constructor(private productCategoryService: ProductCategoriesService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'name', header: 'نام' }, + { field: 'description', header: 'توضیحات' }, + { field: 'imageUrl', header: 'تصویر' }, + { field: 'createdAt', header: 'تاریخ ایجاد' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + + getData() { + this.loading.set(true); + this.productCategoryService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } +} diff --git a/src/app/modules/productCategories/views/single.component.html b/src/app/modules/productCategories/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/productCategories/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/productCategories/views/single.component.ts b/src/app/modules/productCategories/views/single.component.ts new file mode 100644 index 0000000..41d37f8 --- /dev/null +++ b/src/app/modules/productCategories/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-product-category', + templateUrl: './single.component.html', +}) +export class ProductCategoryComponent { + constructor() {} +} diff --git a/src/app/modules/stores/components/form.component.html b/src/app/modules/stores/components/form.component.html new file mode 100644 index 0000000..bec62ff --- /dev/null +++ b/src/app/modules/stores/components/form.component.html @@ -0,0 +1,8 @@ + +
+ + + + + +
diff --git a/src/app/modules/stores/components/form.component.ts b/src/app/modules/stores/components/form.component.ts new file mode 100644 index 0000000..de58e79 --- /dev/null +++ b/src/app/modules/stores/components/form.component.ts @@ -0,0 +1,68 @@ +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'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { IStoreRequest, IStoreResponse } from '../models'; +import { StoresService } from '../services/main.service'; + +@Component({ + selector: 'store-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], +}) +export class StoreFormComponent { + @Input() initialValues?: IStoreResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor(private service: StoresService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + form = this.fb.group({ + name: [this.initialValues?.name || null, [Validators.required]], + location: [this.initialValues?.location || null, [Validators.required]], + isActive: [this.initialValues?.isActive || false, [Validators.required]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as IStoreRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/stores/constants/apiRoutes/index.ts b/src/app/modules/stores/constants/apiRoutes/index.ts new file mode 100644 index 0000000..3fc2cdc --- /dev/null +++ b/src/app/modules/stores/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/stores'; + +export const STORES_API_ROUTES = { + list: () => `${baseUrl}`, + single: (storeId: string) => `${baseUrl}/${storeId}`, +}; diff --git a/src/app/modules/stores/constants/index.ts b/src/app/modules/stores/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/stores/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/stores/constants/routes/index.ts b/src/app/modules/stores/constants/routes/index.ts new file mode 100644 index 0000000..3840c58 --- /dev/null +++ b/src/app/modules/stores/constants/routes/index.ts @@ -0,0 +1,25 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TStoresRouteNames = 'stores' | 'store'; + +export const storesNamedRoutes: NamedRoutes = { + stores: { + path: 'stores', + loadComponent: () => import('../../views/list.component').then((m) => m.StoresComponent), + meta: { + title: 'فروشگاه‌ها', + pagePath: () => '/stores', + }, + }, + store: { + path: 'stores/:storeId', + loadComponent: () => import('../../views/single.component').then((m) => m.StoreComponent), + meta: { + title: 'فروشگاه', + pagePath: () => '/stores/:storeId', + }, + }, +}; + +export const STORES_ROUTES: Routes = Object.values(storesNamedRoutes); diff --git a/src/app/modules/stores/models/index.ts b/src/app/modules/stores/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/stores/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/stores/models/io.d.ts b/src/app/modules/stores/models/io.d.ts new file mode 100644 index 0000000..6978821 --- /dev/null +++ b/src/app/modules/stores/models/io.d.ts @@ -0,0 +1,16 @@ +export interface IStoreRawResponse { + id: number; + name: string; + location: string; + isActive: boolean; + createdAt: Date; + updatedAt: Date; +} + +export interface IStoreResponse extends IStoreRawResponse {} + +export interface IStoreRequest { + name: string; + location: string; + isActive: boolean; +} diff --git a/src/app/modules/stores/services/main.service.ts b/src/app/modules/stores/services/main.service.ts new file mode 100644 index 0000000..8f8edb1 --- /dev/null +++ b/src/app/modules/stores/services/main.service.ts @@ -0,0 +1,24 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { STORES_API_ROUTES } from '../constants'; +import { IStoreRawResponse, IStoreRequest, IStoreResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class StoresService { + constructor(private http: HttpClient) {} + + private apiRoutes = STORES_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + + getSingle(storeId: string): Observable { + return this.http.get(this.apiRoutes.single(storeId)); + } + + create(data: IStoreRequest): Observable { + return this.http.post(this.apiRoutes.list(), data); + } +} diff --git a/src/app/modules/stores/views/index.ts b/src/app/modules/stores/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/stores/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/stores/views/list.component.html b/src/app/modules/stores/views/list.component.html new file mode 100644 index 0000000..71e4440 --- /dev/null +++ b/src/app/modules/stores/views/list.component.html @@ -0,0 +1,12 @@ + + diff --git a/src/app/modules/stores/views/list.component.ts b/src/app/modules/stores/views/list.component.ts new file mode 100644 index 0000000..81ce25a --- /dev/null +++ b/src/app/modules/stores/views/list.component.ts @@ -0,0 +1,39 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { IStoreResponse } from '../models'; +import { StoresService } from '../services/main.service'; + +@Component({ + selector: 'app-stores', + templateUrl: './list.component.html', + imports: [PageDataListComponent], +}) +export class StoresComponent { + constructor(private storeService: StoresService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'name', header: 'نام' }, + { field: 'location', header: 'موقعیت' }, + { field: 'isActive', header: 'فعال' }, + { field: 'createdAt', header: 'تاریخ ایجاد' }, + { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + + getData() { + this.loading.set(true); + this.storeService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } +} diff --git a/src/app/modules/stores/views/single.component.html b/src/app/modules/stores/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/stores/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/stores/views/single.component.ts b/src/app/modules/stores/views/single.component.ts new file mode 100644 index 0000000..95581fd --- /dev/null +++ b/src/app/modules/stores/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-store', + templateUrl: './single.component.html', +}) +export class StoreComponent { + constructor() {} +} diff --git a/src/app/modules/suppliers/components/form.component.html b/src/app/modules/suppliers/components/form.component.html new file mode 100644 index 0000000..33213c7 --- /dev/null +++ b/src/app/modules/suppliers/components/form.component.html @@ -0,0 +1,14 @@ + +
+ + + + + + + + + + + +
diff --git a/src/app/modules/suppliers/components/form.component.ts b/src/app/modules/suppliers/components/form.component.ts new file mode 100644 index 0000000..aa2ee49 --- /dev/null +++ b/src/app/modules/suppliers/components/form.component.ts @@ -0,0 +1,74 @@ +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'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Dialog } from 'primeng/dialog'; +import { ISupplierRequest, ISupplierResponse } from '../models'; +import { SuppliersService } from '../services/main.service'; + +@Component({ + selector: 'supplier-form', + templateUrl: './form.component.html', + imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], +}) +export class SupplierFormComponent { + @Input() initialValues?: ISupplierResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @Output() onSubmit = new EventEmitter(); + + private fb = inject(FormBuilder); + constructor(private service: SuppliersService) { + effect(() => { + const v = this.visibleSignal(); + if (!v) this.form.reset(); + }); + } + + form = this.fb.group({ + firstName: [this.initialValues?.firstName || null, [Validators.required]], + lastName: [this.initialValues?.lastName || null, [Validators.required]], + email: [this.initialValues?.email || null, [Validators.required, Validators.email]], + mobileNumber: [this.initialValues?.mobileNumber || null, [Validators.required]], + address: [this.initialValues?.address || null, [Validators.required]], + city: [this.initialValues?.city || null, [Validators.required]], + state: [this.initialValues?.state || null, [Validators.required]], + country: [this.initialValues?.country || null, [Validators.required]], + isActive: [this.initialValues?.isActive || false, [Validators.required]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as ISupplierRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/suppliers/constants/apiRoutes/index.ts b/src/app/modules/suppliers/constants/apiRoutes/index.ts new file mode 100644 index 0000000..f4a129f --- /dev/null +++ b/src/app/modules/suppliers/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/suppliers'; + +export const SUPPLIERS_API_ROUTES = { + list: () => `${baseUrl}`, + single: (supplierId: string) => `${baseUrl}/${supplierId}`, +}; diff --git a/src/app/modules/suppliers/constants/index.ts b/src/app/modules/suppliers/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/suppliers/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/suppliers/constants/routes/index.ts b/src/app/modules/suppliers/constants/routes/index.ts new file mode 100644 index 0000000..3ec6d6a --- /dev/null +++ b/src/app/modules/suppliers/constants/routes/index.ts @@ -0,0 +1,25 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TSuppliersRouteNames = 'suppliers' | 'supplier'; + +export const suppliersNamedRoutes: NamedRoutes = { + suppliers: { + path: 'suppliers', + loadComponent: () => import('../../views/list.component').then((m) => m.SuppliersComponent), + meta: { + title: 'تامین‌کنندگان', + pagePath: () => '/suppliers', + }, + }, + supplier: { + path: 'suppliers/:supplierId', + loadComponent: () => import('../../views/single.component').then((m) => m.SupplierComponent), + meta: { + title: 'تامین‌کننده', + pagePath: () => '/suppliers/:supplierId', + }, + }, +}; + +export const SUPPLIERS_ROUTES: Routes = Object.values(suppliersNamedRoutes); diff --git a/src/app/modules/suppliers/models/index.ts b/src/app/modules/suppliers/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/suppliers/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/suppliers/models/io.d.ts b/src/app/modules/suppliers/models/io.d.ts new file mode 100644 index 0000000..4d155c9 --- /dev/null +++ b/src/app/modules/suppliers/models/io.d.ts @@ -0,0 +1,29 @@ +export interface ISupplierRawResponse { + id: number; + firstName: string; + lastName: string; + email: string; + mobileNumber: string; + address: string; + city: string; + state: string; + country: string; + isActive: boolean; + createdAt: Date; + updatedAt: Date; + deletedAt: Date; +} + +export interface ISupplierResponse extends ISupplierRawResponse {} + +export interface ISupplierRequest { + firstName: string; + lastName: string; + email: string; + mobileNumber: string; + address: string; + city: string; + state: string; + country: string; + isActive: boolean; +} diff --git a/src/app/modules/suppliers/services/main.service.ts b/src/app/modules/suppliers/services/main.service.ts new file mode 100644 index 0000000..ddbf516 --- /dev/null +++ b/src/app/modules/suppliers/services/main.service.ts @@ -0,0 +1,24 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { SUPPLIERS_API_ROUTES } from '../constants'; +import { ISupplierRawResponse, ISupplierRequest, ISupplierResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class SuppliersService { + constructor(private http: HttpClient) {} + + private apiRoutes = SUPPLIERS_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + + getSingle(supplierId: string): Observable { + return this.http.get(this.apiRoutes.single(supplierId)); + } + + create(data: ISupplierRequest): Observable { + return this.http.post(this.apiRoutes.list(), data); + } +} diff --git a/src/app/modules/suppliers/views/index.ts b/src/app/modules/suppliers/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/suppliers/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/suppliers/views/list.component.html b/src/app/modules/suppliers/views/list.component.html new file mode 100644 index 0000000..f853845 --- /dev/null +++ b/src/app/modules/suppliers/views/list.component.html @@ -0,0 +1,12 @@ + + diff --git a/src/app/modules/suppliers/views/list.component.ts b/src/app/modules/suppliers/views/list.component.ts new file mode 100644 index 0000000..57e2779 --- /dev/null +++ b/src/app/modules/suppliers/views/list.component.ts @@ -0,0 +1,45 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { ISupplierResponse } from '../models'; +import { SuppliersService } from '../services/main.service'; + +@Component({ + selector: 'app-suppliers', + templateUrl: './list.component.html', + imports: [PageDataListComponent], +}) +export class SuppliersComponent { + constructor(private supplierService: SuppliersService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'firstName', header: 'نام' }, + { field: 'lastName', header: 'نام خانوادگی' }, + { field: 'email', header: 'ایمیل' }, + { field: 'mobileNumber', header: 'شماره موبایل' }, + { field: 'address', header: 'آدرس' }, + { field: 'city', header: 'شهر' }, + { field: 'state', header: 'استان' }, + { field: 'country', header: 'کشور' }, + { field: 'isActive', header: 'فعال' }, + { field: 'createdAt', header: 'تاریخ ایجاد' }, + { field: 'updatedAt', header: 'تاریخ به‌روزرسانی' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + + getData() { + this.loading.set(true); + this.supplierService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } +} diff --git a/src/app/modules/suppliers/views/single.component.html b/src/app/modules/suppliers/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/suppliers/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/suppliers/views/single.component.ts b/src/app/modules/suppliers/views/single.component.ts new file mode 100644 index 0000000..4500ce7 --- /dev/null +++ b/src/app/modules/suppliers/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-supplier', + templateUrl: './single.component.html', +}) +export class SupplierComponent { + constructor() {} +} diff --git a/src/app/modules/users/components/form.component.html b/src/app/modules/users/components/form.component.html new file mode 100644 index 0000000..759f6b1 --- /dev/null +++ b/src/app/modules/users/components/form.component.html @@ -0,0 +1,9 @@ + +
+ + + + + + +
diff --git a/src/app/modules/users/components/form.component.ts b/src/app/modules/users/components/form.component.ts new file mode 100644 index 0000000..f40c8c9 --- /dev/null +++ b/src/app/modules/users/components/form.component.ts @@ -0,0 +1,81 @@ +import { mobileValidator } from '@/core/validators'; +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'; +import { IUserRequest, IUserResponse } from '../models'; +import { UsersService } from '../services/main.service'; + +@Component({ + selector: 'user-form', + templateUrl: './form.component.html', + imports: [ + ReactiveFormsModule, + Dialog, + UikitFieldComponent, + InputComponent, + FormFooterActionsComponent, + ], +}) +export class UserFormComponent { + @Input() initialValues?: IUserResponse; + + @Input() + set visible(v: boolean) { + this.visibleSignal.set(!!v); + } + get visible() { + return this.visibleSignal(); + } + private visibleSignal = signal(false); + + @Output() visibleChange = new EventEmitter(); + @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(); + }); + } + + 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]], + }); + + submitLoading = signal(false); + + submit() { + this.form.markAllAsTouched(); + if (this.form.valid) { + this.form.disable(); + this.submitLoading.set(true); + this.service.create(this.form.value as IUserRequest).subscribe({ + next: (res) => { + this.submitLoading.set(false); + this.form.enable(); + this.form.reset(); + this.onSubmit.emit(res); + }, + error: () => { + this.submitLoading.set(false); + this.form.enable(); + }, + }); + } + } + + close() { + this.visibleChange.emit(false); + } +} diff --git a/src/app/modules/users/constants/apiRoutes/index.ts b/src/app/modules/users/constants/apiRoutes/index.ts new file mode 100644 index 0000000..91dda97 --- /dev/null +++ b/src/app/modules/users/constants/apiRoutes/index.ts @@ -0,0 +1,6 @@ +const baseUrl = '/api/v1/users'; + +export const USERS_API_ROUTES = { + list: () => `${baseUrl}`, + single: (userId: string) => `${baseUrl}/${userId}`, +}; diff --git a/src/app/modules/users/constants/index.ts b/src/app/modules/users/constants/index.ts new file mode 100644 index 0000000..ee61bd7 --- /dev/null +++ b/src/app/modules/users/constants/index.ts @@ -0,0 +1,2 @@ +export * from './apiRoutes'; +export * from './routes'; diff --git a/src/app/modules/users/constants/routes/index.ts b/src/app/modules/users/constants/routes/index.ts new file mode 100644 index 0000000..14e9d75 --- /dev/null +++ b/src/app/modules/users/constants/routes/index.ts @@ -0,0 +1,25 @@ +import { NamedRoutes } from '@/core'; +import { Routes } from '@angular/router'; + +export type TUsersRouteNames = 'users' | 'user'; + +export const usersNamedRoutes: NamedRoutes = { + users: { + path: 'users', + loadComponent: () => import('../../views/list.component').then((m) => m.UsersComponent), + meta: { + title: 'کاربران', + pagePath: () => '/users', + }, + }, + user: { + path: 'users/:userId', + loadComponent: () => import('../../views/single.component').then((m) => m.UserComponent), + meta: { + title: 'کاربر', + pagePath: () => '/users/:userId', + }, + }, +}; + +export const USERS_ROUTES: Routes = Object.values(usersNamedRoutes); diff --git a/src/app/modules/users/models/index.ts b/src/app/modules/users/models/index.ts new file mode 100644 index 0000000..61a518a --- /dev/null +++ b/src/app/modules/users/models/index.ts @@ -0,0 +1 @@ +export * from './io'; diff --git a/src/app/modules/users/models/io.d.ts b/src/app/modules/users/models/io.d.ts new file mode 100644 index 0000000..dad19ee --- /dev/null +++ b/src/app/modules/users/models/io.d.ts @@ -0,0 +1,15 @@ +export interface IUserRawResponse { + mobileNumber: string; + firstName: string; + lastName: string; + id: number; + roleId: number; +} +export interface IUserResponse extends IUserRawResponse {} + +export interface IUserRequest { + firstName: string; + lastName: string; + mobileNumber: string; + roleId: number; +} diff --git a/src/app/modules/users/services/main.service.ts b/src/app/modules/users/services/main.service.ts new file mode 100644 index 0000000..ac7a81f --- /dev/null +++ b/src/app/modules/users/services/main.service.ts @@ -0,0 +1,27 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { USERS_API_ROUTES } from '../constants'; +import { IUserRawResponse, IUserRequest, IUserResponse } from '../models'; + +@Injectable({ providedIn: 'root' }) +export class UsersService { + constructor(private http: HttpClient) {} + + private apiRoutes = USERS_API_ROUTES; + + getAll(): Observable { + return this.http.get(this.apiRoutes.list()); + } + getSingle(userId: string): Observable { + return this.http.get(this.apiRoutes.single(userId)); + } + + create(userData: IUserRequest): Observable { + return this.http.post(this.apiRoutes.list(), userData); + } + + update(userId: string, userData: IUserRequest): Observable { + return this.http.patch(this.apiRoutes.single(userId), userData); + } +} diff --git a/src/app/modules/users/views/index.ts b/src/app/modules/users/views/index.ts new file mode 100644 index 0000000..bc9dfc9 --- /dev/null +++ b/src/app/modules/users/views/index.ts @@ -0,0 +1,2 @@ +export * from './list.component'; +export * from './single.component'; diff --git a/src/app/modules/users/views/list.component.html b/src/app/modules/users/views/list.component.html new file mode 100644 index 0000000..439b495 --- /dev/null +++ b/src/app/modules/users/views/list.component.html @@ -0,0 +1,15 @@ + + + diff --git a/src/app/modules/users/views/list.component.ts b/src/app/modules/users/views/list.component.ts new file mode 100644 index 0000000..a762cc5 --- /dev/null +++ b/src/app/modules/users/views/list.component.ts @@ -0,0 +1,48 @@ +import { + IColumn, + PageDataListComponent, +} from '@/shared/components/pageDataList/page-data-list.component'; +import { Component, signal } from '@angular/core'; +import { UserFormComponent } from '../components/form.component'; +import { IUserResponse } from '../models'; +import { UsersService } from '../services/main.service'; + +@Component({ + selector: 'app-users', + templateUrl: './list.component.html', + imports: [PageDataListComponent, UserFormComponent], +}) +export class UsersComponent { + constructor(private userService: UsersService) { + this.getData(); + } + + columns = [ + { field: 'id', header: 'شناسه' }, + { field: 'firstName', header: 'نام' }, + { field: 'lastName', header: 'نام خانوادگی' }, + { field: 'mobileNumber', header: 'شماره موبایل' }, + { field: 'roleId', header: 'نقش' }, + { field: 'actions' }, + ] as IColumn[]; + + loading = signal(false); + items = signal([]); + visibleForm = signal(false); + + refresh() { + this.getData(); + } + + getData() { + this.loading.set(true); + this.userService.getAll().subscribe((res) => { + this.loading.set(false); + this.items.set(res); + }); + } + + openAddForm() { + this.visibleForm.set(true); + } +} diff --git a/src/app/modules/users/views/single.component.html b/src/app/modules/users/views/single.component.html new file mode 100644 index 0000000..7144e26 --- /dev/null +++ b/src/app/modules/users/views/single.component.html @@ -0,0 +1 @@ +
diff --git a/src/app/modules/users/views/single.component.ts b/src/app/modules/users/views/single.component.ts new file mode 100644 index 0000000..e9f64f3 --- /dev/null +++ b/src/app/modules/users/views/single.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-user', + templateUrl: './single.component.html', +}) +export class UserComponent { + constructor() {} +} diff --git a/src/app/pages/auth/access.ts b/src/app/pages/auth/access.ts index 4c62f2d..aba196c 100644 --- a/src/app/pages/auth/access.ts +++ b/src/app/pages/auth/access.ts @@ -2,31 +2,48 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { ButtonModule } from 'primeng/button'; import { RippleModule } from 'primeng/ripple'; -import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; @Component({ - selector: 'app-access', - standalone: true, - imports: [ButtonModule, RouterModule, RippleModule, AppFloatingConfigurator, ButtonModule], - template: ` -
-
-
-
-
-
- -
-

Access Denied

- You do not have the necessary permisions. Please contact admins. - Access denied -
- -
-
-
-
+ selector: 'app-access', + standalone: true, + imports: [ButtonModule, RouterModule, RippleModule, ButtonModule], + template: `
+
+
+
+
+
+
-
` +

+ Access Denied +

+ You do not have the necessary permisions. Please contact admins. + Access denied +
+ +
+
+
+
+
+
`, }) export class Access {} diff --git a/src/app/pages/auth/error.ts b/src/app/pages/auth/error.ts index d3fc6f2..a61019f 100644 --- a/src/app/pages/auth/error.ts +++ b/src/app/pages/auth/error.ts @@ -2,31 +2,46 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { ButtonModule } from 'primeng/button'; import { RippleModule } from 'primeng/ripple'; -import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; @Component({ - selector: 'app-error', - imports: [ButtonModule, RippleModule, RouterModule, AppFloatingConfigurator, ButtonModule], - standalone: true, - template: ` -
-
-
-
-
-
- -
-

Error Occured

- Requested resource is not available. - Error -
- -
-
-
-
+ selector: 'app-error', + imports: [ButtonModule, RippleModule, RouterModule, ButtonModule], + standalone: true, + template: `
+
+
+
+
+
+
-
` +

+ Error Occured +

+ Requested resource is not available. + Error +
+ +
+
+
+
+
+
`, }) export class Error {} diff --git a/src/app/pages/auth/login.ts b/src/app/pages/auth/login.ts index 0b48f0a..aa93066 100644 --- a/src/app/pages/auth/login.ts +++ b/src/app/pages/auth/login.ts @@ -6,66 +6,127 @@ import { CheckboxModule } from 'primeng/checkbox'; import { InputTextModule } from 'primeng/inputtext'; import { PasswordModule } from 'primeng/password'; import { RippleModule } from 'primeng/ripple'; -import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; @Component({ - selector: 'app-login', - standalone: true, - imports: [ButtonModule, CheckboxModule, InputTextModule, PasswordModule, FormsModule, RouterModule, RippleModule, AppFloatingConfigurator], - template: ` - -
-
-
-
-
- - - - - - - - - -
Welcome to PrimeLand!
- Sign in to continue -
- -
- - - - - - -
-
- - -
- Forgot password? -
- -
-
-
+ selector: 'app-login', + standalone: true, + imports: [ + ButtonModule, + CheckboxModule, + InputTextModule, + PasswordModule, + FormsModule, + RouterModule, + RippleModule, + ], + template: ` +
+
+
+
+
+ + + + + + + + + +
+ Welcome to PrimeLand! +
+ Sign in to continue
+ +
+ + + + + + +
+
+ + +
+ Forgot password? +
+ +
+
- ` +
+
+ `, }) export class Login { - email: string = ''; + email: string = ''; - password: string = ''; + password: string = ''; - checked: boolean = false; + checked: boolean = false; } diff --git a/src/app/pages/landing/components/featureswidget.ts b/src/app/pages/landing/components/featureswidget.ts deleted file mode 100644 index 68a459f..0000000 --- a/src/app/pages/landing/components/featureswidget.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Component } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -@Component({ - selector: 'features-widget', - standalone: true, - imports: [CommonModule], - template: `
-
-
-
Marvelous Features
- Placerat in egestas erat... -
- -
-
-
-
- -
-
Easy to Use
- Posuere morbi leo urna molestie. -
-
-
- -
-
-
-
- -
-
Fresh Design
- Semper risus in hendrerit. -
-
-
- -
-
-
-
- -
-
Well Documented
- Non arcu risus quis varius quam quisque. -
-
-
- -
-
-
-
- -
-
Responsive Layout
- Nulla malesuada pellentesque elit. -
-
-
- -
-
-
-
- -
-
Clean Code
- Condimentum lacinia quis vel eros. -
-
-
- -
-
-
-
- -
-
Dark Mode
- Convallis tellus id interdum velit laoreet. -
-
-
- -
-
-
-
- -
-
Ready to Use
- Mauris sit amet massa vitae. -
-
-
- -
-
-
-
- -
-
Modern Practices
- Elementum nibh tellus molestie nunc non. -
-
-
- -
-
-
-
- -
-
Privacy
- Neque egestas congue quisque. -
-
-
- -
-
-
Joséphine Miller
- Peak Interactive -

- “Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.” -

- Company logo -
-
-
-
` -}) -export class FeaturesWidget {} diff --git a/src/app/pages/landing/components/footerwidget.ts b/src/app/pages/landing/components/footerwidget.ts deleted file mode 100644 index a8cbd8d..0000000 --- a/src/app/pages/landing/components/footerwidget.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Component } from '@angular/core'; -import { Router, RouterModule } from '@angular/router'; - -@Component({ - selector: 'footer-widget', - imports: [RouterModule], - template: ` -
-
- - -
-
- - -
-

Resources

- Get Started - Learn - Case Studies -
- -
-

Community

- Discord - Eventsbadge - FAQ - Blog -
- - -
-
-
-
- ` -}) -export class FooterWidget { - constructor(public router: Router) {} -} diff --git a/src/app/pages/landing/components/herowidget.ts b/src/app/pages/landing/components/herowidget.ts deleted file mode 100644 index fbbf017..0000000 --- a/src/app/pages/landing/components/herowidget.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Component } from '@angular/core'; -import { ButtonModule } from 'primeng/button'; -import { RippleModule } from 'primeng/ripple'; - -@Component({ - selector: 'hero-widget', - imports: [ButtonModule, RippleModule], - template: ` -
-
-

Eu sem integereget magna fermentum

-

Sed blandit libero volutpat sed cras. Fames ac turpis egestas integer. Placerat in egestas erat...

- -
-
- Hero Image -
-
- ` -}) -export class HeroWidget {} diff --git a/src/app/pages/landing/components/highlightswidget.ts b/src/app/pages/landing/components/highlightswidget.ts deleted file mode 100644 index 8e80e2d..0000000 --- a/src/app/pages/landing/components/highlightswidget.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'highlights-widget', - template: ` -
-
-
Powerful Everywhere
- Amet consectetur adipiscing elit... -
- -
-
- mockup mobile -
- -
-
- -
-
Congue Quisque Egestas
- Lectus arcu bibendum at varius vel pharetra vel turpis nunc. Eget aliquet nibh praesent tristique magna sit amet purus gravida. Sit amet mattis vulputate enim nulla aliquet. -
-
- -
-
-
- -
-
Celerisque Eu Ultrices
- Adipiscing commodo elit at imperdiet dui. Viverra nibh cras pulvinar mattis nunc sed blandit libero. Suspendisse in est ante in. Mauris pharetra et ultrices neque ornare aenean euismod elementum nisi. -
- -
- mockup -
-
-
- ` -}) -export class HighlightsWidget {} diff --git a/src/app/pages/landing/components/pricingwidget.ts b/src/app/pages/landing/components/pricingwidget.ts deleted file mode 100644 index f091a2c..0000000 --- a/src/app/pages/landing/components/pricingwidget.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Component } from '@angular/core'; -import { ButtonModule } from 'primeng/button'; -import { DividerModule } from 'primeng/divider'; -import { RippleModule } from 'primeng/ripple'; - -@Component({ - selector: 'pricing-widget', - imports: [DividerModule, ButtonModule, RippleModule], - template: ` -
-
-
Matchless Pricing
- Amet consectetur adipiscing elit... -
- -
-
-
-
Free
- free -
-
- $0 - per month -
- -
- -
    -
  • - - Responsive Layout -
  • -
  • - - Unlimited Push Messages -
  • -
  • - - 50 Support Ticket -
  • -
  • - - Free Shipping -
  • -
-
-
- -
-
-
Startup
- startup -
-
- $1 - per month -
- -
- -
    -
  • - - Responsive Layout -
  • -
  • - - Unlimited Push Messages -
  • -
  • - - 50 Support Ticket -
  • -
  • - - Free Shipping -
  • -
-
-
- -
-
-
Enterprise
- enterprise -
-
- $5 - per month -
- -
- -
    -
  • - - Responsive Layout -
  • -
  • - - Unlimited Push Messages -
  • -
  • - - 50 Support Ticket -
  • -
  • - - Free Shipping -
  • -
-
-
-
-
- ` -}) -export class PricingWidget {} diff --git a/src/app/pages/landing/components/topbarwidget.component.ts b/src/app/pages/landing/components/topbarwidget.component.ts deleted file mode 100644 index 879c0a0..0000000 --- a/src/app/pages/landing/components/topbarwidget.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component } from '@angular/core'; -import { StyleClassModule } from 'primeng/styleclass'; -import { Router, RouterModule } from '@angular/router'; -import { RippleModule } from 'primeng/ripple'; -import { ButtonModule } from 'primeng/button'; -import {AppFloatingConfigurator} from "@/layout/component/app.floatingconfigurator"; - -@Component({ - selector: 'topbar-widget', - imports: [RouterModule, StyleClassModule, ButtonModule, RippleModule, AppFloatingConfigurator], - template: ` - - - - - - - - - - SAKAI - - - - - - - ` -}) -export class TopbarWidget { - constructor(public router: Router) {} -} diff --git a/src/app/pages/landing/landing.ts b/src/app/pages/landing/landing.ts deleted file mode 100644 index 56d3e46..0000000 --- a/src/app/pages/landing/landing.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { RippleModule } from 'primeng/ripple'; -import { StyleClassModule } from 'primeng/styleclass'; -import { ButtonModule } from 'primeng/button'; -import { DividerModule } from 'primeng/divider'; -import { TopbarWidget } from './components/topbarwidget.component'; -import { HeroWidget } from './components/herowidget'; -import { FeaturesWidget } from './components/featureswidget'; -import { HighlightsWidget } from './components/highlightswidget'; -import { PricingWidget } from './components/pricingwidget'; -import { FooterWidget } from './components/footerwidget'; - -@Component({ - selector: 'app-landing', - standalone: true, - imports: [RouterModule, TopbarWidget, HeroWidget, FeaturesWidget, HighlightsWidget, PricingWidget, FooterWidget, RippleModule, StyleClassModule, ButtonModule, DividerModule], - template: ` -
-
- - - - - - -
-
- ` -}) -export class Landing {} diff --git a/src/app/pages/notfound/notfound.component.html b/src/app/pages/notfound/notfound.component.html new file mode 100644 index 0000000..d388b20 --- /dev/null +++ b/src/app/pages/notfound/notfound.component.html @@ -0,0 +1,52 @@ +
+
+
+ + صندوق فروشگاهی +
+
+
+ 404 +

صفحه مورد نظر یافت نشد

+
+ متاسفانه صفحه ای که به دنبال آن هستید وجود ندارد یا ممکن است حذف شده باشد. +
+
+ @for (item of notFoundListItems; track item) { + + + + + + {{ + item.title + }} + {{ item.description }} + + + } +
+ +
+
+
+
diff --git a/src/app/pages/notfound/notfound.component.ts b/src/app/pages/notfound/notfound.component.ts new file mode 100644 index 0000000..56ce444 --- /dev/null +++ b/src/app/pages/notfound/notfound.component.ts @@ -0,0 +1,29 @@ +import { Component, inject } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { ButtonModule } from 'primeng/button'; +import images from 'src/assets/images'; + +@Component({ + selector: 'app-notfound', + standalone: true, + imports: [RouterModule, ButtonModule], + templateUrl: './notfound.component.html', +}) +export class Notfound { + readonly logo = images.logo; + + readonly notFoundListItems = [ + { + icon: 'pi-table', + title: 'سوالات متداول', + description: 'لیست سوالات متداول', + link: '/', + }, + { + icon: 'pi-question-circle', + title: 'تیکت پشتیبانی', + description: 'از این طریق می‌توانید با پشتیبانی در ارتباط باشید', + link: '/', + }, + ]; +} diff --git a/src/app/pages/notfound/notfound.ts b/src/app/pages/notfound/notfound.ts deleted file mode 100644 index 07ef44a..0000000 --- a/src/app/pages/notfound/notfound.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Component } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { ButtonModule } from 'primeng/button'; -import { AppFloatingConfigurator } from '../../layout/component/app.floatingconfigurator'; - -@Component({ - selector: 'app-notfound', - standalone: true, - imports: [RouterModule, AppFloatingConfigurator, ButtonModule], - template: ` - ` -}) -export class Notfound {} diff --git a/src/app/shared/components/input/input.component.html b/src/app/shared/components/input/input.component.html index e744cf1..a766364 100644 --- a/src/app/shared/components/input/input.component.html +++ b/src/app/shared/components/input/input.component.html @@ -1,17 +1,21 @@ - + @if (type === "switch") { + + } @else { + + } diff --git a/src/app/shared/components/input/input.component.ts b/src/app/shared/components/input/input.component.ts index f5c881e..a23193b 100644 --- a/src/app/shared/components/input/input.component.ts +++ b/src/app/shared/components/input/input.component.ts @@ -4,15 +4,17 @@ import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms'; import { InputTextModule } from 'primeng/inputtext'; +import { ToggleSwitch } from 'primeng/toggleswitch'; @Component({ selector: 'app-input', standalone: true, - imports: [CommonModule, ReactiveFormsModule, InputTextModule, UikitFieldComponent], + imports: [CommonModule, ReactiveFormsModule, InputTextModule, UikitFieldComponent, ToggleSwitch], templateUrl: './input.component.html', }) export class InputComponent { - @Input() type: 'simple' | 'postalCode' | 'mobile' | 'phone' | 'email' = 'simple'; + @Input() type: 'simple' | 'postalCode' | 'mobile' | 'phone' | 'email' | 'checkbox' | 'switch' = + 'simple'; @Input() control!: FormControl>; @Input() name!: string; @Input() label: string = ''; 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 bacaa3a..dff5a06 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.html +++ b/src/app/shared/components/pageDataList/page-data-list.component.html @@ -83,7 +83,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) { 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 fb1be86..2b6c01b 100644 --- a/src/app/shared/components/pageDataList/page-data-list.component.ts +++ b/src/app/shared/components/pageDataList/page-data-list.component.ts @@ -80,9 +80,9 @@ export class PageDataListComponent { filterDrawerVisible = signal(false); ngOnInit() { - if (this.fullHeight) { - this.renderer.setStyle(this.host.nativeElement, 'height', '100cqmin'); - } + // if (this.fullHeight) { + // this.renderer.setStyle(this.host.nativeElement, 'height', '100cqmin'); + // } } edit = (item: I) => { diff --git a/src/app/uikit/uikit-emptystate.component.html b/src/app/uikit/uikit-emptystate.component.html index b6ce4b5..6e86269 100644 --- a/src/app/uikit/uikit-emptystate.component.html +++ b/src/app/uikit/uikit-emptystate.component.html @@ -1,6 +1,6 @@
- + {{ title }} @if (description) { diff --git a/src/app/uikit/uikit-emptystate.component.ts b/src/app/uikit/uikit-emptystate.component.ts index ee70566..03384ea 100644 --- a/src/app/uikit/uikit-emptystate.component.ts +++ b/src/app/uikit/uikit-emptystate.component.ts @@ -1,36 +1,36 @@ import { - AfterContentInit, - Component, - ContentChild, - ElementRef, - EventEmitter, - Input, - Output, + AfterContentInit, + Component, + ContentChild, + ElementRef, + EventEmitter, + Input, + Output, } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Button } from 'primeng/button'; @Component({ - selector: 'uikit-empty-state', - standalone: true, - imports: [CommonModule, Button], - templateUrl: './uikit-emptystate.component.html', + selector: 'uikit-empty-state', + standalone: true, + imports: [CommonModule, Button], + templateUrl: './uikit-emptystate.component.html', }) export class UikitEmptyStateComponent implements AfterContentInit { - @ContentChild('cta', { static: false, read: ElementRef }) - ctaContent?: ElementRef; - hasCtaContent = false; + @ContentChild('cta', { static: false, read: ElementRef }) + ctaContent?: ElementRef; + hasCtaContent = false; - @Input() title: string = 'متاسفانه موردی یافت نشد.'; - @Input() description?: string; - @Input() icon: string = 'pi pi-database'; - @Input() ctaLabel?: string; - @Input() showCTA?: boolean = true; + @Input() title: string = 'متاسفانه موردی یافت نشد.'; + @Input() description?: string; + @Input() icon: string = 'pi pi-database'; + @Input() ctaLabel?: string; + @Input() showCTA?: boolean = true; - @Output() ctaClick = new EventEmitter(); + @Output() ctaClick = new EventEmitter(); - ngAfterContentInit() { - this.hasCtaContent = !!this.ctaContent; - } + ngAfterContentInit() { + this.hasCtaContent = !!this.ctaContent; + } } diff --git a/src/assets/images/index.ts b/src/assets/images/index.ts index 1d6d6d2..638f57b 100644 --- a/src/assets/images/index.ts +++ b/src/assets/images/index.ts @@ -2,7 +2,7 @@ // The Angular CLI / build serves files in `src/assets` at `/assets`. import errors from './errors'; import login from './login.webp'; -import logo from './logo.webp'; +import logo from './logo.png'; export default { logo, diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png index d9b4cf5a9bc578723957e52970501c02220cf4bd..f82b4c3751acfee55e6fb65da891442596dd8700 100644 GIT binary patch literal 26810 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 OK$PU}%Vyp)di8&-EZUI( literal 56774 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

Hz0ncpEhsqy{%VOIo^34}x2J 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%0Db*d2kbj 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 E0ysw>7XSbN diff --git a/src/assets/layout/_main.scss b/src/assets/layout/_main.scss index 75831da..85ffa8b 100644 --- a/src/assets/layout/_main.scss +++ b/src/assets/layout/_main.scss @@ -15,5 +15,5 @@ .layout-main { flex: 1 1 auto; - overflow: hidden; + overflow: auto; } diff --git a/src/environments/environment.ts b/src/environments/environment.ts index e789966..15584fc 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,9 +1,9 @@ // Development environment configuration export const environment = { - production: false, - apiBaseUrl: 'https://localhost', - host: 'localhost', - port: 60749, - enableLogging: true, - enableDebug: true, + production: false, + apiBaseUrl: 'http://localhost:3000', + host: 'localhost', + port: 60749, + enableLogging: true, + enableDebug: true, }; diff --git a/src/index.html b/src/index.html index 82ca35a..ddfd142 100644 --- a/src/index.html +++ b/src/index.html @@ -5,9 +5,8 @@ pos - - - + + diff --git a/src/proxy.conf.js b/src/proxy.conf.js index 8d37aee..331f35e 100644 --- a/src/proxy.conf.js +++ b/src/proxy.conf.js @@ -1,16 +1,16 @@ -const { env } = require('process'); +// const { env } = require('process'); -const target = env.ASPNETCORE_HTTPS_PORT - ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` - : env.ASPNETCORE_URLS - ? env.ASPNETCORE_URLS.split(';')[0] - : 'https://localhost:7148'; +// const target = env.ASPNETCORE_HTTPS_PORT +// ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` +// : env.ASPNETCORE_URLS +// ? env.ASPNETCORE_URLS.split(';')[0] +// : 'https://localhost:7148'; -const PROXY_CONFIG = [ - { - target, - secure: false, - }, -]; +// const PROXY_CONFIG = [ +// { +// target, +// secure: false, +// }, +// ]; -module.exports = PROXY_CONFIG; +// module.exports = PROXY_CONFIG;