diff --git a/Dockerfile b/Dockerfile index 5036166..dc9cdb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ # Build stage FROM node:20 AS builder +ARG TENANT=tis +ARG DIST_DIR=tis + WORKDIR /app # RUN npm config set registry "https://hub.megan.ir/npm/" @@ -11,14 +14,20 @@ RUN pnpm install COPY . . -RUN pnpm run build +RUN if [ "$TENANT" = "default" ]; then \ + pnpm run build; \ + else \ + pnpm run build:$TENANT; \ + fi FROM nginx:alpine +ARG DIST_DIR=tis + COPY nginx.conf /etc/nginx/nginx.conf -COPY --from=builder /app/dist/pos.client/browser /usr/share/nginx/html +COPY --from=builder /app/dist/${DIST_DIR}/browser /usr/share/nginx/html -EXPOSE 4001 +EXPOSE 5000 CMD ["nginx", "-g", "daemon off;"] diff --git a/WEBVIEW_BRIDGE.md b/WEBVIEW_BRIDGE.md new file mode 100644 index 0000000..d148547 --- /dev/null +++ b/WEBVIEW_BRIDGE.md @@ -0,0 +1,132 @@ +# WebView Bridge Contract + +This document defines the Android WebView bridge contract used by tenant builds like `tis`. + +## Scope + +- The bridge is enabled per tenant via `environment.enableNativeBridge`. +- Current tenant config: + - `src/environments/environment.tis.ts` -> `enableNativeBridge: true` + - other environments -> `false` + +## JavaScript host objects + +The web app checks bridge methods on: + +1. `window.AndroidBridge` +2. `window.Android` (fallback) + +Your Android app can expose either one. + +## Required native methods + +Expose these synchronous methods through `@JavascriptInterface`: + +- `pay(payloadJson: String): String` +- `print(payloadJson: String): String` + +Both methods must return a JSON string. + +## Return JSON format + +Success: + +```json +{ + "success": true, + "data": {} +} +``` + +Failure: + +```json +{ + "success": false, + "error": "Reason message" +} +``` + +If a non-JSON string is returned, the app treats it as success with raw data. + +## Payload schemas + +### pay(payload) + +Called before invoice submit when terminal amount is greater than zero. + +```json +{ + "amount": 1200000, + "totalAmount": 3500000, + "invoiceDate": "2026-04-27T10:30:00.000Z" +} +``` + +Fields: + +- `amount` number: terminal payment amount. +- `totalAmount` number: full invoice total. +- `invoiceDate` string: ISO date-time. + +### print(payload) + +Called after successful invoice creation (and from invoice view print action). + +```json +{ + "invoiceId": "a7e1f2...", + "code": "INV-1745730090000" +} +``` + +Fields are optional, but should be used when available. + +## Web behavior summary + +- On native pay failure: + - Invoice submit is stopped. + - User sees warning toast. +- On native print failure or missing bridge: + - In invoice page, app falls back to `window.print()`. + - In POS checkout flow, print failure does not block successful submit. + +## Android integration example (Kotlin) + +```kotlin +class AndroidBridge( + private val context: Context +) { + @JavascriptInterface + fun pay(payloadJson: String): String { + return try { + // TODO: parse payloadJson and run POS payment SDK synchronously + """{"success":true,"data":{"txId":"12345"}}""" + } catch (e: Exception) { + """{"success":false,"error":"${e.message ?: "Payment failed"}"}""" + } + } + + @JavascriptInterface + fun print(payloadJson: String): String { + return try { + // TODO: parse payloadJson and print with printer SDK + """{"success":true}""" + } catch (e: Exception) { + """{"success":false,"error":"${e.message ?: "Print failed"}"}""" + } + } +} +``` + +Attach to WebView: + +```kotlin +webView.settings.javaScriptEnabled = true +webView.addJavascriptInterface(AndroidBridge(this), "AndroidBridge") +``` + +## Notes + +- For production, use HTTPS for web app + API to avoid mixed-content issues. +- Keep method names and payload keys exactly as specified. diff --git a/angular.json b/angular.json index 564b7ee..5bf3c3e 100644 --- a/angular.json +++ b/angular.json @@ -34,7 +34,9 @@ "with": "src/environments/environment.prod.ts" } ], - "outputHashing": "all" + "outputHashing": "all", + "outputPath": "dist/pos.client", + "serviceWorker": "ngsw-config.json" }, "staging": { "extractLicenses": true, @@ -46,7 +48,45 @@ ], "optimization": true, "outputHashing": "all", + "serviceWorker": "ngsw-config.json", "sourceMap": false + }, + "tis": { + "assets": [ + { + "glob": "**/*", + "input": "public-tis" + } + ], + "budgets": [ + { + "maximumError": "3MB", + "maximumWarning": "2MB", + "type": "initial" + }, + { + "maximumError": "8kB", + "maximumWarning": "4kB", + "type": "anyComponentStyle" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.tis.ts" + }, + { + "replace": "src/app.routes.ts", + "with": "src/tenants/tis/app.routes.ts" + }, + { + "replace": "src/app/branding/branding.config.ts", + "with": "src/tenants/tis/branding.config.ts" + } + ], + "outputHashing": "all", + "outputPath": "dist/tis", + "serviceWorker": "ngsw-config.json" } }, "defaultConfiguration": "production", @@ -86,12 +126,14 @@ }, "staging": { "buildTarget": "pos.client:build:staging" + }, + "tis": { + "buildTarget": "pos.client:build:tis" } }, "defaultConfiguration": "development", "options": { - "port": 5000, - "proxyConfig": "src/proxy.conf.js" + "port": 5000 } }, "test": { diff --git a/docker-compose.yml b/docker-compose.yml index 6f60819..8b20d76 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,9 @@ services: build: context: . dockerfile: Dockerfile + args: + TENANT: ${TENANT:-tis} + DIST_DIR: ${DIST_DIR:-tis} container_name: psp_panel_prod ports: - "5000:5000" @@ -14,7 +17,7 @@ services: - NODE_ENV=production restart: unless-stopped healthcheck: - test: [ "CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/" ] + test: [ "CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:5000/" ] interval: 30s timeout: 3s retries: 3 diff --git a/nginx.conf b/nginx.conf index b5d1eac..1ca0967 100644 --- a/nginx.conf +++ b/nginx.conf @@ -55,6 +55,17 @@ http { add_header Cache-Control "public, immutable"; } + # Service worker files should be revalidated on each request + location = /ngsw.json { + expires -1; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + + location = /ngsw-worker.js { + expires -1; + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } + # Angular routing - serve index.html for all routes location / { try_files $uri $uri/ /index.html; diff --git a/ngsw-config.json b/ngsw-config.json new file mode 100644 index 0000000..bbbb970 --- /dev/null +++ b/ngsw-config.json @@ -0,0 +1,66 @@ +{ + "$schema": "./node_modules/@angular/service-worker/config/schema.json", + "appData": { + "appVersion": "0.0.0", + "buildDate": "2026-04-27T06:02:22.249Z" + }, + "assetGroups": [ + { + "installMode": "prefetch", + "name": "app", + "resources": { + "files": [ + "/favicon.ico", + "/index.html", + "/*.css", + "/*.js" + ] + }, + "updateMode": "prefetch" + }, + { + "installMode": "lazy", + "name": "assets", + "resources": { + "files": [ + "/favicon/**", + "/assets/**", + "/**/*.svg", + "/**/*.png", + "/**/*.jpg", + "/**/*.jpeg", + "/**/*.webp", + "/**/*.gif", + "/**/*.woff", + "/**/*.woff2", + "/**/*.ttf", + "/**/*.otf" + ] + }, + "updateMode": "prefetch" + } + ], + "dataGroups": [ + { + "cacheConfig": { + "maxAge": "1h", + "maxSize": 100, + "strategy": "freshness", + "timeout": "10s" + }, + "name": "api-fresh", + "urls": [ + "/api/**", + "https://*/api/**", + "http://*/api/**" + ] + } + ], + "index": "/index.html", + "navigationUrls": [ + "/**", + "!/**/*.*", + "!/**/*__*", + "!/**/*__*/**" + ] +} diff --git a/package.json b/package.json index ff29732..d2084df 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@angular/platform-browser": "^20.3.19", "@angular/platform-browser-dynamic": "^20.3.19", "@angular/router": "^20.3.19", + "@angular/service-worker": "^20.3.19", "@primeng/themes": "^20.4.0", "@primeuix/themes": "^1.2.5", "@tailwindcss/postcss": "^4.2.3", @@ -45,6 +46,7 @@ "karma-coverage": "~2.2.1", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "madge": "^8.0.0", "postcss": "^8.5.10", "prettier": "^3.8.3", "prettier-plugin-organize-imports": "^4.3.0", @@ -66,9 +68,13 @@ "private": true, "scripts": { "build": "ng build", + "build:tis": "ng build --configuration tis", "ng": "ng", + "prebuild": "node scripts/update-ngsw-appdata.js", + "prebuild:tis": "node scripts/update-ngsw-appdata.js", "prestart": "node aspnetcore-https", "start": "run-script-os", + "start:tis": "ng serve --configuration tis", "test": "ng test", "watch": "ng build --watch --configuration development" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dee4e6f..7af0a38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@angular/router': specifier: ^20.3.19 version: 20.3.19(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(rxjs@7.8.2) + '@angular/service-worker': + specifier: ^20.3.19 + version: 20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2) '@primeng/themes': specifier: ^20.4.0 version: 20.4.0 @@ -89,10 +92,10 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: ^20.3.24 - version: 20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(lightningcss@1.32.0)(tailwindcss@4.2.3)(tsx@4.21.0)(typescript@5.8.3) + version: 20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(lightningcss@1.32.0)(tailwindcss@4.2.3)(tsx@4.21.0)(typescript@5.8.3) '@angular/build': specifier: ^20.3.24 - version: 20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.10)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3) + version: 20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.10)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3) '@angular/cli': specifier: ^20.3.24 version: 20.3.24(@types/node@25.6.0)(chokidar@4.0.3) @@ -138,6 +141,9 @@ importers: karma-jasmine-html-reporter: specifier: ~2.1.0 version: 2.1.0(jasmine-core@5.8.0)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4) + madge: + specifier: ^8.0.0 + version: 8.0.0(typescript@5.8.3) postcss: specifier: ^8.5.10 version: 8.5.10 @@ -434,6 +440,14 @@ packages: '@angular/platform-browser': 20.3.19 rxjs: ^6.5.3 || ^7.4.0 + '@angular/service-worker@20.3.19': + resolution: {integrity: sha512-djGCXrKw3TavjwKydtR5DXOZaSq9CK6O29OCDC4pCjw4SJmT5bV/PnntOH7jlpxzvWD9/ofO/89M1e2lBzoAWA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@angular/core': 20.3.19 + rxjs: ^6.5.3 || ^7.4.0 + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -1052,6 +1066,10 @@ packages: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + '@dependents/detective-less@5.0.3': + resolution: {integrity: sha512-v6oD9Ukp+N7V4n6p5I/+mM5fIohSfkrDSGlFm5w/pYmchvbk+sMIHsLxrFJ5Lnujewj1BzWL0K84d88lwZAMQA==} + engines: {node: '>=18'} + '@discoveryjs/json-ext@0.6.3': resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} engines: {node: '>=14.17.0'} @@ -2524,6 +2542,22 @@ packages: '@tailwindcss/postcss@4.2.3': resolution: {integrity: sha512-MehdHOQRVFf300r8F430s4cf2QL+nSjFUNIndX5ZMqDLyMwTnyL4RDZsoDsDU+ThzT5eCj1+erSDKBWdn462Nw==} + '@ts-graphviz/adapter@2.0.6': + resolution: {integrity: sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==} + engines: {node: '>=18'} + + '@ts-graphviz/ast@2.0.7': + resolution: {integrity: sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw==} + engines: {node: '>=18'} + + '@ts-graphviz/common@2.1.5': + resolution: {integrity: sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==} + engines: {node: '>=18'} + + '@ts-graphviz/core@2.0.7': + resolution: {integrity: sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg==} + engines: {node: '>=18'} + '@ts-morph/common@0.22.0': resolution: {integrity: sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==} @@ -2646,6 +2680,32 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + '@typescript-eslint/project-service@8.59.0': + resolution: {integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/tsconfig-utils@8.59.0': + resolution: {integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.0': + resolution: {integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.0': + resolution: {integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.0': + resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -2655,6 +2715,21 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 + '@vue/compiler-core@3.5.33': + resolution: {integrity: sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==} + + '@vue/compiler-dom@3.5.33': + resolution: {integrity: sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==} + + '@vue/compiler-sfc@3.5.33': + resolution: {integrity: sha512-UTUvRO9cY+rROrx/pvN9P5Z7FgA6QGfokUCfhQE4EnmUj3rVnK+CHI0LsEO1pg+I7//iRYMUfcNcCPe7tg0CoA==} + + '@vue/compiler-ssr@3.5.33': + resolution: {integrity: sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==} + + '@vue/shared@3.5.33': + resolution: {integrity: sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==} + '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -2824,10 +2899,16 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + app-module-path@2.2.0: + resolution: {integrity: sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -2861,6 +2942,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + ast-module-types@6.0.1: + resolution: {integrity: sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA==} + engines: {node: '>=18'} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -3128,9 +3213,20 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -3282,6 +3378,10 @@ packages: supports-color: optional: true + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -3320,6 +3420,11 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + dependency-tree@11.4.3: + resolution: {integrity: sha512-Y2gzOJ2Rb2X7MN6pT9llWpXxl5J5s5/11CBpJ5b85DjEqZH7jv3T9RO6HRV/PI/3MDmaKn/g7uoYdYmSb9vLlw==} + engines: {node: '>=18'} + hasBin: true + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -3331,6 +3436,49 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + detective-amd@6.1.0: + resolution: {integrity: sha512-fmI6LGMvotqd49QaA3ZYw+q0aGp2yXmMjzIuY6fH9j9YFIXY/73yDhMwhX9cPbhWd+AH06NH1Di/LKOuCH0Ubg==} + engines: {node: '>=18'} + hasBin: true + + detective-cjs@6.1.1: + resolution: {integrity: sha512-pSh7mkCKEtLlmANqLu3KDFS3NV8Hx41jy/JF1/gAWOgU+Uo5QTkeI1tWNP4dWGo4L0E9j18Ez9EPsTleautKqA==} + engines: {node: '>=18'} + + detective-es6@5.0.2: + resolution: {integrity: sha512-+qHHGYhjupiVs4rnIpI9nZ5B130A4AmE35ZX1w33hb46vcZ7T3jfDbvmPw0FhWtMHn5BS5HHu7ZtnZ53bMcXZA==} + engines: {node: '>=18'} + + detective-postcss@7.0.1: + resolution: {integrity: sha512-bEOVpHU9picRZux5XnwGsmCN4+8oZo7vSW0O0/Enq/TO5R2pIAP2279NsszpJR7ocnQt4WXU0+nnh/0JuK4KHQ==} + engines: {node: ^14.0.0 || >=16.0.0} + peerDependencies: + postcss: ^8.4.47 + + detective-sass@6.0.2: + resolution: {integrity: sha512-i3xpXHDKS0qI2aFW4asQ7fqlPK00ndOVZELvQapFJCaF0VxYmsNWtd0AmvXbTLMk7bfO5VdIeorhY9KfmHVoVA==} + engines: {node: '>=18'} + + detective-scss@5.0.2: + resolution: {integrity: sha512-9JOEMZ8pDh3ShXmftq7hoQqqJsClaGgxo1hghfCeFlmKf5TC/Twtwb0PAaK8dXwpg9Z0uCmEYSrCxO+kel2eEg==} + engines: {node: '>=18'} + + detective-stylus@5.0.1: + resolution: {integrity: sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA==} + engines: {node: '>=18'} + + detective-typescript@14.1.2: + resolution: {integrity: sha512-bIeEn0eVi/JRsE1YizBR2ilnMlWRAIBJJ6kXCKNFxEEWhUcEY3R6I3KYIAy48ieURbD1hcb3Ebvl8AqeoPMSzg==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 || ^6.0.2 + + detective-vue2@2.3.0: + resolution: {integrity: sha512-3gwbZPqVTm9sL9XdZsgEJ7x4x99O853VVZHapQAiEkGuMJMpFPjHDrecSgfqnS5JW3FJfYXesLZGvUOibjn49g==} + engines: {node: '>=18'} + peerDependencies: + typescript: ^5.4.4 || ^6.0.2 + di@0.0.1: resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} @@ -3505,6 +3653,11 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-config-prettier@10.1.8: resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true @@ -3580,6 +3733,10 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3615,6 +3772,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -3711,6 +3871,11 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filing-cabinet@5.4.0: + resolution: {integrity: sha512-wLWf4M3lDRoFB2Tp8/ueMhT9i2iahDAOUcBSY6oeReIo0d/3z2U8yEZsfQ3HpDKBlarnHTjDfOK77g5bpYNqVg==} + engines: {node: '>=18'} + hasBin: true + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -3828,6 +3993,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-amd-module-type@6.0.2: + resolution: {integrity: sha512-7zShVYAYtMnj9S65CfN+hvpBCByfuB1OY8xID01nZEzXTZbx4YyysAfi+nMl95JSR6odt4q8TCj2W63KAoyVLQ==} + engines: {node: '>=18'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -3840,6 +4009,9 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} @@ -3892,6 +4064,11 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} + gonzales-pe@4.3.0: + resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} + engines: {node: '>=0.6.0'} + hasBin: true + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -4047,6 +4224,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@5.0.0: resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} engines: {node: ^18.17.0 || >=20.5.0} @@ -4180,6 +4360,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + is-plain-obj@3.0.0: resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} engines: {node: '>=10'} @@ -4199,6 +4383,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} @@ -4231,6 +4419,13 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-url-superb@4.0.0: + resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} + engines: {node: '>=10'} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -4626,6 +4821,16 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + madge@8.0.0: + resolution: {integrity: sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: ^5.4.4 + peerDependenciesMeta: + typescript: + optional: true + magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -4784,6 +4989,16 @@ packages: engines: {node: '>=10'} hasBin: true + module-definition@6.0.2: + resolution: {integrity: sha512-SvAU3lB0+Yjbq55yHY3wkRZBOh+fhU1SnIF3IFbTewv6mtAh7yUT8ACHAJ2mGIJ7tCes2QuCL/cl6m0JSZ/ArA==} + engines: {node: '>=18'} + hasBin: true + + module-lookup-amd@9.1.2: + resolution: {integrity: sha512-HFEiUNm8/woZFJZcd42wrovEHjHN6nwfNjf2CjiVLbVFRbj+sEmEJn0mrx8JY4/qJP8wSZTtmguikAJBqEuRRQ==} + engines: {node: '>=18'} + hasBin: true + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -4893,6 +5108,10 @@ packages: node-releases@2.0.37: resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + node-source-walk@7.0.2: + resolution: {integrity: sha512-71kFFjYaSshDTA8/a2HiTYPLdASWjLJxUyJxGE+ffxU+KhxSBtM9kiLUX+R2yooFdSFKMFpi4n3PFtDy6qXv8A==} + engines: {node: '>=18'} + nopt@9.0.0: resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} engines: {node: ^20.17.0 || >=22.9.0} @@ -5071,6 +5290,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@2.1.0: + resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} + engines: {node: '>=6'} + parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} @@ -5143,6 +5366,10 @@ packages: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -5194,6 +5421,12 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss-values-parser@6.0.2: + resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.2.9 + postcss@8.5.10: resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} engines: {node: ^10 || ^12 || >=14} @@ -5202,6 +5435,11 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + precinct@12.3.1: + resolution: {integrity: sha512-wGyTIvtxh2S2NAHxTJj0YymxWOIcEDotu17yHoQUd2Bz2C07LrS28L1nvXDMxrCHvHmV6KTlaIQy5PzRm7Y8rg==} + engines: {node: '>=18'} + hasBin: true + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -5288,6 +5526,10 @@ packages: resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + pretty-ms@7.0.1: + resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} + engines: {node: '>=10'} + primeclt@0.1.5: resolution: {integrity: sha512-9gSu9yFNWq4GfSisqrHRapi06WsnqqPwu8wCEaHtPZEZcydFpWRKtoi4kFRZuFqrVE+dvNgH1AQ6usAB7a1upg==} engines: {node: '>=14.0.0', npm: '>=6.0.0'} @@ -5352,6 +5594,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + quote-unquote@1.0.0: + resolution: {integrity: sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -5364,6 +5609,10 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -5422,9 +5671,22 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + requirejs-config-file@4.0.0: + resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} + engines: {node: '>=10.13.0'} + + requirejs@2.3.8: + resolution: {integrity: sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==} + engines: {node: '>=0.4.0'} + hasBin: true + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resolve-dependency-path@4.0.1: + resolution: {integrity: sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ==} + engines: {node: '>=18'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -5552,6 +5814,11 @@ packages: webpack: optional: true + sass-lookup@6.1.2: + resolution: {integrity: sha512-GjmndmKQBtlPil79RK72L7yc5kDXZPCQeH97bP8R8DcxtXQJO6vECExb3WP/m6+cxaV9h4ZxrSRvCkPG2v/VSw==} + engines: {node: '>=18'} + hasBin: true + sass@1.90.0: resolution: {integrity: sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==} engines: {node: '>=14.0.0'} @@ -5774,6 +6041,9 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + stream-to-array@2.3.0: + resolution: {integrity: sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==} + streamroller@3.1.5: resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} engines: {node: '>=8.0'} @@ -5804,6 +6074,10 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -5816,10 +6090,19 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylus-lookup@6.1.2: + resolution: {integrity: sha512-O+Q/SJ8s1X2aMLh4213fQ9X/bND9M3dhSsyTRe+O1OXPcewGLiYmAtKCrnP7FDvDBaXB2ZHPkCt3zi4cJXBlCQ==} + engines: {node: '>=18'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -5922,12 +6205,26 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-graphviz@2.1.6: + resolution: {integrity: sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw==} + engines: {node: '>=18'} + ts-morph@21.0.1: resolution: {integrity: sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==} tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -5980,6 +6277,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + ua-parser-js@0.7.41: resolution: {integrity: sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==} hasBin: true @@ -6090,6 +6392,10 @@ packages: resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==} engines: {node: '>=0.10.0'} + walkdir@0.4.1: + resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} + engines: {node: '>=6.0.0'} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -6408,13 +6714,13 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(lightningcss@1.32.0)(tailwindcss@4.2.3)(tsx@4.21.0)(typescript@5.8.3)': + '@angular-devkit/build-angular@20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(lightningcss@1.32.0)(tailwindcss@4.2.3)(tsx@4.21.0)(typescript@5.8.3)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.24(chokidar@4.0.3) '@angular-devkit/build-webpack': 0.2003.24(chokidar@4.0.3)(webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.105.0))(webpack@5.105.0(esbuild@0.28.0)) '@angular-devkit/core': 20.3.24(chokidar@4.0.3) - '@angular/build': 20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.6)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3) + '@angular/build': 20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.6)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3) '@angular/compiler-cli': 20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3) '@babel/core': 7.28.3 '@babel/generator': 7.28.3 @@ -6469,6 +6775,7 @@ snapshots: optionalDependencies: '@angular/core': 20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2) '@angular/platform-browser': 20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)) + '@angular/service-worker': 20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2) esbuild: 0.28.0 karma: 6.4.4 tailwindcss: 4.2.3 @@ -6530,7 +6837,7 @@ snapshots: '@angular/core': 20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2) tslib: 2.8.1 - '@angular/build@20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.10)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)': + '@angular/build@20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.10)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.24(chokidar@4.0.3) @@ -6565,6 +6872,7 @@ snapshots: optionalDependencies: '@angular/core': 20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2) '@angular/platform-browser': 20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)) + '@angular/service-worker': 20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2) karma: 6.4.4 less: 4.4.0 lmdb: 3.4.2 @@ -6583,7 +6891,7 @@ snapshots: - tsx - yaml - '@angular/build@20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.6)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)': + '@angular/build@20.3.24(@angular/compiler-cli@20.3.19(@angular/compiler@20.3.19)(typescript@5.8.3))(@angular/compiler@20.3.19)(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(@angular/platform-browser@20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@types/node@25.6.0)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.4.0)(lightningcss@1.32.0)(postcss@8.5.6)(tailwindcss@4.2.3)(terser@5.43.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2003.24(chokidar@4.0.3) @@ -6618,6 +6926,7 @@ snapshots: optionalDependencies: '@angular/core': 20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2) '@angular/platform-browser': 20.3.19(@angular/animations@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)))(@angular/common@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2))(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2)) + '@angular/service-worker': 20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2) karma: 6.4.4 less: 4.4.0 lmdb: 3.4.2 @@ -6735,6 +7044,12 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 + '@angular/service-worker@20.3.19(@angular/core@20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2))(rxjs@7.8.2)': + dependencies: + '@angular/core': 20.3.19(@angular/compiler@20.3.19)(rxjs@7.8.2) + rxjs: 7.8.2 + tslib: 2.8.1 + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -7557,6 +7872,11 @@ snapshots: '@colors/colors@1.5.0': {} + '@dependents/detective-less@5.0.3': + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + '@discoveryjs/json-ext@0.6.3': {} '@esbuild/aix-ppc64@0.19.12': @@ -8726,6 +9046,21 @@ snapshots: postcss: 8.5.10 tailwindcss: 4.2.3 + '@ts-graphviz/adapter@2.0.6': + dependencies: + '@ts-graphviz/common': 2.1.5 + + '@ts-graphviz/ast@2.0.7': + dependencies: + '@ts-graphviz/common': 2.1.5 + + '@ts-graphviz/common@2.1.5': {} + + '@ts-graphviz/core@2.0.7': + dependencies: + '@ts-graphviz/ast': 2.0.7 + '@ts-graphviz/common': 2.1.5 + '@ts-morph/common@0.22.0': dependencies: fast-glob: 3.3.3 @@ -8879,12 +9214,79 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 + '@typescript-eslint/project-service@8.59.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3) + '@typescript-eslint/types': 8.59.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/tsconfig-utils@8.59.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/types@8.59.0': {} + + '@typescript-eslint/typescript-estree@8.59.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3) + '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/visitor-keys': 8.59.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.0': + dependencies: + '@typescript-eslint/types': 8.59.0 + eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.0': {} '@vitejs/plugin-basic-ssl@2.1.0(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.4.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.43.1)(tsx@4.21.0))': dependencies: vite: 7.3.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.4.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.43.1)(tsx@4.21.0) + '@vue/compiler-core@3.5.33': + dependencies: + '@babel/parser': 7.29.2 + '@vue/shared': 3.5.33 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.33': + dependencies: + '@vue/compiler-core': 3.5.33 + '@vue/shared': 3.5.33 + + '@vue/compiler-sfc@3.5.33': + dependencies: + '@babel/parser': 7.29.2 + '@vue/compiler-core': 3.5.33 + '@vue/compiler-dom': 3.5.33 + '@vue/compiler-ssr': 3.5.33 + '@vue/shared': 3.5.33 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.10 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.33': + dependencies: + '@vue/compiler-dom': 3.5.33 + '@vue/shared': 3.5.33 + + '@vue/shared@3.5.33': {} + '@webassemblyjs/ast@1.14.1': dependencies: '@webassemblyjs/helper-numbers': 1.13.2 @@ -9074,11 +9476,15 @@ snapshots: ansi-styles@6.2.3: {} + any-promise@1.3.0: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.2 + app-module-path@2.2.0: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -9137,6 +9543,8 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + ast-module-types@6.0.1: {} + async-function@1.0.0: {} asynckit@0.4.0: {} @@ -9464,8 +9872,14 @@ snapshots: commander@11.1.0: {} + commander@12.1.0: {} + commander@2.20.3: {} + commander@7.2.0: {} + + commondir@1.0.1: {} + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -9613,6 +10027,8 @@ snapshots: dependencies: ms: 2.1.3 + deep-extend@0.6.0: {} + deep-is@0.1.4: {} default-browser-id@5.0.1: {} @@ -9646,12 +10062,77 @@ snapshots: depd@2.0.0: {} + dependency-tree@11.4.3: + dependencies: + commander: 12.1.0 + filing-cabinet: 5.4.0 + precinct: 12.3.1 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + destroy@1.2.0: {} detect-libc@2.1.2: {} detect-node@2.1.0: {} + detective-amd@6.1.0: + dependencies: + ast-module-types: 6.0.1 + escodegen: 2.1.0 + get-amd-module-type: 6.0.2 + node-source-walk: 7.0.2 + + detective-cjs@6.1.1: + dependencies: + ast-module-types: 6.0.1 + node-source-walk: 7.0.2 + + detective-es6@5.0.2: + dependencies: + node-source-walk: 7.0.2 + + detective-postcss@7.0.1(postcss@8.5.10): + dependencies: + is-url: 1.2.4 + postcss: 8.5.10 + postcss-values-parser: 6.0.2(postcss@8.5.10) + + detective-sass@6.0.2: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + detective-scss@5.0.2: + dependencies: + gonzales-pe: 4.3.0 + node-source-walk: 7.0.2 + + detective-stylus@5.0.1: {} + + detective-typescript@14.1.2(typescript@5.9.3): + dependencies: + '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3) + ast-module-types: 6.0.1 + node-source-walk: 7.0.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + detective-vue2@2.3.0(typescript@5.9.3): + dependencies: + '@dependents/detective-less': 5.0.3 + '@vue/compiler-sfc': 3.5.33 + detective-es6: 5.0.2 + detective-sass: 6.0.2 + detective-scss: 5.0.2 + detective-stylus: 5.0.1 + detective-typescript: 14.1.2(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + di@0.0.1: {} dns-packet@5.6.1: @@ -9944,6 +10425,14 @@ snapshots: escape-string-regexp@4.0.0: {} + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.6.1)): dependencies: eslint: 9.39.4(jiti@2.6.1) @@ -10020,6 +10509,8 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint-visitor-keys@5.0.1: {} + eslint@9.39.4(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) @@ -10081,6 +10572,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} etag@1.8.1: {} @@ -10224,6 +10717,20 @@ snapshots: dependencies: flat-cache: 4.0.1 + filing-cabinet@5.4.0: + dependencies: + app-module-path: 2.2.0 + commander: 12.1.0 + enhanced-resolve: 5.20.1 + module-definition: 6.0.2 + module-lookup-amd: 9.1.2 + resolve: 1.22.12 + resolve-dependency-path: 4.0.1 + sass-lookup: 6.1.2 + stylus-lookup: 6.1.2 + tsconfig-paths: 4.2.0 + typescript: 5.9.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -10353,6 +10860,11 @@ snapshots: gensync@1.0.0-beta.2: {} + get-amd-module-type@6.0.2: + dependencies: + ast-module-types: 6.0.1 + node-source-walk: 7.0.2 + get-caller-file@2.0.5: {} get-east-asian-width@1.5.0: {} @@ -10370,6 +10882,8 @@ snapshots: hasown: 2.0.3 math-intrinsics: 1.1.0 + get-own-enumerable-property-symbols@3.0.2: {} + get-package-type@0.1.0: {} get-proto@1.0.1: @@ -10425,6 +10939,10 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 + gonzales-pe@4.3.0: + dependencies: + minimist: 1.2.8 + gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -10591,6 +11109,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + ini@5.0.0: {} ini@6.0.0: {} @@ -10719,6 +11239,8 @@ snapshots: is-number@7.0.0: {} + is-obj@1.0.1: {} + is-plain-obj@3.0.0: {} is-plain-object@2.0.4: @@ -10736,6 +11258,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.3 + is-regexp@1.0.0: {} + is-set@2.0.3: {} is-shared-array-buffer@1.0.4: @@ -10763,6 +11287,10 @@ snapshots: is-unicode-supported@2.1.0: {} + is-url-superb@4.0.0: {} + + is-url@1.2.4: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -11258,6 +11786,25 @@ snapshots: dependencies: yallist: 3.1.1 + madge@8.0.0(typescript@5.8.3): + dependencies: + chalk: 4.1.2 + commander: 7.2.0 + commondir: 1.0.1 + debug: 4.4.3 + dependency-tree: 11.4.3 + ora: 5.4.1 + pluralize: 8.0.0 + pretty-ms: 7.0.1 + rc: 1.2.8 + stream-to-array: 2.3.0 + ts-graphviz: 2.1.6 + walkdir: 0.4.1 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -11417,6 +11964,17 @@ snapshots: mkdirp@3.0.1: {} + module-definition@6.0.2: + dependencies: + ast-module-types: 6.0.1 + node-source-walk: 7.0.2 + + module-lookup-amd@9.1.2: + dependencies: + commander: 12.1.0 + requirejs: 2.3.8 + requirejs-config-file: 4.0.0 + mrmime@2.0.1: {} ms@2.0.0: {} @@ -11522,6 +12080,10 @@ snapshots: node-releases@2.0.37: {} + node-source-walk@7.0.2: + dependencies: + '@babel/parser': 7.29.2 + nopt@9.0.0: dependencies: abbrev: 4.0.0 @@ -11765,6 +12327,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@2.1.0: {} + parse-node-version@1.0.1: {} parse5-html-rewriting-stream@8.0.0: @@ -11819,6 +12383,8 @@ snapshots: pkce-challenge@5.0.1: {} + pluralize@8.0.0: {} + possible-typed-array-names@1.1.0: {} postcss-loader@8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.105.0(esbuild@0.28.0)): @@ -11862,6 +12428,13 @@ snapshots: postcss-value-parser@4.2.0: {} + postcss-values-parser@6.0.2(postcss@8.5.10): + dependencies: + color-name: 1.1.4 + is-url-superb: 4.0.0 + postcss: 8.5.10 + quote-unquote: 1.0.0 + postcss@8.5.10: dependencies: nanoid: 3.3.11 @@ -11874,6 +12447,26 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + precinct@12.3.1: + dependencies: + '@dependents/detective-less': 5.0.3 + commander: 12.1.0 + detective-amd: 6.1.0 + detective-cjs: 6.1.1 + detective-es6: 5.0.2 + detective-postcss: 7.0.1(postcss@8.5.10) + detective-sass: 6.0.2 + detective-scss: 5.0.2 + detective-stylus: 5.0.1 + detective-typescript: 14.1.2(typescript@5.9.3) + detective-vue2: 2.3.0(typescript@5.9.3) + module-definition: 6.0.2 + node-source-walk: 7.0.2 + postcss: 8.5.10 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.1: @@ -11905,6 +12498,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-ms@7.0.1: + dependencies: + parse-ms: 2.1.0 + primeclt@0.1.5(@types/node@25.6.0)(encoding@0.1.13)(ws@8.20.0)(zod@4.1.13): dependencies: '@types/inquirer': 9.0.9 @@ -11973,6 +12570,8 @@ snapshots: queue-microtask@1.2.3: {} + quote-unquote@1.0.0: {} + range-parser@1.2.1: {} raw-body@2.5.3: @@ -11989,6 +12588,13 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + react-is@18.3.1: {} readable-stream@2.3.8: @@ -12062,8 +12668,17 @@ snapshots: require-from-string@2.0.2: {} + requirejs-config-file@4.0.0: + dependencies: + esprima: 4.0.1 + stringify-object: 3.3.0 + + requirejs@2.3.8: {} + requires-port@1.0.0: {} + resolve-dependency-path@4.0.1: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -12209,6 +12824,11 @@ snapshots: sass: 1.90.0 webpack: 5.105.0(esbuild@0.28.0) + sass-lookup@6.1.2: + dependencies: + commander: 12.1.0 + enhanced-resolve: 5.20.1 + sass@1.90.0: dependencies: chokidar: 4.0.3 @@ -12519,6 +13139,10 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + stream-to-array@2.3.0: + dependencies: + any-promise: 1.3.0 + streamroller@3.1.5: dependencies: date-format: 4.0.14 @@ -12570,6 +13194,12 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -12580,8 +13210,14 @@ snapshots: strip-bom@3.0.0: {} + strip-json-comments@2.0.1: {} + strip-json-comments@3.1.1: {} + stylus-lookup@6.1.2: + dependencies: + commander: 12.1.0 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -12669,6 +13305,17 @@ snapshots: tree-kill@1.2.2: {} + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-graphviz@2.1.6: + dependencies: + '@ts-graphviz/adapter': 2.0.6 + '@ts-graphviz/ast': 2.0.7 + '@ts-graphviz/common': 2.1.5 + '@ts-graphviz/core': 2.0.7 + ts-morph@21.0.1: dependencies: '@ts-morph/common': 0.22.0 @@ -12681,6 +13328,12 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@2.8.1: {} tsx@4.21.0: @@ -12752,6 +13405,8 @@ snapshots: typescript@5.8.3: {} + typescript@5.9.3: {} + ua-parser-js@0.7.41: {} unbox-primitive@1.1.0: @@ -12820,6 +13475,8 @@ snapshots: void-elements@2.0.1: {} + walkdir@0.4.1: {} + walker@1.0.8: dependencies: makeerror: 1.0.12 diff --git a/public-tis/branding/login.webp b/public-tis/branding/login.webp new file mode 100644 index 0000000..6e0ecb5 Binary files /dev/null and b/public-tis/branding/login.webp differ diff --git a/public-tis/branding/logo.png b/public-tis/branding/logo.png new file mode 100644 index 0000000..f82b4c3 Binary files /dev/null and b/public-tis/branding/logo.png differ diff --git a/public-tis/favicon.ico b/public-tis/favicon.ico new file mode 100644 index 0000000..04bc49e Binary files /dev/null and b/public-tis/favicon.ico differ diff --git a/public-tis/favicon/apple-touch-icon.png b/public-tis/favicon/apple-touch-icon.png new file mode 100644 index 0000000..107a390 Binary files /dev/null and b/public-tis/favicon/apple-touch-icon.png differ diff --git a/public-tis/favicon/favicon-96x96.png b/public-tis/favicon/favicon-96x96.png new file mode 100644 index 0000000..9cc83f1 Binary files /dev/null and b/public-tis/favicon/favicon-96x96.png differ diff --git a/public-tis/favicon/favicon.ico b/public-tis/favicon/favicon.ico new file mode 100644 index 0000000..04bc49e Binary files /dev/null and b/public-tis/favicon/favicon.ico differ diff --git a/public-tis/favicon/favicon.svg b/public-tis/favicon/favicon.svg new file mode 100644 index 0000000..9d8298e --- /dev/null +++ b/public-tis/favicon/favicon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/public-tis/favicon/site.webmanifest b/public-tis/favicon/site.webmanifest new file mode 100644 index 0000000..2ac3423 --- /dev/null +++ b/public-tis/favicon/site.webmanifest @@ -0,0 +1,24 @@ +{ + "background_color": "#ffffff", + "display": "standalone", + "id": "/", + "icons": [ + { + "purpose": "maskable", + "sizes": "192x192", + "src": "/favicon/web-app-manifest-192x192.png", + "type": "image/png" + }, + { + "purpose": "maskable", + "sizes": "512x512", + "src": "/favicon/web-app-manifest-512x512.png", + "type": "image/png" + } + ], + "name": "PSP A - مدیریت صورت‌حساب‌های مالیاتی", + "short_name": "PSP A", + "start_url": "/", + "scope": "/", + "theme_color": "#ffffff" +} diff --git a/public-tis/favicon/web-app-manifest-192x192.png b/public-tis/favicon/web-app-manifest-192x192.png new file mode 100644 index 0000000..56bb138 Binary files /dev/null and b/public-tis/favicon/web-app-manifest-192x192.png differ diff --git a/public-tis/favicon/web-app-manifest-512x512.png b/public-tis/favicon/web-app-manifest-512x512.png new file mode 100644 index 0000000..f82b4c3 Binary files /dev/null and b/public-tis/favicon/web-app-manifest-512x512.png differ diff --git a/public/favicon/site.webmanifest b/public/favicon/site.webmanifest index d2499ae..ef3c29a 100644 --- a/public/favicon/site.webmanifest +++ b/public/favicon/site.webmanifest @@ -5,17 +5,20 @@ { "purpose": "maskable", "sizes": "192x192", - "src": "/web-app-manifest-192x192.png", + "src": "/favicon/web-app-manifest-192x192.png", "type": "image/png" }, { "purpose": "maskable", "sizes": "512x512", - "src": "/web-app-manifest-512x512.png", + "src": "/favicon/web-app-manifest-512x512.png", "type": "image/png" } ], + "id": "/", "name": "مدیریت صورت‌حساب‌های مالیاتی", + "scope": "/", "short_name": "مدیریت صورت‌حساب‌های مالیاتی", + "start_url": "/", "theme_color": "#ffffff" } diff --git a/scripts/update-ngsw-appdata.js b/scripts/update-ngsw-appdata.js new file mode 100644 index 0000000..e87f000 --- /dev/null +++ b/scripts/update-ngsw-appdata.js @@ -0,0 +1,21 @@ +const fs = require('fs'); +const path = require('path'); + +const root = path.resolve(__dirname, '..'); +const packageJsonPath = path.join(root, 'package.json'); +const ngswConfigPath = path.join(root, 'ngsw-config.json'); + +const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); +const ngswConfig = JSON.parse(fs.readFileSync(ngswConfigPath, 'utf8')); + +const now = new Date(); +const buildDate = process.env.BUILD_DATE || now.toISOString(); +const appVersion = process.env.APP_VERSION || packageJson.version || '0.0.0'; + +ngswConfig.appData = { + ...(ngswConfig.appData || {}), + appVersion, + buildDate, +}; + +fs.writeFileSync(ngswConfigPath, `${JSON.stringify(ngswConfig, null, 2)}\n`); diff --git a/src/app.config.ts b/src/app.config.ts index 319208f..6188ccb 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -12,13 +12,14 @@ import { withInterceptors, withInterceptorsFromDi, } from '@angular/common/http'; -import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core'; +import { ApplicationConfig, isDevMode, provideZonelessChangeDetection } from '@angular/core'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling, } from '@angular/router'; +import { provideServiceWorker } from '@angular/service-worker'; // Use the consolidated preset that includes our custom variables import { ConfirmationService, MessageService } from 'primeng/api'; import { providePrimeNG } from 'primeng/config'; @@ -73,5 +74,9 @@ export const appConfig: ApplicationConfig = { withInterceptors([loggingInterceptor, authInterceptor, errorInterceptor, dedupInterceptor]), withInterceptorsFromDi(), ), + provideServiceWorker('ngsw-worker.js', { + enabled: !isDevMode(), + registrationStrategy: 'registerWhenStable:30000', + }), ], }; diff --git a/src/app.routes.ts b/src/app.routes.ts index 0017e36..38c1f0b 100644 --- a/src/app.routes.ts +++ b/src/app.routes.ts @@ -1,40 +1 @@ -import { CONSUMER_ROUTES } from '@/domains/consumer/routes'; -import { PARTNER_ROUTES } from '@/domains/partner/routes'; -import { POS_ROUTES } from '@/domains/pos/routes'; -import { PROVIDER_ROUTES } from '@/domains/provider/routes'; -import { SUPER_ADMIN_ROUTES } from '@/domains/superAdmin/routes'; -import { AuthComponent } from '@/modules/auth/pages/auth.component'; -import { Notfound } from '@/pages/notfound/notfound.component'; -import { Routes } from '@angular/router'; -import { AppLayout } from './app/layout/default/app.layout.component'; -import { Dashboard } from './app/pages/dashboard/dashboard'; -import { Documentation } from './app/pages/documentation/documentation'; - -export const appRoutes: Routes = [ - { - path: '', - component: AppLayout, - children: [ - SUPER_ADMIN_ROUTES, - CONSUMER_ROUTES, - PROVIDER_ROUTES, - PARTNER_ROUTES, - { path: 'ng', component: Dashboard }, - { path: 'uikit', loadChildren: () => import('./app/pages/uikit/uikit.routes') }, - { path: 'documentation', component: Documentation }, - { path: 'pages', loadChildren: () => import('./app/pages/pages.routes') }, - ], - }, - POS_ROUTES, - // { - // path: 'pos/:posId', - // component: POSComponent, - // }, - { - path: 'auth', - component: AuthComponent, - }, - { path: 'notfound', component: Notfound }, - // { path: 'auth', loadChildren: () => import('./app/pages/auth/auth.routes') }, - { path: '**', redirectTo: '/notfound' }, -]; +export { appRoutes } from './tenants/default/app.routes'; diff --git a/src/app/branding/branding.config.ts b/src/app/branding/branding.config.ts new file mode 100644 index 0000000..7b2b136 --- /dev/null +++ b/src/app/branding/branding.config.ts @@ -0,0 +1 @@ +export { brandingConfig } from '../../tenants/default/branding.config'; diff --git a/src/app/core/services/index.ts b/src/app/core/services/index.ts index d33c9a3..4329573 100644 --- a/src/app/core/services/index.ts +++ b/src/app/core/services/index.ts @@ -3,3 +3,4 @@ export { AuthService } from './auth.service'; export { ErrorHandlerService } from './error-handler.service'; export { ConfigService } from './config.service'; export * from './breadcrumb.service'; +export * from './native-bridge.service'; diff --git a/src/app/core/services/native-bridge.service.ts b/src/app/core/services/native-bridge.service.ts new file mode 100644 index 0000000..1694f50 --- /dev/null +++ b/src/app/core/services/native-bridge.service.ts @@ -0,0 +1,88 @@ +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; + +interface INativeBridgeHost { + pay?: (payload: string) => unknown; + print?: (payload: string) => unknown; +} + +export interface INativePayRequest { + amount: number; + totalAmount: number; + invoiceDate: string; +} + +export interface INativePrintRequest { + invoiceId?: string; + code?: string; +} + +export interface INativeBridgeResult { + success: boolean; + data?: T; + error?: string; +} + +@Injectable({ providedIn: 'root' }) +export class NativeBridgeService { + private get host(): INativeBridgeHost | undefined { + const globalWindow = window as unknown as { + AndroidBridge?: INativeBridgeHost; + Android?: INativeBridgeHost; + }; + + return globalWindow.AndroidBridge || globalWindow.Android; + } + + isEnabled(): boolean { + return !!environment.enableNativeBridge; + } + + canPay(): boolean { + return this.isEnabled() && typeof this.host?.pay === 'function'; + } + + canPrint(): boolean { + return this.isEnabled() && typeof this.host?.print === 'function'; + } + + pay(request: INativePayRequest): INativeBridgeResult { + return this.invoke('pay', request); + } + + print(request: INativePrintRequest): INativeBridgeResult { + return this.invoke('print', request); + } + + private invoke(method: 'pay' | 'print', payload: object): INativeBridgeResult { + if (!this.isEnabled()) { + return { success: false, error: 'Native bridge is disabled for this tenant.' }; + } + + const fn = this.host?.[method]; + if (typeof fn !== 'function') { + return { success: false, error: `Native method "${method}" is not available.` }; + } + + try { + const raw = fn(JSON.stringify(payload)); + const parsed = this.tryParse(raw); + if (parsed && typeof parsed === 'object' && 'success' in parsed) { + return parsed as INativeBridgeResult; + } + + return { success: true, data: parsed ?? raw }; + } catch (error) { + return { success: false, error: (error as Error).message }; + } + } + + private tryParse(value: unknown): unknown { + if (typeof value !== 'string') return value; + try { + return JSON.parse(value); + } catch { + return value; + } + } +} diff --git a/src/app/core/validators/fiscal-code.validator.ts b/src/app/core/validators/fiscal-code.validator.ts new file mode 100644 index 0000000..dc117f2 --- /dev/null +++ b/src/app/core/validators/fiscal-code.validator.ts @@ -0,0 +1,12 @@ +import { ValidatorFn } from '@angular/forms'; + +export function fiscalCodeValidator(): ValidatorFn { + return (control) => { + if (control.value === null || control.value === undefined || control.value === '') { + return null; + } + return control.value.length === 11 && /^[0-9]{11}$/.test(control.value) + ? null + : { fiscalCode: 'معتبر نیست' }; + }; +} diff --git a/src/app/core/validators/index.ts b/src/app/core/validators/index.ts index 3608a4f..66965fa 100644 --- a/src/app/core/validators/index.ts +++ b/src/app/core/validators/index.ts @@ -1,3 +1,4 @@ +export * from './fiscal-code.validator'; export * from './iban.validator'; export * from './mobile.validator'; export * from './must-match.validator'; diff --git a/src/app/core/validators/mobile.validator.ts b/src/app/core/validators/mobile.validator.ts index 71852c7..20907a9 100644 --- a/src/app/core/validators/mobile.validator.ts +++ b/src/app/core/validators/mobile.validator.ts @@ -21,7 +21,7 @@ export function mobileValidator(): ValidatorFn { } if (typeof v !== 'string') { - return { invalidMobile: true }; + return { invalidMobile: 'معتبر نیست' }; } const trimmed = v.trim(); diff --git a/src/app/core/validators/national-id.validator.ts b/src/app/core/validators/national-id.validator.ts index 520a1dc..5a7bcc9 100644 --- a/src/app/core/validators/national-id.validator.ts +++ b/src/app/core/validators/national-id.validator.ts @@ -7,6 +7,6 @@ export function nationalIdValidator(): ValidatorFn { } return control.value.length === 10 && /^[0-9]{10}$/.test(control.value) ? null - : { nationalId: true }; + : { nationalId: 'معتبر نیست' }; }; } diff --git a/src/app/core/validators/postal-code.validator.ts b/src/app/core/validators/postal-code.validator.ts index 5878633..d4309d3 100644 --- a/src/app/core/validators/postal-code.validator.ts +++ b/src/app/core/validators/postal-code.validator.ts @@ -7,6 +7,6 @@ export function postalCodeValidator(): ValidatorFn { } return control.value.length === 10 && /^[0-9]{10}$/.test(control.value) ? null - : { postalCode: true }; + : { postalCode: 'معتبر نیست' }; }; } diff --git a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts index 3e43e6d..9e31568 100644 --- a/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts +++ b/src/app/domains/consumer/components/invoices/shared-saleInvoice.component.ts @@ -1,4 +1,5 @@ import { Maybe } from '@/core'; +import { NativeBridgeService } from '@/core/services'; import { AppCardComponent, KeyValueComponent } from '@/shared/components'; import { PageLoadingComponent } from '@/shared/components/page-loading.component'; import { @@ -8,7 +9,7 @@ import { import { PriceMaskDirective } from '@/shared/directives'; import { UikitEmptyStateComponent } from '@/uikit'; import { getGoodUnitTypeProperties } from '@/utils'; -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; +import { Component, EventEmitter, inject, Input, Output, TemplateRef, ViewChild } from '@angular/core'; import { Badge } from 'primeng/badge'; import { ButtonDirective } from 'primeng/button'; import { Divider } from 'primeng/divider'; @@ -34,6 +35,8 @@ export type TConsumerSaleInvoice = 'full' | 'customer' | 'pos'; ], }) export class ConsumerSaleInvoiceSharedComponent { + private readonly nativeBridge = inject(NativeBridgeService); + @Input({ required: true }) loading!: boolean; @Input() invoice?: Maybe; @Input() variant: TConsumerSaleInvoice = 'full'; @@ -43,6 +46,14 @@ export class ConsumerSaleInvoiceSharedComponent { @ViewChild('totalAmount', { static: false }) totalAmount!: TemplateRef; printInvoice = () => { + if (this.nativeBridge.isEnabled()) { + const result = this.nativeBridge.print({ + invoiceId: this.invoice?.id, + code: this.invoice?.code, + }); + if (result.success) return; + } + window.print(); }; diff --git a/src/app/domains/consumer/components/license-info-dialog.component.html b/src/app/domains/consumer/components/license-info-dialog.component.html index 32055cd..f5e5b61 100644 --- a/src/app/domains/consumer/components/license-info-dialog.component.html +++ b/src/app/domains/consumer/components/license-info-dialog.component.html @@ -1,4 +1,4 @@ - } --> - + diff --git a/src/app/domains/consumer/components/license-info-dialog.component.ts b/src/app/domains/consumer/components/license-info-dialog.component.ts index 5ef5f48..7894970 100644 --- a/src/app/domains/consumer/components/license-info-dialog.component.ts +++ b/src/app/domains/consumer/components/license-info-dialog.component.ts @@ -1,14 +1,12 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; -import { KeyValueComponent } from '@/shared/components'; import { Component, inject } from '@angular/core'; -import { Dialog } from 'primeng/dialog'; -import { Message } from 'primeng/message'; import { ConsumerStore } from '../store/main.store'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-license-info-dialog', templateUrl: './license-info-dialog.component.html', - imports: [Dialog, KeyValueComponent, Message], + imports: [SharedDialogComponent], }) export class ConsumerLicenseInfoDialogComponent extends AbstractDialog { private readonly store = inject(ConsumerStore); diff --git a/src/app/domains/consumer/modules/accounts/components/form.component.html b/src/app/domains/consumer/modules/accounts/components/form.component.html index eb59e74..58fb76b 100644 --- a/src/app/domains/consumer/modules/accounts/components/form.component.html +++ b/src/app/domains/consumer/modules/accounts/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/consumer/modules/accounts/components/form.component.ts b/src/app/domains/consumer/modules/accounts/components/form.component.ts index 9a1a768..c679589 100644 --- a/src/app/domains/consumer/modules/accounts/components/form.component.ts +++ b/src/app/domains/consumer/modules/accounts/components/form.component.ts @@ -4,14 +4,14 @@ import { SharedPasswordInputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IAccountRequest, IAccountResponse } from '../models'; import { AccountsService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'account-password-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, FormFooterActionsComponent, SharedPasswordInputComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, FormFooterActionsComponent, SharedPasswordInputComponent], }) export class ConsumerAccountPasswordFormComponent extends AbstractFormDialog< IAccountRequest, diff --git a/src/app/domains/consumer/modules/accounts/components/permissions/list.component.ts b/src/app/domains/consumer/modules/accounts/components/permissions/list.component.ts index 48c6fd3..6156c42 100644 --- a/src/app/domains/consumer/modules/accounts/components/permissions/list.component.ts +++ b/src/app/domains/consumer/modules/accounts/components/permissions/list.component.ts @@ -54,7 +54,7 @@ export class ConsumerAccountPermissionListComponent { submit() { const dataToSet: IPermissionRequest = {}; const posPermissions = this.permissions() - ?.poses.filter((pos) => pos.hasAccess) + ?.poses?.filter((pos) => pos.hasAccess) .map((pos) => pos.id); const complexPermissions = this.permissions() ?.complexes?.filter((complex) => complex.hasAccess) diff --git a/src/app/domains/consumer/modules/accounts/models/io.d.ts b/src/app/domains/consumer/modules/accounts/models/io.d.ts index a43d10e..96a8e0b 100644 --- a/src/app/domains/consumer/modules/accounts/models/io.d.ts +++ b/src/app/domains/consumer/modules/accounts/models/io.d.ts @@ -3,12 +3,15 @@ export interface IAccountRawResponse { role: string; created_at: string; account: Account; - pos: PosComplex; + pos: PosSummary; } export interface IAccountResponse extends IAccountRawResponse {} export interface IAccountRequest { - name: string; + username?: string; + password?: string; + role?: string; + status?: string; } export interface IAccountPasswordRequest { @@ -20,6 +23,12 @@ interface Account { status: string; } +interface PosSummary { + id: string; + name: string; + complex: PosComplex; +} + interface PosComplex { name: string; branch_code: string; diff --git a/src/app/domains/consumer/modules/accounts/models/permissions.io.d.ts b/src/app/domains/consumer/modules/accounts/models/permissions.io.d.ts index 21dc884..5c13aed 100644 --- a/src/app/domains/consumer/modules/accounts/models/permissions.io.d.ts +++ b/src/app/domains/consumer/modules/accounts/models/permissions.io.d.ts @@ -1,9 +1,11 @@ import ISummary from '@/core/models/summary'; export interface IPermissionRawResponse { - poses: IPosPermissionResponse[]; - complexes: IComplexPermissionResponse[]; - business_activities: ICommonPermissionResponse[]; + poses?: IPosPermissionResponse[]; + complexes?: IComplexPermissionResponse[]; + business_activities?: ICommonPermissionResponse[]; + // Backend currently returns this key in camelCase for non-operator roles. + businessActivities?: ICommonPermissionResponse[]; } export interface IPermissionResponse extends IPermissionRawResponse {} @@ -17,6 +19,7 @@ interface ICommonPermissionResponse { id: string; name: string; hasAccess: boolean; + role?: string | null; } interface IComplexPermissionResponse extends ICommonPermissionResponse { diff --git a/src/app/domains/consumer/modules/accounts/services/permissions.service.ts b/src/app/domains/consumer/modules/accounts/services/permissions.service.ts index d1332fa..f1487f0 100644 --- a/src/app/domains/consumer/modules/accounts/services/permissions.service.ts +++ b/src/app/domains/consumer/modules/accounts/services/permissions.service.ts @@ -1,5 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { CONSUMER_PERMISSIONS_API_ROUTES } from '../constants'; import { IPermissionRawResponse, IPermissionRequest, IPermissionResponse } from '../models'; @@ -11,7 +12,13 @@ export class PermissionsService { private apiRoutes = CONSUMER_PERMISSIONS_API_ROUTES; getAll(accountId: string): Observable { - return this.http.get(this.apiRoutes.list(accountId)); + return this.http.get(this.apiRoutes.list(accountId)).pipe( + map((res) => ({ + poses: res.poses ?? [], + complexes: res.complexes ?? [], + business_activities: res.business_activities ?? res.businessActivities ?? [], + })), + ); } update(accountId: string, data: IPermissionRequest): Observable { diff --git a/src/app/domains/consumer/modules/accounts/views/list.component.html b/src/app/domains/consumer/modules/accounts/views/list.component.html index 704076d..ff43b3f 100644 --- a/src/app/domains/consumer/modules/accounts/views/list.component.html +++ b/src/app/domains/consumer/modules/accounts/views/list.component.html @@ -8,11 +8,10 @@ /> @if (selectedItemForEdit()) { - } diff --git a/src/app/domains/consumer/modules/accounts/views/list.component.ts b/src/app/domains/consumer/modules/accounts/views/list.component.ts index 0dfd199..df65209 100644 --- a/src/app/domains/consumer/modules/accounts/views/list.component.ts +++ b/src/app/domains/consumer/modules/accounts/views/list.component.ts @@ -1,9 +1,13 @@ // import { CatalogRoleTagComponent } from '@/shared/catalog/roles'; import { AbstractList } from '@/shared/abstractClasses/abstract-list'; +import { + ChangePasswordFormDialogComponent, + IChangePasswordSubmitPayload, +} from '@/shared/components'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; -import { Component, inject } from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; import { Router } from '@angular/router'; -import { ConsumerAccountPasswordFormComponent } from '../components/form.component'; +import { finalize } from 'rxjs'; import { consumerAccountsNamedRoutes } from '../constants'; import { IAccountResponse } from '../models'; import { AccountsService } from '../services/main.service'; @@ -11,11 +15,12 @@ import { AccountsService } from '../services/main.service'; @Component({ selector: 'consumer-accounts', templateUrl: './list.component.html', - imports: [PageDataListComponent, ConsumerAccountPasswordFormComponent], + imports: [PageDataListComponent, ChangePasswordFormDialogComponent], }) export class ConsumerAccountsComponent extends AbstractList { private readonly service = inject(AccountsService); private readonly router = inject(Router); + passwordSubmitLoading = signal(false); override setColumns(): void { this.columns = [ @@ -48,4 +53,17 @@ export class ConsumerAccountsComponent extends AbstractList { toSinglePage(account: IAccountResponse) { this.router.navigateByUrl(consumerAccountsNamedRoutes.account.meta.pagePath!(account.id)); } + + updatePassword(payload: IChangePasswordSubmitPayload) { + if (!this.selectedItemForEdit()?.id) return; + + this.passwordSubmitLoading.set(true); + this.service + .update(this.selectedItemForEdit()!.id, { password: payload.password }) + .pipe(finalize(() => this.passwordSubmitLoading.set(false))) + .subscribe(() => { + this.visibleForm.set(false); + this.refresh(); + }); + } } diff --git a/src/app/domains/consumer/modules/accounts/views/single.component.ts b/src/app/domains/consumer/modules/accounts/views/single.component.ts index 31ef772..19a127d 100644 --- a/src/app/domains/consumer/modules/accounts/views/single.component.ts +++ b/src/app/domains/consumer/modules/accounts/views/single.component.ts @@ -2,13 +2,12 @@ import { BreadcrumbService } from '@/core/services'; import { AppCardComponent, KeyValueComponent } from '@/shared/components'; import { Component, computed, effect, inject, signal } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { ConsumerAccountPermissionListComponent } from '../components/permissions/list.component'; import { AccountStore } from '../store/account.store'; @Component({ selector: 'consumer-account', templateUrl: './single.component.html', - imports: [AppCardComponent, KeyValueComponent, ConsumerAccountPermissionListComponent], + imports: [AppCardComponent, KeyValueComponent], }) export class ConsumerAccountComponent { private readonly store = inject(AccountStore); diff --git a/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.html b/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.html index 248f2ba..1af5372 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.html +++ b/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.html @@ -1,4 +1,4 @@ -
- - - + + + -
+ diff --git a/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.ts b/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.ts index 17330be..faf551b 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.ts +++ b/src/app/domains/consumer/modules/businessActivities/components/complexes/form.component.ts @@ -1,17 +1,29 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { InputComponent } from '@/shared/components'; +import { + NameComponent, + AddressComponent, + BranchCodeComponent, +} from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; +import { ReactiveFormsModule } from '@angular/forms'; import { IComplexRequest, IComplexResponse } from '../../models'; import { ConsumerComplexesService } from '../../services/complexes.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-complex-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ + ReactiveFormsModule, + SharedDialogComponent, + NameComponent, + BranchCodeComponent, + AddressComponent, + FormFooterActionsComponent, + ], }) export class ConsumerComplexFormComponent extends AbstractFormDialog< IComplexRequest, @@ -24,9 +36,9 @@ export class ConsumerComplexFormComponent extends AbstractFormDialog< initForm = () => { return this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - branch_code: [this.initialValues?.branch_code || ''], - address: [this.initialValues?.address || '', [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + branch_code: fieldControl.branch_code(this.initialValues?.branch_code || ''), + address: fieldControl.address(this.initialValues?.address || ''), }); }; diff --git a/src/app/domains/consumer/modules/businessActivities/components/form.component.html b/src/app/domains/consumer/modules/businessActivities/components/form.component.html index 57499c6..941cdb6 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/form.component.html +++ b/src/app/domains/consumer/modules/businessActivities/components/form.component.html @@ -1,4 +1,4 @@ -
- + + + + -
+ diff --git a/src/app/domains/consumer/modules/businessActivities/components/form.component.ts b/src/app/domains/consumer/modules/businessActivities/components/form.component.ts index d20d38b..df4f118 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/form.component.ts +++ b/src/app/domains/consumer/modules/businessActivities/components/form.component.ts @@ -1,16 +1,30 @@ import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { InputComponent } from '@/shared/components'; +import { + EconomicCodeComponent, + FiscalCodeComponent, + NameComponent, + PartnerTokenComponent, +} from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { fieldControl } from '@/shared/constants/fields'; import { Component, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; +import { ReactiveFormsModule } from '@angular/forms'; import { IBusinessActivityRequest, IBusinessActivityResponse } from '../models'; import { BusinessActivitiesService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-businessActivity-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, FormFooterActionsComponent, InputComponent], + imports: [ + ReactiveFormsModule, + SharedDialogComponent, + FormFooterActionsComponent, + NameComponent, + EconomicCodeComponent, + FiscalCodeComponent, + PartnerTokenComponent, + ], }) export class ConsumerBusinessActivityFormComponent extends AbstractFormDialog< IBusinessActivityRequest, @@ -20,7 +34,10 @@ export class ConsumerBusinessActivityFormComponent extends AbstractFormDialog< private service = inject(BusinessActivitiesService); form = this.fb.group({ - name: [this.initialValues?.name, [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''), + fiscal_code: fieldControl.fiscal_code(this.initialValues?.fiscal_code || ''), + partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''), }); override submitForm(payload: IBusinessActivityRequest) { diff --git a/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html index 053da93..9ab4c83 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html +++ b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.html @@ -1,4 +1,4 @@ - +
@@ -14,4 +14,4 @@ -
+ diff --git a/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts index 80ed53f..49b37ef 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts +++ b/src/app/domains/consumer/modules/businessActivities/components/goods/form.component.ts @@ -3,7 +3,6 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; import { CatalogGuildGoodCategoriesSelectComponent } from '@/shared/catalog/guildGoodCategories'; @@ -12,13 +11,14 @@ import { IConsumerBusinessActivityGoodResponse, } from '../../models/goods_io'; import { BusinessActivityGoodsService } from '../../services/goods.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-businessActivity-good-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, + SharedDialogComponent, InputComponent, FormFooterActionsComponent, EnumSelectComponent, diff --git a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html index b70178a..3d05561 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html +++ b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html @@ -1,4 +1,4 @@ -
- + - + @if (form.controls.pos_type.value === "PSP") { - - - + + + } @if (!editMode) { اطلاعات ورود - + -
+ diff --git a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts index b844993..5ea2dba 100644 --- a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts +++ b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts @@ -1,29 +1,37 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { MustMatch } from '@/core/validators'; import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; -import { CatalogProviderSelectComponent } from '@/shared/catalog'; -import { CatalogDeviceSelectComponent } from '@/shared/catalog/devices'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { + NameComponent, + SharedPasswordInputComponent, + DeviceIdComponent, + ProviderIdComponent, + SerialNumberComponent, + PosTypeComponent, + UsernameComponent, +} from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { Divider } from 'primeng/divider'; import { IPosRequest, IPosResponse } from '../../models'; import { ConsumerPosesService } from '../../services/poses.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-pos-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + NameComponent, FormFooterActionsComponent, - CatalogDeviceSelectComponent, - CatalogProviderSelectComponent, - EnumSelectComponent, + DeviceIdComponent, + ProviderIdComponent, + PosTypeComponent, + SerialNumberComponent, + UsernameComponent, Divider, SharedPasswordInputComponent, ], @@ -37,11 +45,11 @@ export class ConsumerPosFormComponent extends AbstractFormDialog { const form = this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - pos_type: [this.initialValues?.pos_type || '', [Validators.required]], - serial_number: [this.initialValues?.serial_number || ''], - device_id: [this.initialValues?.device?.id || ''], - provider_id: [this.initialValues?.provider?.id || ''], + name: fieldControl.name(this.initialValues?.name || ''), + pos_type: fieldControl.pos_type(this.initialValues?.pos_type || ''), + serial_number: fieldControl.serial_number(this.initialValues?.serial_number || ''), + device_id: fieldControl.device_id(this.initialValues?.device?.id || ''), + provider_id: fieldControl.provider_id(this.initialValues?.provider?.id || ''), username: [''], password: [''], confirmPassword: [''], @@ -82,13 +90,15 @@ export class ConsumerPosFormComponent extends AbstractFormDialog('', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.username('')[1], }), ); + // @ts-ignore form.addControl( 'password', this.fb.control('', { @@ -96,6 +106,7 @@ export class ConsumerPosFormComponent extends AbstractFormDialog('', { diff --git a/src/app/domains/consumer/modules/businessActivities/models/io.d.ts b/src/app/domains/consumer/modules/businessActivities/models/io.d.ts index b60d201..1ddbaac 100644 --- a/src/app/domains/consumer/modules/businessActivities/models/io.d.ts +++ b/src/app/domains/consumer/modules/businessActivities/models/io.d.ts @@ -4,6 +4,8 @@ export interface IBusinessActivityRawResponse { id: string; name: string; economic_code: string; + fiscal_code: string; + partner_token: string; guild: ISummary; created_at: string; license_info: LicenseInfo; @@ -12,6 +14,8 @@ export interface IBusinessActivityResponse extends IBusinessActivityRawResponse export interface IBusinessActivityRequest { economic_code: string; + fiscal_code: string; + partner_token: string; name: string; } diff --git a/src/app/domains/consumer/modules/customers/components/form-dialog.component.html b/src/app/domains/consumer/modules/customers/components/form-dialog.component.html index 788b056..2ab1a44 100644 --- a/src/app/domains/consumer/modules/customers/components/form-dialog.component.html +++ b/src/app/domains/consumer/modules/customers/components/form-dialog.component.html @@ -1,4 +1,4 @@ - } - + diff --git a/src/app/domains/consumer/modules/customers/components/form-dialog.component.ts b/src/app/domains/consumer/modules/customers/components/form-dialog.component.ts index a88a382..e186e08 100644 --- a/src/app/domains/consumer/modules/customers/components/form-dialog.component.ts +++ b/src/app/domains/consumer/modules/customers/components/form-dialog.component.ts @@ -1,10 +1,10 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { ICustomerResponse } from '../models'; import { ConsumerCustomerIndividualFormComponent } from './form-individual.component'; import { ConsumerCustomerLegalFormComponent } from './form-legal.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-customer-form-dialog', @@ -14,7 +14,7 @@ import { ConsumerCustomerLegalFormComponent } from './form-legal.component'; ConsumerCustomerIndividualFormComponent, ConsumerCustomerLegalFormComponent, ReactiveFormsModule, - Dialog, + SharedDialogComponent, ], }) export class ConsumerBusinessActivityFormComponent extends AbstractDialog { diff --git a/src/app/domains/consumer/modules/customers/components/form-individual.component.html b/src/app/domains/consumer/modules/customers/components/form-individual.component.html index 2eda7a2..a8b346d 100644 --- a/src/app/domains/consumer/modules/customers/components/form-individual.component.html +++ b/src/app/domains/consumer/modules/customers/components/form-individual.component.html @@ -1,7 +1,7 @@
- + diff --git a/src/app/domains/consumer/modules/customers/components/form-individual.component.ts b/src/app/domains/consumer/modules/customers/components/form-individual.component.ts index 8022cb0..825b309 100644 --- a/src/app/domains/consumer/modules/customers/components/form-individual.component.ts +++ b/src/app/domains/consumer/modules/customers/components/form-individual.component.ts @@ -27,7 +27,7 @@ export class ConsumerCustomerIndividualFormComponent extends AbstractForm< return this.fb.group({ first_name: [this.initialValues?.individual?.first_name || '', [Validators.required]], last_name: [this.initialValues?.individual?.last_name || '', [Validators.required]], - national_id: [ + national_code: [ this.initialValues?.individual?.national_id || '', [Validators.required, nationalIdValidator()], ], diff --git a/src/app/domains/consumer/modules/customers/models/io.d.ts b/src/app/domains/consumer/modules/customers/models/io.d.ts index 9090547..1b62129 100644 --- a/src/app/domains/consumer/modules/customers/models/io.d.ts +++ b/src/app/domains/consumer/modules/customers/models/io.d.ts @@ -12,12 +12,21 @@ export interface ICustomerResponse extends ICustomerRawResponse {} export interface ICustomerRequest {} -export interface ICustomerIndividualRequest extends CustomerIndividual { - is_favorite: boolean; +export interface ICustomerIndividualRequest { + first_name?: string; + last_name?: string; + national_code?: string; + postal_code?: string; + economic_code?: string; + is_favorite?: boolean; } -export interface ICustomerLegalRequest extends CustomerLegal { - is_favorite: boolean; +export interface ICustomerLegalRequest { + company_name?: string; + registration_number?: string; + postal_code?: string; + economic_code?: string; + is_favorite?: boolean; } interface CustomerLegal { diff --git a/src/app/domains/consumer/modules/dashboard/models/saleInvoices.io.d.ts b/src/app/domains/consumer/modules/dashboard/models/saleInvoices.io.d.ts index 115b06b..a851f71 100644 --- a/src/app/domains/consumer/modules/dashboard/models/saleInvoices.io.d.ts +++ b/src/app/domains/consumer/modules/dashboard/models/saleInvoices.io.d.ts @@ -3,11 +3,11 @@ import ISummary from '@/core/models/summary'; export interface IStatisticsSaleInvoicesRawResponse { id: string; code: string; - created_date: string; + created_at: string; + created_date?: string; total_amount: string; pos: Pos; consumer_account: ConsumerAccount; - created_at: string; } export interface IStatisticsSaleInvoicesResponse extends IStatisticsSaleInvoicesRawResponse {} diff --git a/src/app/domains/consumer/modules/poses/components/form.component.html b/src/app/domains/consumer/modules/poses/components/form.component.html index c4d25d4..688bcb8 100644 --- a/src/app/domains/consumer/modules/poses/components/form.component.html +++ b/src/app/domains/consumer/modules/poses/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/consumer/modules/poses/components/form.component.ts b/src/app/domains/consumer/modules/poses/components/form.component.ts index 183cc42..299908e 100644 --- a/src/app/domains/consumer/modules/poses/components/form.component.ts +++ b/src/app/domains/consumer/modules/poses/components/form.component.ts @@ -5,14 +5,14 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IPosRequest, IPosResponse } from '../models'; import { ConsumerPosesService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'consumer-pos-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent], }) export class ConsumerPosFormComponent extends AbstractFormDialog { private readonly service = inject(ConsumerPosesService); diff --git a/src/app/domains/consumer/modules/poses/models/io.d.ts b/src/app/domains/consumer/modules/poses/models/io.d.ts index 50f079a..5c745ab 100644 --- a/src/app/domains/consumer/modules/poses/models/io.d.ts +++ b/src/app/domains/consumer/modules/poses/models/io.d.ts @@ -7,9 +7,13 @@ export interface IPosRawResponse { model?: string; status: string; pos_type: string; + created_at?: string; complex: ISummary; device?: ISummary; provider?: ISummary; + account?: { + username: string; + }; } export interface IPosResponse extends IPosRawResponse {} diff --git a/src/app/domains/consumer/modules/saleInvoices/models/io.d.ts b/src/app/domains/consumer/modules/saleInvoices/models/io.d.ts index 60125e5..7cf7b36 100644 --- a/src/app/domains/consumer/modules/saleInvoices/models/io.d.ts +++ b/src/app/domains/consumer/modules/saleInvoices/models/io.d.ts @@ -33,6 +33,6 @@ interface Complex extends ISummary { interface Payments { amount: string; - paid_at: string; + paid_at?: string; payment_method: string; } diff --git a/src/app/domains/partner/modules/accounts/components/form.component.html b/src/app/domains/partner/modules/accounts/components/form.component.html index eb59e74..58fb76b 100644 --- a/src/app/domains/partner/modules/accounts/components/form.component.html +++ b/src/app/domains/partner/modules/accounts/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/partner/modules/accounts/components/form.component.ts b/src/app/domains/partner/modules/accounts/components/form.component.ts index 513fc0b..6b6e7ec 100644 --- a/src/app/domains/partner/modules/accounts/components/form.component.ts +++ b/src/app/domains/partner/modules/accounts/components/form.component.ts @@ -4,14 +4,14 @@ import { SharedPasswordInputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IAccountRequest, IAccountResponse } from '../models'; import { AccountsService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'account-password-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, FormFooterActionsComponent, SharedPasswordInputComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, FormFooterActionsComponent, SharedPasswordInputComponent], }) export class PartnerAccountPasswordFormComponent extends AbstractFormDialog< IAccountRequest, diff --git a/src/app/domains/partner/modules/accounts/models/io.d.ts b/src/app/domains/partner/modules/accounts/models/io.d.ts index 06ff6de..177fb71 100644 --- a/src/app/domains/partner/modules/accounts/models/io.d.ts +++ b/src/app/domains/partner/modules/accounts/models/io.d.ts @@ -7,7 +7,10 @@ export interface IAccountRawResponse { export interface IAccountResponse extends IAccountRawResponse {} export interface IAccountRequest { - name: string; + username?: string; + password?: string; + role?: string; + status?: string; } export interface IAccountPasswordRequest { diff --git a/src/app/domains/partner/modules/accounts/views/list.component.html b/src/app/domains/partner/modules/accounts/views/list.component.html index 704076d..ff43b3f 100644 --- a/src/app/domains/partner/modules/accounts/views/list.component.html +++ b/src/app/domains/partner/modules/accounts/views/list.component.html @@ -8,11 +8,10 @@ /> @if (selectedItemForEdit()) { - } diff --git a/src/app/domains/partner/modules/accounts/views/list.component.ts b/src/app/domains/partner/modules/accounts/views/list.component.ts index 05f36ce..75920e5 100644 --- a/src/app/domains/partner/modules/accounts/views/list.component.ts +++ b/src/app/domains/partner/modules/accounts/views/list.component.ts @@ -1,9 +1,13 @@ // import { CatalogRoleTagComponent } from '@/shared/catalog/roles'; import { AbstractList } from '@/shared/abstractClasses/abstract-list'; +import { + ChangePasswordFormDialogComponent, + IChangePasswordSubmitPayload, +} from '@/shared/components'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; -import { Component, inject } from '@angular/core'; +import { Component, inject, signal } from '@angular/core'; import { Router } from '@angular/router'; -import { PartnerAccountPasswordFormComponent } from '../components/form.component'; +import { finalize } from 'rxjs'; import { partnerAccountsNamedRoutes } from '../constants'; import { IAccountResponse } from '../models'; import { AccountsService } from '../services/main.service'; @@ -11,11 +15,12 @@ import { AccountsService } from '../services/main.service'; @Component({ selector: 'partner-accounts', templateUrl: './list.component.html', - imports: [PageDataListComponent, PartnerAccountPasswordFormComponent], + imports: [PageDataListComponent, ChangePasswordFormDialogComponent], }) export class PartnerAccountsComponent extends AbstractList { private readonly service = inject(AccountsService); private readonly router = inject(Router); + passwordSubmitLoading = signal(false); override setColumns(): void { this.columns = [ @@ -48,4 +53,17 @@ export class PartnerAccountsComponent extends AbstractList { toSinglePage(account: IAccountResponse) { this.router.navigateByUrl(partnerAccountsNamedRoutes.account.meta.pagePath!(account.id)); } + + updatePassword(payload: IChangePasswordSubmitPayload) { + if (!this.selectedItemForEdit()?.id) return; + + this.passwordSubmitLoading.set(true); + this.service + .update(this.selectedItemForEdit()!.id, { password: payload.password }) + .pipe(finalize(() => this.passwordSubmitLoading.set(false))) + .subscribe(() => { + this.visibleForm.set(false); + this.refresh(); + }); + } } diff --git a/src/app/domains/partner/modules/consumers/components/accounts/form.component.html b/src/app/domains/partner/modules/consumers/components/accounts/form.component.html index 4c9c65b..3a4b946 100644 --- a/src/app/domains/partner/modules/consumers/components/accounts/form.component.html +++ b/src/app/domains/partner/modules/consumers/components/accounts/form.component.html @@ -1,4 +1,4 @@ -
- + -
+ diff --git a/src/app/domains/partner/modules/consumers/components/accounts/form.component.ts b/src/app/domains/partner/modules/consumers/components/accounts/form.component.ts index 8c5b4fe..587954c 100644 --- a/src/app/domains/partner/modules/consumers/components/accounts/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/accounts/form.component.ts @@ -2,21 +2,22 @@ import { MustMatch, password } from '@/core/validators'; // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { SharedPasswordInputComponent, UsernameComponent } from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IConsumerAccountRequest, IConsumerAccountResponse } from '../../models'; import { PartnerConsumerAccountsService } from '../../services/accounts.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-consumer-account-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + UsernameComponent, FormFooterActionsComponent, SharedPasswordInputComponent, EnumSelectComponent, @@ -35,7 +36,7 @@ export class ConsumerAccountFormComponent extends AbstractFormDialog< if (this.editMode) { return this.fb.group( { - username: [this.initialValues?.account.username || '', [Validators.required]], + username: fieldControl.username(this.initialValues?.account.username || ''), password: ['', [password()]], confirmPassword: [''], }, @@ -46,7 +47,7 @@ export class ConsumerAccountFormComponent extends AbstractFormDialog< } return this.fb.group( { - username: [this.initialValues?.account.username || '', [Validators.required]], + username: fieldControl.username(this.initialValues?.account.username || ''), role: ['', [Validators.required]], password: ['', [Validators.required, password()]], confirmPassword: ['', [Validators.required]], diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/create-wrapper/create-wrapper.component.html b/src/app/domains/partner/modules/consumers/components/businessActivities/create-wrapper/create-wrapper.component.html new file mode 100644 index 0000000..12eda76 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/create-wrapper/create-wrapper.component.html @@ -0,0 +1,45 @@ + + + + فعالیت اقتصادی + فروشگاه + پایانه‌ی فروشگاهی + + + +
+ @switch (activeStep()) { + @case (1) { + + } + @case (2) { + + } + @case (3) { + + } + } +
+
diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/create-wrapper/create-wrapper.component.ts b/src/app/domains/partner/modules/consumers/components/businessActivities/create-wrapper/create-wrapper.component.ts new file mode 100644 index 0000000..233659a --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/create-wrapper/create-wrapper.component.ts @@ -0,0 +1,84 @@ +import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; +import { Component, effect, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Step, StepList, Stepper } from 'primeng/stepper'; +import { IBusinessActivityResponse, IComplexResponse, IPosResponse } from '../../../models'; +import { ConsumerComplexFormContentComponent } from '../../complexes/form.component'; +import { ConsumerPosFormContentComponent } from '../../poses/form.component'; +import { ConsumerBusinessActivitiesFormComponent } from '../form.component'; + +export interface ICreateBusinessActivityStepperResult { + businessActivity: IBusinessActivityResponse; + complex: IComplexResponse; + pos: IPosResponse; +} + +@Component({ + selector: 'partner-consumer-businessActivities-create-wrapper', + templateUrl: './create-wrapper.component.html', + imports: [ + SharedDialogComponent, + Stepper, + StepList, + Step, + ConsumerBusinessActivitiesFormComponent, + ConsumerComplexFormContentComponent, + ConsumerPosFormContentComponent, + ], +}) +export class ConsumerBusinessActivitiesCreateWrapperComponent extends AbstractDialog { + @Input({ required: true }) consumerId!: string; + @Output() onSubmit = new EventEmitter(); + + activeStep = signal(1); + businessActivity = signal(null); + complex = signal(null); + + constructor() { + super(); + + effect(() => { + if (this.visible) { + this.resetWizard(); + } + }); + } + + onBusinessActivitySubmit(response: IBusinessActivityResponse) { + this.businessActivity.set(response); + this.activeStep.set(2); + } + + onComplexSubmit(response: IComplexResponse) { + this.complex.set(response); + this.activeStep.set(3); + } + + onPosSubmit(response: IPosResponse) { + const businessActivity = this.businessActivity(); + const complex = this.complex(); + + if (!businessActivity || !complex) { + return; + } + + this.onSubmit.emit({ + businessActivity, + complex, + pos: response, + }); + + this.close(); + } + + override close() { + this.resetWizard(); + super.close(); + } + + private resetWizard() { + this.activeStep.set(1); + this.businessActivity.set(null); + this.complex.set(null); + } +} diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.html b/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.html index 966c95e..e5edd4e 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.html +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.ts b/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.ts index b1325a5..731c02c 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.ts +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/form-dialog.component.ts @@ -1,13 +1,13 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Dialog } from 'primeng/dialog'; import { IBusinessActivityResponse } from '../../models'; import { ConsumerBusinessActivitiesFormComponent } from './form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-consumer-businessActivities-form', templateUrl: './form-dialog.component.html', - imports: [Dialog, ConsumerBusinessActivitiesFormComponent], + imports: [SharedDialogComponent, ConsumerBusinessActivitiesFormComponent], }) export class ConsumerBusinessActivitiesFormDialogComponent extends AbstractDialog { @Output() onSubmit = new EventEmitter(); diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.html b/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.html index 1ea2ecd..a0ab54e 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.html +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.html @@ -1,15 +1,14 @@
- - - + + + + + @if (!editMode) { اطلاعات لایسنس - } diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts b/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts index e20c2e6..05f1597 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/form.component.ts @@ -1,10 +1,16 @@ import { AbstractForm } from '@/shared/abstractClasses'; -import { CatalogGuildSelectComponent } from '@/shared/catalog/guild/components/select.component'; -import { InputComponent } from '@/shared/components'; +import { + EconomicCodeComponent, + FiscalCodeComponent, + GuildIdComponent, + LicenseStartsAtComponent, + NameComponent, + PartnerTokenComponent, +} from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; -import { UikitFlatpickrJalaliComponent } from '@/uikit'; +import { fieldControl } from '@/shared/constants/fields'; import { Component, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; import { Divider } from 'primeng/divider'; import { IBusinessActivityRequest, IBusinessActivityResponse } from '../../models'; import { PartnerConsumerBusinessActivitiesService } from '../../services/businessActivities.service'; @@ -14,11 +20,14 @@ import { PartnerConsumerBusinessActivitiesService } from '../../services/busines templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - InputComponent, + NameComponent, + EconomicCodeComponent, + FiscalCodeComponent, + PartnerTokenComponent, + GuildIdComponent, + LicenseStartsAtComponent, FormFooterActionsComponent, - CatalogGuildSelectComponent, Divider, - UikitFlatpickrJalaliComponent, ], }) export class ConsumerBusinessActivitiesFormComponent extends AbstractForm< @@ -32,9 +41,11 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm< initForm = () => { const form = this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - economic_code: [this.initialValues?.economic_code || '', [Validators.required]], - guild_id: [this.initialValues?.guild?.id || '', [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''), + fiscal_code: fieldControl.fiscal_code(this.initialValues?.fiscal_code || ''), + partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''), + guild_id: fieldControl.guild_id(this.initialValues?.guild?.id || ''), license_starts_at: [''], }); @@ -46,7 +57,7 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractForm< 'license_starts_at', this.fb.control('', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.license_starts_at('')[1], }), ); } diff --git a/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html b/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html index a5e9c41..e7f9169 100644 --- a/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html +++ b/src/app/domains/partner/modules/consumers/components/businessActivities/list.component.html @@ -16,9 +16,14 @@ (onRefresh)="refresh()" > + { @Input({ required: true }) consumerId!: string; @@ -50,6 +55,7 @@ export class ConsumerBusinessActivitiesComponent extends AbstractList - + diff --git a/src/app/domains/partner/modules/consumers/components/chargeAccount/charge-account-form-dialog.component.ts b/src/app/domains/partner/modules/consumers/components/chargeAccount/charge-account-form-dialog.component.ts index 6ec8c89..7d11216 100644 --- a/src/app/domains/partner/modules/consumers/components/chargeAccount/charge-account-form-dialog.component.ts +++ b/src/app/domains/partner/modules/consumers/components/chargeAccount/charge-account-form-dialog.component.ts @@ -3,14 +3,19 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IPartnerChargeAccountRequest } from '../../models'; import { PartnerChargeAccountService } from '../../services/chargeAccount.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-charge-account-form-dialog', templateUrl: './charge-account-form-dialog.component.html', - imports: [Dialog, ReactiveFormsModule, InputComponent, FormFooterActionsComponent], + imports: [ + SharedDialogComponent, + ReactiveFormsModule, + InputComponent, + FormFooterActionsComponent, + ], }) export class PartnerChargeAccountFormDialogComponent extends AbstractFormDialog< IPartnerChargeAccountRequest, diff --git a/src/app/domains/partner/modules/consumers/components/complexes/create-wrapper/create-wrapper.component.html b/src/app/domains/partner/modules/consumers/components/complexes/create-wrapper/create-wrapper.component.html new file mode 100644 index 0000000..c277108 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/complexes/create-wrapper/create-wrapper.component.html @@ -0,0 +1,37 @@ + + + + فروشگاه + پایانه‌ی روشگاهی + + + +
+ @switch (activeStep()) { + @case (1) { + + } + @case (2) { + + } + } +
+
diff --git a/src/app/domains/partner/modules/consumers/components/complexes/create-wrapper/create-wrapper.component.ts b/src/app/domains/partner/modules/consumers/components/complexes/create-wrapper/create-wrapper.component.ts new file mode 100644 index 0000000..49a4cc3 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/complexes/create-wrapper/create-wrapper.component.ts @@ -0,0 +1,72 @@ +import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; +import { Component, effect, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Step, StepList, Stepper } from 'primeng/stepper'; +import { IComplexResponse, IPosResponse } from '../../../models'; +import { ConsumerPosFormContentComponent } from '../../poses/form.component'; +import { ConsumerComplexFormContentComponent } from '../form.component'; + +export interface ICreateComplexStepperResult { + complex: IComplexResponse; + pos: IPosResponse; +} + +@Component({ + selector: 'partner-consumer-complexes-create-wrapper', + templateUrl: './create-wrapper.component.html', + imports: [ + SharedDialogComponent, + Stepper, + StepList, + Step, + ConsumerComplexFormContentComponent, + ConsumerPosFormContentComponent, + ], +}) +export class ConsumerComplexesCreateWrapperComponent extends AbstractDialog { + @Input({ required: true }) consumerId!: string; + @Input({ required: true }) businessActivityId!: string; + @Output() onSubmit = new EventEmitter(); + + activeStep = signal(1); + complex = signal(null); + + constructor() { + super(); + + effect(() => { + if (this.visible) { + this.resetWizard(); + } + }); + } + + onComplexSubmit(response: IComplexResponse) { + this.complex.set(response); + this.activeStep.set(2); + } + + onPosSubmit(response: IPosResponse) { + const complex = this.complex(); + if (!complex) { + return; + } + + this.onSubmit.emit({ + complex, + pos: response, + }); + + this.close(); + } + + override close() { + this.resetWizard(); + super.close(); + } + + private resetWizard() { + this.activeStep.set(1); + this.complex.set(null); + } +} diff --git a/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.html b/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.html index db27aa3..0d93858 100644 --- a/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.html +++ b/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.ts b/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.ts index c3251a0..69fcfc8 100644 --- a/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.ts +++ b/src/app/domains/partner/modules/consumers/components/complexes/form-dialog.component.ts @@ -1,13 +1,13 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Dialog } from 'primeng/dialog'; import { IComplexResponse } from '../../models'; import { ConsumerComplexFormContentComponent } from './form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-consumer-complex-form', templateUrl: './form-dialog.component.html', - imports: [Dialog, ConsumerComplexFormContentComponent], + imports: [SharedDialogComponent, ConsumerComplexFormContentComponent], }) export class ConsumerComplexFormDialogComponent extends AbstractDialog { @Output() onSubmit = new EventEmitter(); diff --git a/src/app/domains/partner/modules/consumers/components/complexes/form.component.html b/src/app/domains/partner/modules/consumers/components/complexes/form.component.html index 34fb8d7..0afc1c6 100644 --- a/src/app/domains/partner/modules/consumers/components/complexes/form.component.html +++ b/src/app/domains/partner/modules/consumers/components/complexes/form.component.html @@ -1,6 +1,6 @@
- - - + + + diff --git a/src/app/domains/partner/modules/consumers/components/complexes/form.component.ts b/src/app/domains/partner/modules/consumers/components/complexes/form.component.ts index 175d8b6..f5d7421 100644 --- a/src/app/domains/partner/modules/consumers/components/complexes/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/complexes/form.component.ts @@ -1,16 +1,27 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractForm } from '@/shared/abstractClasses'; -import { InputComponent } from '@/shared/components'; +import { + NameComponent, + AddressComponent, + BranchCodeComponent, +} from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; import { IComplexRequest, IComplexResponse } from '../../models'; import { PartnerComplexesService } from '../../services/complexes.service'; @Component({ selector: 'partner-consumer-complex-form-content', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, InputComponent, FormFooterActionsComponent], + imports: [ + ReactiveFormsModule, + NameComponent, + BranchCodeComponent, + AddressComponent, + FormFooterActionsComponent, + ], }) export class ConsumerComplexFormContentComponent extends AbstractForm< IComplexRequest, @@ -24,9 +35,9 @@ export class ConsumerComplexFormContentComponent extends AbstractForm< initForm = () => { return this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - branch_code: [this.initialValues?.branch_code || ''], - address: [this.initialValues?.address || '', [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + branch_code: fieldControl.branch_code(this.initialValues?.branch_code || ''), + address: fieldControl.address(this.initialValues?.address || ''), }); }; diff --git a/src/app/domains/partner/modules/consumers/components/complexes/list.component.html b/src/app/domains/partner/modules/consumers/components/complexes/list.component.html index b7686f2..7bf03d4 100644 --- a/src/app/domains/partner/modules/consumers/components/complexes/list.component.html +++ b/src/app/domains/partner/modules/consumers/components/complexes/list.component.html @@ -16,9 +16,15 @@ (onRefresh)="refresh()" > + { @Input() consumerId!: string; @@ -23,6 +28,8 @@ export class ConsumerComplexesComponent extends AbstractList { @Input() header: IColumn[] = [ { field: 'id', header: 'شناسه', type: 'id' }, { field: 'name', header: 'عنوان' }, + { field: 'branch_code', header: 'کد شعبه' }, + { field: 'pos_count', header: 'تعداد پایانه‌ها' }, { field: 'created_at', header: 'تاریخ ایجاد', @@ -32,6 +39,7 @@ export class ConsumerComplexesComponent extends AbstractList { private readonly service = inject(PartnerComplexesService); private readonly router = inject(Router); + visibleCreateForm = signal(false); override setColumns(): void { this.columns = this.header; @@ -41,6 +49,10 @@ export class ConsumerComplexesComponent extends AbstractList { return this.service.getAll(this.consumerId, this.businessId); } + override openAddForm() { + this.visibleCreateForm.set(true); + } + toSinglePage(item: IComplexResponse) { this.router.navigateByUrl( partnerConsumerComplexesNamedRoutes.complex.meta.pagePath!( diff --git a/src/app/domains/partner/modules/consumers/components/createConsumerWrapper/create-consumer-wrapper.component.html b/src/app/domains/partner/modules/consumers/components/createConsumerWrapper/create-consumer-wrapper.component.html new file mode 100644 index 0000000..f5b1479 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/createConsumerWrapper/create-consumer-wrapper.component.html @@ -0,0 +1,49 @@ + + + + اطلاعات اولیه + فعالیت اقتصادی + فروشگاه + پایانه‌ی فروشگاهی + + + +
+ @switch (activeStep()) { + @case (1) { + + } + @case (2) { + + } + @case (3) { + + } + @case (4) { + + } + } +
+
diff --git a/src/app/domains/partner/modules/consumers/components/createConsumerWrapper/create-consumer-wrapper.component.ts b/src/app/domains/partner/modules/consumers/components/createConsumerWrapper/create-consumer-wrapper.component.ts new file mode 100644 index 0000000..01e8a02 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/createConsumerWrapper/create-consumer-wrapper.component.ts @@ -0,0 +1,100 @@ +import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; +import { Component, effect, EventEmitter, Output, signal } from '@angular/core'; +import { Step, StepList, Stepper } from 'primeng/stepper'; +import { + IBusinessActivityResponse, + IComplexResponse, + IPartnerConsumerResponse, + IPosResponse, +} from '../../models'; +import { ConsumerBusinessActivitiesFormComponent } from '../businessActivities/form.component'; +import { ConsumerComplexFormContentComponent } from '../complexes/form.component'; +import { ConsumerUserFormContentComponent } from '../form.component'; +import { ConsumerPosFormContentComponent } from '../poses/form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; + +export interface ICreateConsumerStepperResult { + consumer: IPartnerConsumerResponse; + businessActivity: IBusinessActivityResponse; + complex: IComplexResponse; + pos: IPosResponse; +} + +@Component({ + selector: 'partner-create-consumer-wrapper', + templateUrl: './create-consumer-wrapper.component.html', + imports: [ + Stepper, + StepList, + Step, + ConsumerUserFormContentComponent, + ConsumerBusinessActivitiesFormComponent, + ConsumerComplexFormContentComponent, + ConsumerPosFormContentComponent, + SharedDialogComponent, + ], +}) +export class CreateConsumerWrapperComponent extends AbstractDialog { + @Output() onSubmit = new EventEmitter(); + + activeStep = signal(1); + consumer = signal(null); + businessActivity = signal(null); + complex = signal(null); + + constructor() { + super(); + + effect(() => { + if (this.visible) { + this.resetWizard(); + } + }); + } + + onConsumerSubmit(response: IPartnerConsumerResponse) { + this.consumer.set(response); + this.activeStep.set(2); + } + + onBusinessActivitySubmit(response: IBusinessActivityResponse) { + this.businessActivity.set(response); + this.activeStep.set(3); + } + + onComplexSubmit(response: IComplexResponse) { + this.complex.set(response); + this.activeStep.set(4); + } + + onPosSubmit(response: IPosResponse) { + const consumer = this.consumer(); + const businessActivity = this.businessActivity(); + const complex = this.complex(); + + if (!consumer || !businessActivity || !complex) { + return; + } + + this.onSubmit.emit({ + consumer, + businessActivity, + complex, + pos: response, + }); + + this.close(); + } + + override close() { + this.resetWizard(); + super.close(); + } + + private resetWizard() { + this.activeStep.set(1); + this.consumer.set(null); + this.businessActivity.set(null); + this.complex.set(null); + } +} diff --git a/src/app/domains/partner/modules/consumers/components/form-dialog.component.html b/src/app/domains/partner/modules/consumers/components/form-dialog.component.html new file mode 100644 index 0000000..f0087e2 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/form-dialog.component.html @@ -0,0 +1,16 @@ + + + diff --git a/src/app/domains/partner/modules/consumers/components/form-dialog.component.ts b/src/app/domains/partner/modules/consumers/components/form-dialog.component.ts new file mode 100644 index 0000000..c2aeb95 --- /dev/null +++ b/src/app/domains/partner/modules/consumers/components/form-dialog.component.ts @@ -0,0 +1,27 @@ +import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { IPartnerConsumerResponse } from '../models'; +import { ConsumerUserFormContentComponent } from './form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; + +@Component({ + selector: 'partner-consumer-form', + templateUrl: './form-dialog.component.html', + imports: [SharedDialogComponent, ConsumerUserFormContentComponent], +}) +export class ConsumerUserFormDialogComponent extends AbstractDialog { + @Output() onSubmit = new EventEmitter(); + @Input() initialValues?: IPartnerConsumerResponse; + @Input() editMode?: boolean; + + @Input() consumerId?: string; + + get preparedTitle() { + return `${this.editMode ? 'ویرایش' : 'ایجاد'} مشتری`; + } + + onFormSubmit(response: IPartnerConsumerResponse) { + this.onSubmit.emit(response); + this.close(); + } +} diff --git a/src/app/domains/partner/modules/consumers/components/form.component.html b/src/app/domains/partner/modules/consumers/components/form.component.html index cddace7..c6731f7 100644 --- a/src/app/domains/partner/modules/consumers/components/form.component.html +++ b/src/app/domains/partner/modules/consumers/components/form.component.html @@ -1,34 +1,32 @@ - -
- - - - +@if (!editMode) { + +} + + @if (selectedType() === "INDIVIDUAL" && form.controls.individual) { + + + + + } @else if (form.controls.legal) { + + + } - @if (!editMode) { - اطلاعات ورود - - - - اطلاعات لایسنس - - } + @if (!editMode) { + اطلاعات ورود + + + + } - - -
+ + diff --git a/src/app/domains/partner/modules/consumers/components/form.component.ts b/src/app/domains/partner/modules/consumers/components/form.component.ts index 95dffcb..0247233 100644 --- a/src/app/domains/partner/modules/consumers/components/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/form.component.ts @@ -1,55 +1,88 @@ -import { mobileValidator, MustMatch } from '@/core/validators'; -import { nationalIdValidator } from '@/core/validators/national-id.validator'; -import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { MustMatch } from '@/core/validators'; +import { AbstractForm } from '@/shared/abstractClasses'; +import { fieldControl } from '@/shared/constants/fields'; +import { + FirstNameComponent, + LastNameComponent, + LegalNameComponent, + MobileNumberComponent, + NationalCodeComponent, + RegistrationCodeComponent, + SharedPasswordInputComponent, + UsernameComponent, +} from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; -import { UikitFlatpickrJalaliComponent } from '@/uikit'; -import { nowJalali } from '@/utils'; -import { Component, computed, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; +import { Component, inject, Input, signal } from '@angular/core'; +import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { Divider } from 'primeng/divider'; -import { IConsumerRequest, IConsumerResponse } from '../models'; +import { SelectButton } from 'primeng/selectbutton'; +import { IPartnerConsumerRequest, IPartnerConsumerResponse } from '../models'; import { ConsumersService } from '../services/main.service'; @Component({ - selector: 'partner-consumer-form', + selector: 'partner-consumer-form-content', templateUrl: './form.component.html', imports: [ + FormsModule, ReactiveFormsModule, - Dialog, - InputComponent, + FirstNameComponent, + LastNameComponent, + MobileNumberComponent, + NationalCodeComponent, + LegalNameComponent, + RegistrationCodeComponent, + UsernameComponent, FormFooterActionsComponent, Divider, - UikitFlatpickrJalaliComponent, SharedPasswordInputComponent, + SelectButton, ], }) -export class ConsumerUserFormComponent extends AbstractFormDialog< - IConsumerRequest, - IConsumerResponse +export class ConsumerUserFormContentComponent extends AbstractForm< + IPartnerConsumerRequest, + IPartnerConsumerResponse > { @Input() consumerId?: string; private service = inject(ConsumersService); - preparedTitle = computed(() => `${this.editMode ? 'ویرایش' : 'ایجاد'} مشتری`); + consumerTypes = [ + { + value: 'INDIVIDUAL', + name: 'حقیقی', + }, + { + value: 'LEGAL', + name: 'حقوقی', + }, + ]; + + selectedType = signal(this.initialValues?.type || this.consumerTypes[0].value); initForm = () => { + this.selectedType.set(this.initialValues?.type || this.consumerTypes[0].value); + const legal = this.fb.group({ + name: fieldControl.legal_name(this.initialValues?.legal?.name || ''), + registration_code: fieldControl.registration_code( + this.initialValues?.legal?.registration_code || '', + ), + }); + const individual = this.fb.group({ + first_name: fieldControl.first_name(this.initialValues?.individual?.first_name || ''), + last_name: fieldControl.last_name(this.initialValues?.individual?.last_name || ''), + mobile_number: fieldControl.mobile_number( + this.initialValues?.individual?.mobile_number || '', + ), + national_code: fieldControl.national_code( + this.initialValues?.individual?.national_code || '', + ), + }); const form = this.fb.group({ - first_name: [this.initialValues?.first_name || '', [Validators.required]], - last_name: [this.initialValues?.last_name || '', [Validators.required]], - mobile_number: [ - this.initialValues?.mobile_number || '', - [Validators.required, mobileValidator()], - ], - national_code: [ - this.initialValues?.national_code || '', - [Validators.required, nationalIdValidator()], - ], + type: [this.initialValues?.type || this.consumerTypes[0].value, [Validators.required]], + legal, + individual, username: [''], password: [''], confirmPassword: [''], - license_starts_at: [nowJalali(), [Validators.required]], }); if (this.editMode) { @@ -59,15 +92,13 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< form.removeControl('password'); // @ts-ignore form.removeControl('confirmPassword'); - // @ts-ignore - form.removeControl('license_starts_at'); form.removeValidators([MustMatch('password', 'confirmPassword')]); } else { form.addControl( 'username', this.fb.control('', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.username('')[1], }), ); form.addControl( @@ -84,23 +115,43 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< validators: [Validators.required], }), ); - form.addControl( - 'license_starts_at', - this.fb.control(nowJalali(), { - nonNullable: true, - validators: [Validators.required], - }), - ); + form.addValidators([MustMatch('password', 'confirmPassword')]); } + if (form.controls.type.value === 'LEGAL') { + // @ts-ignore + form.addControl('legal', legal); + // @ts-ignore + form.removeControl('individual'); + } else { + // @ts-ignore + form.addControl('individual', individual); + // @ts-ignore + form.removeControl('legal'); + } + + form.controls.type.valueChanges.subscribe((type) => { + if (type === 'LEGAL') { + // @ts-ignore + form.addControl('legal', legal); + // @ts-ignore + form.removeControl('individual'); + } else { + // @ts-ignore + form.addControl('individual', individual); + // @ts-ignore + form.removeControl('legal'); + } + }); + return form; }; form = this.initForm(); override ngOnChanges() { - this.form.patchValue(this.initialValues || {}); + this.form.patchValue((this.initialValues as any) || {}); if (this.editMode && !this.consumerId) { throw 'missing some arguments'; } @@ -108,7 +159,7 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< this.form = this.initForm(); } - override submitForm(payload: IConsumerRequest) { + override submitForm(payload: IPartnerConsumerRequest) { if (this.editMode) { return this.service.update(this.consumerId!, payload); } @@ -116,4 +167,8 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< const { confirmPassword, ...rest } = payload; return this.service.create(rest); } + + changeType(newType: string) { + this.form.controls.type.setValue(newType); + } } diff --git a/src/app/domains/partner/modules/consumers/components/licenses/form.component.html b/src/app/domains/partner/modules/consumers/components/licenses/form.component.html index 2b160b1..a8c1c44 100644 --- a/src/app/domains/partner/modules/consumers/components/licenses/form.component.html +++ b/src/app/domains/partner/modules/consumers/components/licenses/form.component.html @@ -1,4 +1,4 @@ - --> - + diff --git a/src/app/domains/partner/modules/consumers/components/licenses/form.component.ts b/src/app/domains/partner/modules/consumers/components/licenses/form.component.ts index 560fbbd..dc564c2 100644 --- a/src/app/domains/partner/modules/consumers/components/licenses/form.component.ts +++ b/src/app/domains/partner/modules/consumers/components/licenses/form.component.ts @@ -3,14 +3,19 @@ import { FormFooterActionsComponent } from '@/shared/components/formFooterAction import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { Component, computed, inject, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { ILicenseRequest, ILicenseResponse } from '../../models'; import { LicensesService } from '../../services/licenses.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-consumer-license-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, FormFooterActionsComponent, UikitFlatpickrJalaliComponent], + imports: [ + ReactiveFormsModule, + SharedDialogComponent, + FormFooterActionsComponent, + UikitFlatpickrJalaliComponent, + ], }) export class ConsumerLicenseFormComponent extends AbstractFormDialog< ILicenseRequest, diff --git a/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.html b/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.html index 48b9e16..11ea5bd 100644 --- a/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.html +++ b/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.ts b/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.ts index f3319a4..1edcca5 100644 --- a/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.ts +++ b/src/app/domains/partner/modules/consumers/components/poses/form-dialog.component.ts @@ -1,13 +1,13 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { Dialog } from 'primeng/dialog'; import { IPosResponse } from '../../models'; import { ConsumerPosFormContentComponent } from './form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-consumer-pos-form', templateUrl: './form-dialog.component.html', - imports: [Dialog, ConsumerPosFormContentComponent], + imports: [SharedDialogComponent, ConsumerPosFormContentComponent], }) export class ConsumerPosFormDialogComponent extends AbstractDialog { @Output() onSubmit = new EventEmitter(); diff --git a/src/app/domains/partner/modules/consumers/components/poses/form.component.html b/src/app/domains/partner/modules/consumers/components/poses/form.component.html index 9ef9410..0dd0f2f 100644 --- a/src/app/domains/partner/modules/consumers/components/poses/form.component.html +++ b/src/app/domains/partner/modules/consumers/components/poses/form.component.html @@ -1,16 +1,16 @@
- - + + @if (form.controls.pos_type.value === "PSP") { - - - + + + } @if (!editMode) { اطلاعات ورود - + { const form = this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - pos_type: [this.initialValues?.pos_type || '', [Validators.required]], - serial_number: [this.initialValues?.serial_number || ''], - device_id: [this.initialValues?.device?.id || ''], - provider_id: [this.initialValues?.provider?.id || ''], + name: fieldControl.name(this.initialValues?.name || ''), + pos_type: fieldControl.pos_type(this.initialValues?.pos_type || ''), + serial_number: fieldControl.serial_number(this.initialValues?.serial_number || ''), + device_id: fieldControl.device_id(this.initialValues?.device?.id || ''), + provider_id: fieldControl.provider_id(this.initialValues?.provider?.id || ''), username: [''], password: [''], confirmPassword: [''], @@ -81,13 +89,15 @@ export class ConsumerPosFormContentComponent extends AbstractForm('', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.username('')[1], }), ); + // @ts-ignore form.addControl( 'password', this.fb.control('', { @@ -95,6 +105,7 @@ export class ConsumerPosFormContentComponent extends AbstractForm('', { diff --git a/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts b/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts index 9c590bf..72e3555 100644 --- a/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts +++ b/src/app/domains/partner/modules/consumers/models/businessActivities_io.d.ts @@ -4,6 +4,8 @@ export interface IBusinessActivityRawResponse { id: string; name: string; economic_code: string; + fiscal_code: string; + partner_token: string; guild: ISummary; created_at: string; license_info: LicenseInfo; @@ -13,9 +15,11 @@ export interface IBusinessActivityResponse extends IBusinessActivityRawResponse export interface IBusinessActivityRequest { name: string; economic_code: string; + fiscal_code: string; + partner_token: string; guild_id: string; - username?: string; - password?: string; + license_starts_at?: string; + expires_at?: string; } interface LicenseInfo { diff --git a/src/app/domains/partner/modules/consumers/models/chargeAccount_io.d.ts b/src/app/domains/partner/modules/consumers/models/chargeAccount_io.d.ts index 0079cbf..74f80fa 100644 --- a/src/app/domains/partner/modules/consumers/models/chargeAccount_io.d.ts +++ b/src/app/domains/partner/modules/consumers/models/chargeAccount_io.d.ts @@ -1,11 +1,8 @@ export interface IPartnerChargeAccountRawResponse { id: string; + credit_id: string; + license_activation_id: string; created_at: string; - // tracking_code: string; - // activation_expires_at: string; - // charged_license_count: number; - // activated_license_count: number; - // remained_license_count: number; } export interface IPartnerChargeAccountResponse extends IPartnerChargeAccountRawResponse {} diff --git a/src/app/domains/partner/modules/consumers/models/io.d.ts b/src/app/domains/partner/modules/consumers/models/io.d.ts index e1f95ea..7005c9b 100644 --- a/src/app/domains/partner/modules/consumers/models/io.d.ts +++ b/src/app/domains/partner/modules/consumers/models/io.d.ts @@ -1,28 +1,6 @@ -export interface IConsumerRawResponse { - id: string; - first_name: string; - last_name: string; - mobile_number: string; - national_code: string; - status: string; - created_at: string; - fullname: string; - license_info: ILicenseInfo; -} -export interface IConsumerResponse extends IConsumerRawResponse {} +import { IConsumerRequest, IConsumerResponse } from '@/shared/models/consumer.type'; -export interface IConsumerRequest { - first_name: string; - last_name: string; - mobile_number: string; - national_code: string; - username: string; - password: string; - license_starts_at: string; -} +export interface IPartnerConsumerRawResponse extends IConsumerResponse {} +export interface IPartnerConsumerResponse extends IPartnerConsumerRawResponse {} -interface ILicenseInfo { - id: string; - starts_at: string; - expires_at: string; -} +export interface IPartnerConsumerRequest extends IConsumerRequest {} diff --git a/src/app/domains/partner/modules/consumers/services/chargeAccount.service.ts b/src/app/domains/partner/modules/consumers/services/chargeAccount.service.ts index 281815b..7d3c002 100644 --- a/src/app/domains/partner/modules/consumers/services/chargeAccount.service.ts +++ b/src/app/domains/partner/modules/consumers/services/chargeAccount.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { PARTNER_BA_ACCOUNTS_CHARGE_API_ROUTES } from '../constants/apiRoutes/accountsCharge'; -import { IPartnerChargeAccountRequest } from '../models'; +import { IPartnerChargeAccountRequest, IPartnerChargeAccountResponse } from '../models'; @Injectable({ providedIn: 'root' }) export class PartnerChargeAccountService { @@ -20,8 +20,8 @@ export class PartnerChargeAccountService { consumerId: string, businessId: string, data: IPartnerChargeAccountRequest, - ): Observable { - return this.http.post( + ): Observable { + return this.http.post( this.apiRoutes.single(consumerId, businessId), data, ); diff --git a/src/app/domains/partner/modules/consumers/services/main.service.ts b/src/app/domains/partner/modules/consumers/services/main.service.ts index 8fe9cd6..43b0cf6 100644 --- a/src/app/domains/partner/modules/consumers/services/main.service.ts +++ b/src/app/domains/partner/modules/consumers/services/main.service.ts @@ -3,7 +3,11 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { CONSUMERS_API_ROUTES } from '../constants'; -import { IConsumerRawResponse, IConsumerRequest, IConsumerResponse } from '../models'; +import { + IPartnerConsumerRawResponse, + IPartnerConsumerRequest, + IPartnerConsumerResponse, +} from '../models'; @Injectable({ providedIn: 'root' }) export class ConsumersService { @@ -11,18 +15,21 @@ export class ConsumersService { private apiRoutes = CONSUMERS_API_ROUTES; - getAll(): Observable> { - return this.http.get>(this.apiRoutes.list()); + getAll(): Observable> { + return this.http.get>(this.apiRoutes.list()); } - getSingle(consumerId: string): Observable { - return this.http.get(this.apiRoutes.single(consumerId)); + getSingle(consumerId: string): Observable { + return this.http.get(this.apiRoutes.single(consumerId)); } - create(userData: IConsumerRequest): Observable { - return this.http.post(this.apiRoutes.list(), userData); + create(userData: IPartnerConsumerRequest): Observable { + return this.http.post(this.apiRoutes.list(), userData); } - update(consumerId: string, userData: IConsumerRequest): Observable { - return this.http.patch(this.apiRoutes.single(consumerId), userData); + update( + consumerId: string, + userData: IPartnerConsumerRequest, + ): Observable { + return this.http.patch(this.apiRoutes.single(consumerId), userData); } } diff --git a/src/app/domains/partner/modules/consumers/store/consumer.store.ts b/src/app/domains/partner/modules/consumers/store/consumer.store.ts index 0efcc5f..e761fed 100644 --- a/src/app/domains/partner/modules/consumers/store/consumer.store.ts +++ b/src/app/domains/partner/modules/consumers/store/consumer.store.ts @@ -3,17 +3,17 @@ import { computed, inject, Injectable } from '@angular/core'; import { MenuItem } from 'primeng/api'; import { catchError, finalize } from 'rxjs'; import { partnerConsumersNamedRoutes } from '../constants'; -import { IConsumerResponse } from '../models'; +import { IPartnerConsumerResponse } from '../models'; import { ConsumersService } from '../services/main.service'; -interface ConsumerState extends EntityState { +interface ConsumerState extends EntityState { breadcrumbItems: MenuItem[]; } @Injectable({ providedIn: 'root', }) -export class ConsumerStore extends EntityStore { +export class ConsumerStore extends EntityStore { private readonly service = inject(ConsumersService); constructor() { super({ @@ -36,7 +36,7 @@ export class ConsumerStore extends EntityStore routerLink: partnerConsumersNamedRoutes.consumers.meta.pagePath!(), }, { - title: this.entity()?.fullname, + title: this.entity()?.name, routerLink: partnerConsumersNamedRoutes.consumer.meta.pagePath!(consumerId), }, ], diff --git a/src/app/domains/partner/modules/consumers/views/list.component.html b/src/app/domains/partner/modules/consumers/views/list.component.html index d99e857..acd5a29 100644 --- a/src/app/domains/partner/modules/consumers/views/list.component.html +++ b/src/app/domains/partner/modules/consumers/views/list.component.html @@ -15,9 +15,11 @@ (onEdit)="onEditClick($event)" (onRefresh)="refresh()" /> + + { +export class ConsumersComponent extends AbstractList { private readonly service = inject(ConsumersService); private readonly router = inject(Router); + visibleCreateForm = signal(false); override setColumns(): void { this.columns = [ { field: 'id', header: 'شناسه', type: 'id' }, - { field: 'fullname', header: 'نام' }, - { field: 'mobile_number', header: 'شماره موبایل' }, - { field: 'national_code', header: 'کد ملی' }, - // { field: 'status', header: 'وضعیت' }, - // { - // field: 'license_expires_at', - // header: 'تاریخ انقضای لایسنس', - // customDataModel(item) { - // if (item.license_info) { - // return formatJalali(item.license_info.expires_at); - // } - // return 'بدون لایسنس'; - // }, - // }, + { field: 'name', header: 'نام' }, + { field: 'business_counts', header: 'تعداد فعالیت‌های اقتصادی فعال' }, + { field: 'created_at', header: 'تاریخ ایجاد', @@ -46,7 +41,11 @@ export class ConsumersComponent extends AbstractList { return this.service.getAll(); } - toSinglePage(consumer: IConsumerResponse) { + override openAddForm() { + this.visibleCreateForm.set(true); + } + + toSinglePage(consumer: IPartnerConsumerResponse) { this.router.navigateByUrl(partnerConsumersNamedRoutes.consumer.meta.pagePath!(consumer.id)); } } diff --git a/src/app/domains/partner/modules/consumers/views/single.component.html b/src/app/domains/partner/modules/consumers/views/single.component.html index 55573ae..c317862 100644 --- a/src/app/domains/partner/modules/consumers/views/single.component.html +++ b/src/app/domains/partner/modules/consumers/views/single.component.html @@ -3,8 +3,8 @@
- - + +
diff --git a/src/app/domains/partner/modules/consumers/views/single.component.ts b/src/app/domains/partner/modules/consumers/views/single.component.ts index 7800494..1ccfee0 100644 --- a/src/app/domains/partner/modules/consumers/views/single.component.ts +++ b/src/app/domains/partner/modules/consumers/views/single.component.ts @@ -1,11 +1,10 @@ import { BreadcrumbService } from '@/core/services'; import { AppCardComponent, KeyValueComponent } from '@/shared/components'; -import { getLicenseStatus } from '@/utils'; import { Component, computed, effect, inject, signal } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ConsumerAccountListComponent } from '../components/accounts/list.component'; import { ConsumerBusinessActivitiesComponent } from '../components/businessActivities/list.component'; -import { ConsumerUserFormComponent } from '../components/form.component'; +import { ConsumerUserFormDialogComponent } from '../components/form-dialog.component'; import { ConsumerStore } from '../store/consumer.store'; @Component({ @@ -14,7 +13,7 @@ import { ConsumerStore } from '../store/consumer.store'; imports: [ AppCardComponent, KeyValueComponent, - ConsumerUserFormComponent, + ConsumerUserFormDialogComponent, ConsumerAccountListComponent, ConsumerBusinessActivitiesComponent, ], @@ -31,10 +30,6 @@ export class ConsumerComponent { readonly loading = computed(() => this.store.loading()); readonly consumer = computed(() => this.store.entity()); - readonly license = computed(() => this.store.entity()?.license_info); - readonly licenseStatus = computed(() => - getLicenseStatus(this.store.entity()?.license_info?.expires_at), - ); constructor() { effect(() => { diff --git a/src/app/domains/partner/modules/licenses/components/list.component.html b/src/app/domains/partner/modules/licenses/components/list.component.html index 9e8b390..d279f77 100644 --- a/src/app/domains/partner/modules/licenses/components/list.component.html +++ b/src/app/domains/partner/modules/licenses/components/list.component.html @@ -4,7 +4,7 @@ emptyPlaceholderTitle="تا به حال لایسنسی نفروخته‌اید." [currentPage]="responseMetaData()?.page" [totalRecords]="responseMetaData()?.totalRecords || items().length" - [perPage]="1" + [perPage]="responseMetaData()?.perPage" [items]="items()" [loading]="loading()" (onRefresh)="refresh()" diff --git a/src/app/domains/partner/modules/licenses/components/list.component.ts b/src/app/domains/partner/modules/licenses/components/list.component.ts index 3bd0910..175a417 100644 --- a/src/app/domains/partner/modules/licenses/components/list.component.ts +++ b/src/app/domains/partner/modules/licenses/components/list.component.ts @@ -1,7 +1,7 @@ import { AbstractList } from '@/shared/abstractClasses/abstract-list'; import { PageDataListComponent } from '@/shared/components/pageDataList/page-data-list.component'; import { Component, inject } from '@angular/core'; -import { ILicenseRawResponse, ILicenseResponse } from '../models'; +import { ILicenseResponse } from '../models'; import { PartnerLicensesService } from '../services/main.service'; import { AdminAgentsFilterComponent } from './filter-form.component'; @@ -17,19 +17,25 @@ export class PartnerLicenseListComponent extends AbstractList this.columns = [ { field: 'id', header: 'شناسه', type: 'id' }, { - field: 'consumer', - header: 'مشتری', - customDataModel(item: ILicenseRawResponse) { - return `${item.consumer.first_name} ${item.consumer.last_name}`; + field: 'business_activity', + header: 'عنوان مشتری', + type: 'nested', + nestedOption: { + path: 'business_activity.consumer.name', }, }, { - field: 'accounts_limit', - header: 'تعداد کاربر قابل تعریف', - customDataModel(item: ILicenseRawResponse) { - return item.license.accounts_limit || '-'; + field: 'business_activity', + header: 'عنوان فعالیت اقتصادی', + type: 'nested', + nestedOption: { + path: 'business_activity.name', }, }, + { + field: 'account_allocation_count', + header: 'محدودیت کاربر', + }, { field: 'expires_at', header: 'تاریخ انقضا', type: 'date' }, { field: 'created_at', header: 'تاریخ ایجاد', type: 'date' }, ]; @@ -38,7 +44,7 @@ export class PartnerLicenseListComponent extends AbstractList override getDataRequest() { return this.service.getAll({ page: this.responseMetaData()?.page || 1, - pageSize: this.responseMetaData()?.perPage || 1, + pageSize: this.responseMetaData()?.perPage || 10, }); } } diff --git a/src/app/domains/partner/modules/licenses/models/io.d.ts b/src/app/domains/partner/modules/licenses/models/io.d.ts index 2b9af00..f624c5e 100644 --- a/src/app/domains/partner/modules/licenses/models/io.d.ts +++ b/src/app/domains/partner/modules/licenses/models/io.d.ts @@ -1,3 +1,5 @@ +import { IConsumerResponse } from '@/shared/models/consumer.type'; + export interface ILicenseRawResponse { id: string; starts_at: string; @@ -13,9 +15,4 @@ interface License { accounts_limit?: number; } -interface Consumer { - first_name: string; - last_name: string; - mobile_number: string; - status: string; -} +interface Consumer extends IConsumerResponse {} diff --git a/src/app/domains/partner/modules/licenses/services/main.service.ts b/src/app/domains/partner/modules/licenses/services/main.service.ts index 7f32b99..889e30f 100644 --- a/src/app/domains/partner/modules/licenses/services/main.service.ts +++ b/src/app/domains/partner/modules/licenses/services/main.service.ts @@ -15,7 +15,7 @@ export class PartnerLicensesService { return this.http.get>(this.apiRoutes.list(), { params: { page: paginationQuery?.page || '1', - pageSize: paginationQuery?.pageSize || '50', + perPage: paginationQuery?.pageSize || '50', }, }); } diff --git a/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.html b/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.html index 4515247..7cbab4c 100644 --- a/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.html +++ b/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.html @@ -1,4 +1,4 @@ - +
@@ -13,4 +13,4 @@ } @else if (selectedCustomerType() === "LEGAL") { } - + diff --git a/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.ts b/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.ts index 9c57a2a..437f353 100644 --- a/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.ts +++ b/src/app/domains/pos/modules/landing/components/customers/customer-dialog.component.ts @@ -2,18 +2,18 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { CustomerType } from '@/shared/localEnum/constants/customerTypes'; import { Component, EventEmitter, inject, Output, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { SelectButton } from 'primeng/selectbutton'; import { PosLandingStore } from '../../store/main.store'; import { CustomerIndividualFormComponent } from './individual/form.component'; import { CustomerLegalFormComponent } from './legal/form.component'; import { CustomerUnknownFormComponent } from './unknown/form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'pos-order-customer-dialog', templateUrl: './customer-dialog.component.html', imports: [ - Dialog, + SharedDialogComponent, CustomerIndividualFormComponent, FormsModule, CustomerLegalFormComponent, diff --git a/src/app/domains/pos/modules/landing/components/payload-form.component.html b/src/app/domains/pos/modules/landing/components/payload-form.component.html index f6bebc9..634398a 100644 --- a/src/app/domains/pos/modules/landing/components/payload-form.component.html +++ b/src/app/domains/pos/modules/landing/components/payload-form.component.html @@ -1,4 +1,4 @@ - } } - + diff --git a/src/app/domains/pos/modules/landing/components/payload-form.component.ts b/src/app/domains/pos/modules/landing/components/payload-form.component.ts index e62c685..b049040 100644 --- a/src/app/domains/pos/modules/landing/components/payload-form.component.ts +++ b/src/app/domains/pos/modules/landing/components/payload-form.component.ts @@ -1,16 +1,16 @@ import { IGoodResponse } from '@/domains/pos/models/good.io'; import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { Component, computed, EventEmitter, inject, Input, Output, signal } from '@angular/core'; -import { Dialog } from 'primeng/dialog'; import { IGoldPayload, IPosOrderItem, IStandardPayload, TPosOrderGoodPayload } from '../models'; import { PosLandingStore } from '../store/main.store'; import { PosGoldPayloadFormComponent } from './payloads/gold/form.component'; import { PosStandardPayloadFormComponent } from './payloads/standard/form.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'pos-payload-form-dialog', templateUrl: './payload-form.component.html', - imports: [PosGoldPayloadFormComponent, Dialog, PosStandardPayloadFormComponent], + imports: [PosGoldPayloadFormComponent, SharedDialogComponent, PosStandardPayloadFormComponent], }) export class PayloadFormDialogComponent extends AbstractDialog { @Input({ required: true }) good!: IGoodResponse; diff --git a/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html b/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html index 6c7afc4..3a267d8 100644 --- a/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html +++ b/src/app/domains/pos/modules/landing/components/payloads/gold/form.component.html @@ -1,7 +1,7 @@ - + - + diff --git a/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts b/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts index 56523dd..17acbe0 100644 --- a/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts +++ b/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts @@ -1,3 +1,4 @@ +import { NativeBridgeService } from '@/core/services'; import { ToastService } from '@/core/services/toast.service'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { InputComponent, KeyValueComponent } from '@/shared/components'; @@ -5,10 +6,10 @@ import { FormFooterActionsComponent } from '@/shared/components/formFooterAction import { Component, computed, inject, signal } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; import { ButtonDirective } from 'primeng/button'; -import { Dialog } from 'primeng/dialog'; import { catchError, throwError } from 'rxjs'; import { IPayment, TOrderPaymentTypes } from '../../models'; import { PosLandingStore } from '../../store/main.store'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'pos-payment-form-dialog', @@ -19,11 +20,12 @@ import { PosLandingStore } from '../../store/main.store'; FormFooterActionsComponent, KeyValueComponent, ButtonDirective, - Dialog, + SharedDialogComponent, ], }) export class PosPaymentFormDialogComponent extends AbstractFormDialog { private readonly store = inject(PosLandingStore); + private readonly nativeBridge = inject(NativeBridgeService); private readonly toastServices = inject(ToastService); readonly totalAmount = computed(() => this.store.orderPricingInfo().totalAmount); @@ -100,6 +102,21 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog 0 && this.nativeBridge.isEnabled()) { + const payResult = this.nativeBridge.pay({ + amount: payment.terminal, + totalAmount: this.totalAmount(), + invoiceDate: new Date().toISOString(), + }); + + if (!payResult.success) { + return this.toastServices.warn({ + text: payResult.error || 'پرداخت با دستگاه انجام نشد. لطفا دوباره تلاش کنید.', + }); + } + } + this.store.setPayment(payment); this.store @@ -109,7 +126,13 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog err); }), ) - .subscribe(() => { + .subscribe((res) => { + if (this.nativeBridge.isEnabled()) { + this.nativeBridge.print({ + invoiceId: res?.id, + code: res?.code, + }); + } this.close(); this.toastServices.success({ text: 'فاکتور شما با موفقیت ایجاد شد', diff --git a/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.html b/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.html index 0486e44..2292f7f 100644 --- a/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.html +++ b/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.html @@ -1,4 +1,4 @@ -
-
+ diff --git a/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.ts b/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.ts index fb47fbf..830d60f 100644 --- a/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.ts +++ b/src/app/domains/pos/modules/landing/components/pre-invoice-dialog.component.ts @@ -2,13 +2,13 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { Component, inject } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { PosLandingStore } from '../store/main.store'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'pos-pre-invoice-dialog', templateUrl: './pre-invoice-dialog.component.html', - imports: [Dialog, UikitFlatpickrJalaliComponent, ReactiveFormsModule], + imports: [SharedDialogComponent, UikitFlatpickrJalaliComponent, ReactiveFormsModule], }) export class PosPreInvoiceDialogComponent extends AbstractDialog { private readonly store = inject(PosLandingStore); diff --git a/src/app/domains/pos/modules/landing/models/io.d.ts b/src/app/domains/pos/modules/landing/models/io.d.ts index a780ca3..469e5d7 100644 --- a/src/app/domains/pos/modules/landing/models/io.d.ts +++ b/src/app/domains/pos/modules/landing/models/io.d.ts @@ -14,5 +14,8 @@ export interface IPosOrderRequest { customer?: ICustomer; } -export interface IPosOrderRawResponse {} +export interface IPosOrderRawResponse { + id?: string; + code?: string; +} export interface IPosOrderResponse extends IPosOrderRawResponse {} diff --git a/src/app/domains/pos/modules/landing/store/main.store.ts b/src/app/domains/pos/modules/landing/store/main.store.ts index b9c997f..bb2b967 100644 --- a/src/app/domains/pos/modules/landing/store/main.store.ts +++ b/src/app/domains/pos/modules/landing/store/main.store.ts @@ -4,7 +4,7 @@ import { IGoodCategoryResponse, IGoodResponse } from '@/domains/pos/models/good. import { CustomerType } from '@/shared/localEnum/constants/customerTypes'; import { createUUID, JALALI_DATE_FORMATS, nowJalali, parseJalali } from '@/utils'; import { computed, inject, Injectable, signal } from '@angular/core'; -import { catchError, finalize, map, Observable } from 'rxjs'; +import { catchError, finalize, map, throwError } from 'rxjs'; import { ICustomer, IPayment, IPosOrderRequest, IPosOrderResponse } from '../models'; import { IPosInOrderGood, IPosOrderItem } from '../models/types'; import { PosService } from '../services/main.service'; @@ -228,9 +228,7 @@ export class PosLandingStore { finalize(() => { this.setState({ submitOrderLoading: false }); }), - catchError(() => { - return new Observable((observer) => observer.error()); - }), + catchError((error) => throwError(() => error)), map((_res) => { if (_res) { diff --git a/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.html b/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.html index 4c9c65b..3a4b946 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.html @@ -1,4 +1,4 @@ -
- + -
+ diff --git a/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.ts b/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.ts index fc2ce5d..a64b792 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/components/accounts/form.component.ts @@ -2,21 +2,22 @@ import { MustMatch, password } from '@/core/validators'; // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { SharedPasswordInputComponent, UsernameComponent } from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IConsumerAccountRequest, IConsumerAccountResponse } from '../../models'; import { AdminConsumerAccountsService } from '../../services/accounts.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-consumer-account-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + UsernameComponent, FormFooterActionsComponent, SharedPasswordInputComponent, EnumSelectComponent, @@ -35,7 +36,7 @@ export class ConsumerAccountFormComponent extends AbstractFormDialog< if (this.editMode) { return this.fb.group( { - username: [this.initialValues?.username || '', [Validators.required]], + username: fieldControl.username(this.initialValues?.account?.username || ''), password: ['', [password()]], confirmPassword: [''], }, @@ -46,7 +47,7 @@ export class ConsumerAccountFormComponent extends AbstractFormDialog< } return this.fb.group( { - username: [this.initialValues?.username || '', [Validators.required]], + username: fieldControl.username(this.initialValues?.account?.username || ''), role: ['', [Validators.required]], password: ['', [Validators.required, password()]], confirmPassword: ['', [Validators.required]], diff --git a/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.html b/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.html index cb0d0e4..0059d5d 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.html @@ -1,4 +1,4 @@ -
- - + + + + + -
+ diff --git a/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.ts b/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.ts index 800ffb1..31ab4d6 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/components/businessActivities/form.component.ts @@ -1,23 +1,32 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { CatalogGuildSelectComponent } from '@/shared/catalog/guild/components/select.component'; -import { InputComponent } from '@/shared/components'; +import { + EconomicCodeComponent, + FiscalCodeComponent, + GuildIdComponent, + NameComponent, + PartnerTokenComponent, +} from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; +import { fieldControl } from '@/shared/constants/fields'; import { Component, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; +import { ReactiveFormsModule } from '@angular/forms'; import { IBusinessActivityRequest, IBusinessActivityResponse } from '../../models'; import { AdminConsumerBusinessActivitiesService } from '../../services/businessActivities.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-consumer-businessActivities-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + NameComponent, + EconomicCodeComponent, + FiscalCodeComponent, + PartnerTokenComponent, + GuildIdComponent, FormFooterActionsComponent, - CatalogGuildSelectComponent, ], }) export class ConsumerBusinessActivitiesFormComponent extends AbstractFormDialog< @@ -31,9 +40,12 @@ export class ConsumerBusinessActivitiesFormComponent extends AbstractFormDialog< initForm = () => { return this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + economic_code: fieldControl.economic_code(this.initialValues?.economic_code || ''), + fiscal_code: fieldControl.fiscal_code(this.initialValues?.fiscal_code || ''), + partner_token: fieldControl.partner_token(this.initialValues?.partner_token || ''), // @ts-ignore - guild_id: [this.initialValues?.guild?.id || '', [Validators.required]], + guild_id: fieldControl.guild_id(this.initialValues?.guild?.id || ''), }); }; diff --git a/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.html b/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.html index 248f2ba..1af5372 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.html @@ -1,4 +1,4 @@ -
- - - + + + -
+ diff --git a/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.ts b/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.ts index f6e8c72..42c11be 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/components/complexes/form.component.ts @@ -1,17 +1,29 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { InputComponent } from '@/shared/components'; +import { + NameComponent, + AddressComponent, + BranchCodeComponent, +} from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; -import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; +import { ReactiveFormsModule } from '@angular/forms'; import { IComplexRequest, IComplexResponse } from '../../models'; import { AdminComplexesService } from '../../services/complexes.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-consumer-complex-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ + ReactiveFormsModule, + SharedDialogComponent, + NameComponent, + BranchCodeComponent, + AddressComponent, + FormFooterActionsComponent, + ], }) export class ConsumerComplexFormComponent extends AbstractFormDialog< IComplexRequest, @@ -25,9 +37,9 @@ export class ConsumerComplexFormComponent extends AbstractFormDialog< initForm = () => { return this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - branch_code: [this.initialValues?.branch_code || ''], - address: [this.initialValues?.address || '', [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + branch_code: fieldControl.branch_code(this.initialValues?.branch_code || ''), + address: fieldControl.address(this.initialValues?.address || ''), }); }; diff --git a/src/app/domains/superAdmin/modules/consumers/components/form.component.html b/src/app/domains/superAdmin/modules/consumers/components/form.component.html index db9e098..5ee5fee 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/form.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/form.component.html @@ -1,4 +1,4 @@ -
- + @if (!editMode) { اطلاعات ورود - + اطلاعات لایسنس - + /> --> - + } -
+ diff --git a/src/app/domains/superAdmin/modules/consumers/components/form.component.ts b/src/app/domains/superAdmin/modules/consumers/components/form.component.ts index 6a4aa3e..12f078f 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/components/form.component.ts @@ -1,14 +1,13 @@ -import { mobileValidator, MustMatch } from '@/core/validators'; +import { MustMatch } from '@/core/validators'; import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { SharedPasswordInputComponent, UsernameComponent } from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; -import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { Component, computed, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { Divider } from 'primeng/divider'; -import { PartnerSelectComponent } from '../../partners/shared/select.component'; -import { IConsumerRequest, IConsumerResponse } from '../models'; +import { IAdminConsumerRequest, IAdminConsumerResponse } from '../models'; import { ConsumersService } from '../services/main.service'; @Component({ @@ -16,18 +15,16 @@ import { ConsumersService } from '../services/main.service'; templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + UsernameComponent, FormFooterActionsComponent, Divider, - UikitFlatpickrJalaliComponent, - PartnerSelectComponent, SharedPasswordInputComponent, ], }) export class ConsumerUserFormComponent extends AbstractFormDialog< - IConsumerRequest, - IConsumerResponse + IAdminConsumerRequest, + IAdminConsumerResponse > { @Input() consumerId?: string; private service = inject(ConsumersService); @@ -36,19 +33,19 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< initForm = () => { const form = this.fb.group({ - first_name: [this.initialValues?.first_name || '', [Validators.required]], - last_name: [this.initialValues?.last_name || '', [Validators.required]], - mobile_number: [ - this.initialValues?.mobile_number || '', - [Validators.required, mobileValidator()], - ], + // first_name: [this.initialValues?.first_name || '', [Validators.required]], + // last_name: [this.initialValues?.last_name || '', [Validators.required]], + // mobile_number: [ + // this.initialValues?.mobile_number || '', + // [Validators.required, mobileValidator()], + // ], username: [''], password: [''], confirmPassword: [''], - license: this.fb.group({ - starts_at: [this.initialValues?.license_info?.starts_at || ''], - partner_id: [this.initialValues?.license_info?.partner?.id || ''], - }), + // license: this.fb.group({ + // starts_at: [this.initialValues?.license_info?.starts_at || ''], + // partner_id: [this.initialValues?.license_info?.partner?.id || ''], + // }), }); if (this.editMode) { @@ -60,13 +57,15 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< form.removeControl('confirmPassword'); form.removeValidators([MustMatch('password', 'confirmPassword')]); } else { + // @ts-ignore form.addControl( 'username', this.fb.control('', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.username('')[1], }), ); + // @ts-ignore form.addControl( 'password', this.fb.control('', { @@ -74,6 +73,7 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< validators: [Validators.required], }), ); + // @ts-ignore form.addControl( 'confirmPassword', this.fb.control('', { @@ -90,13 +90,14 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< form = this.initForm(); override ngOnChanges() { - this.form.patchValue({ - ...(this.initialValues ?? {}), - license: { - ...(this.initialValues?.license_info ?? {}), - partner_id: this.initialValues?.license_info?.partner?.id || '', - }, - }); + // this.form.patchValue(this.initialValues ?? {}); + // this.form.patchValue({ + // ...(this.initialValues ?? {}), + // license: { + // ...(this.initialValues?.license_info ?? {}), + // partner_id: this.initialValues?.license_info?.partner?.id || '', + // }, + // }); if (this.editMode && !this.consumerId) { throw 'missing some arguments'; } @@ -104,7 +105,7 @@ export class ConsumerUserFormComponent extends AbstractFormDialog< this.form = this.initForm(); } - override submitForm(payload: IConsumerRequest) { + override submitForm(payload: IAdminConsumerRequest) { if (this.editMode) { return this.service.update(this.consumerId!, payload); } diff --git a/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.html b/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.html index 449bac2..0416858 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.ts b/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.ts index 0e73ff3..c457ce8 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/components/licenses/form.component.ts @@ -3,17 +3,17 @@ import { FormFooterActionsComponent } from '@/shared/components/formFooterAction import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { Component, computed, inject, Input } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { PartnerSelectComponent } from '../../../partners/shared/select.component'; import { ILicenseRequest, ILicenseResponse } from '../../models'; import { LicensesService } from '../../services/licenses.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-consumer-license-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, + SharedDialogComponent, FormFooterActionsComponent, UikitFlatpickrJalaliComponent, PartnerSelectComponent, diff --git a/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.html b/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.html index 2a8e022..1201ba8 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.html +++ b/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.html @@ -1,4 +1,4 @@ -
- - + + - + @if (form.controls.pos_type.value === "PSP") { - - + + } -
+ diff --git a/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.ts b/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.ts index de4ed4f..9ebc490 100644 --- a/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/components/poses/form.component.ts @@ -1,27 +1,32 @@ // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; -import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; -import { CatalogProviderSelectComponent } from '@/shared/catalog'; -import { CatalogDeviceSelectComponent } from '@/shared/catalog/devices'; -import { InputComponent } from '@/shared/components'; +import { + NameComponent, + DeviceIdComponent, + ProviderIdComponent, + SerialNumberComponent, + PosTypeComponent, +} from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IPosRequest, IPosResponse } from '../../models'; import { AdminPosesService } from '../../services/poses.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-consumer-pos-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + NameComponent, FormFooterActionsComponent, - CatalogDeviceSelectComponent, - CatalogProviderSelectComponent, - EnumSelectComponent, + DeviceIdComponent, + ProviderIdComponent, + PosTypeComponent, + SerialNumberComponent, ], }) export class ConsumerPosFormComponent extends AbstractFormDialog { @@ -34,27 +39,29 @@ export class ConsumerPosFormComponent extends AbstractFormDialog { const form = this.fb.group({ - name: [this.initialValues?.name || '', [Validators.required]], - serial_number: [this.initialValues?.serial_number || '', [Validators.required]], + name: fieldControl.name(this.initialValues?.name || ''), + serial_number: fieldControl.serial_number(this.initialValues?.serial_number || '', true), // model: [this.initialValues?.model || '', [Validators.required]], - pos_type: [this.initialValues?.pos_type || '', [Validators.required]], - device_id: [this.initialValues?.device?.id || ''], - provider_id: [this.initialValues?.provider?.id || ''], + pos_type: fieldControl.pos_type(this.initialValues?.pos_type || ''), + device_id: fieldControl.device_id(this.initialValues?.device?.id || ''), + provider_id: fieldControl.provider_id(this.initialValues?.provider?.id || ''), }); form.controls.pos_type.valueChanges.subscribe((value) => { if (value === 'PSP') { + // @ts-ignore form.addControl( 'device_id', this.fb.control(this.initialValues?.device?.id ?? '', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.device_id('', true)[1], }), ); + // @ts-ignore form.addControl( 'provider_id', this.fb.control(this.initialValues?.provider?.id ?? '', { nonNullable: true, - validators: [Validators.required], + validators: fieldControl.provider_id('', true)[1], }), ); } else { diff --git a/src/app/domains/superAdmin/modules/consumers/models/accounts_io.d.ts b/src/app/domains/superAdmin/modules/consumers/models/accounts_io.d.ts index f95fd73..fca6d27 100644 --- a/src/app/domains/superAdmin/modules/consumers/models/accounts_io.d.ts +++ b/src/app/domains/superAdmin/modules/consumers/models/accounts_io.d.ts @@ -1,13 +1,24 @@ import { TAccountType } from '@/core/constants/accountTypes.const'; export interface IConsumerAccountRawResponse { - username: string; id: string; + role: string; + created_at: string; + account: AccountInfo; } export interface IConsumerAccountResponse extends IConsumerAccountRawResponse {} export interface IConsumerAccountRequest { username: string; password?: string; - type: TAccountType; + // Backend accepts role on create and status on update. + role?: string; + status?: string; + // Keep type optional for current form compatibility. + type?: TAccountType; +} + +interface AccountInfo { + username: string; + status: string; } diff --git a/src/app/domains/superAdmin/modules/consumers/models/businessActivities_io.d.ts b/src/app/domains/superAdmin/modules/consumers/models/businessActivities_io.d.ts index a97a076..43fbd86 100644 --- a/src/app/domains/superAdmin/modules/consumers/models/businessActivities_io.d.ts +++ b/src/app/domains/superAdmin/modules/consumers/models/businessActivities_io.d.ts @@ -2,6 +2,9 @@ import ISummary from '@/core/models/summary'; export interface IBusinessActivityRawResponse { name: string; + economic_code: string; + fiscal_code: string; + partner_token: string; guild: ISummary; id: string; } @@ -9,5 +12,8 @@ export interface IBusinessActivityResponse extends IBusinessActivityRawResponse export interface IBusinessActivityRequest { name: string; + economic_code: string; + fiscal_code: string; + partner_token: string; guild_id: string; } diff --git a/src/app/domains/superAdmin/modules/consumers/models/io.d.ts b/src/app/domains/superAdmin/modules/consumers/models/io.d.ts index 68a2be2..ced95e6 100644 --- a/src/app/domains/superAdmin/modules/consumers/models/io.d.ts +++ b/src/app/domains/superAdmin/modules/consumers/models/io.d.ts @@ -1,28 +1,8 @@ -import ISummary from '@/core/models/summary'; +import { IConsumerRequest, IConsumerResponse } from '@/shared/models/consumer.type'; -export interface IConsumerRawResponse { - id: string; - first_name: string; - last_name: string; - mobile_number: string; - status: string; - created_at: string; - fullname: string; - license_info: ILicenseInfo; -} -export interface IConsumerResponse extends IConsumerRawResponse {} +export interface IAdminConsumerRawResponse extends IConsumerResponse {} +export interface IAdminConsumerResponse extends IAdminConsumerRawResponse {} -export interface IConsumerRequest { - first_name: string; - last_name: string; - mobile_number: string; - username: string; - password: string; -} - -interface ILicenseInfo { - id: string; - starts_at: string; - expires_at: string; - partner: ISummary; +export interface IAdminConsumerRequest extends IConsumerRequest { + partner_id: string; } diff --git a/src/app/domains/superAdmin/modules/consumers/services/main.service.ts b/src/app/domains/superAdmin/modules/consumers/services/main.service.ts index 8fe9cd6..572f30b 100644 --- a/src/app/domains/superAdmin/modules/consumers/services/main.service.ts +++ b/src/app/domains/superAdmin/modules/consumers/services/main.service.ts @@ -3,7 +3,11 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { CONSUMERS_API_ROUTES } from '../constants'; -import { IConsumerRawResponse, IConsumerRequest, IConsumerResponse } from '../models'; +import { + IAdminConsumerRawResponse, + IAdminConsumerRequest, + IAdminConsumerResponse, +} from '../models'; @Injectable({ providedIn: 'root' }) export class ConsumersService { @@ -11,18 +15,18 @@ export class ConsumersService { private apiRoutes = CONSUMERS_API_ROUTES; - getAll(): Observable> { - return this.http.get>(this.apiRoutes.list()); + getAll(): Observable> { + return this.http.get>(this.apiRoutes.list()); } - getSingle(consumerId: string): Observable { - return this.http.get(this.apiRoutes.single(consumerId)); + getSingle(consumerId: string): Observable { + return this.http.get(this.apiRoutes.single(consumerId)); } - create(userData: IConsumerRequest): Observable { - return this.http.post(this.apiRoutes.list(), userData); + create(userData: IAdminConsumerRequest): Observable { + return this.http.post(this.apiRoutes.list(), userData); } - update(consumerId: string, userData: IConsumerRequest): Observable { - return this.http.patch(this.apiRoutes.single(consumerId), userData); + update(consumerId: string, userData: IAdminConsumerRequest): Observable { + return this.http.patch(this.apiRoutes.single(consumerId), userData); } } diff --git a/src/app/domains/superAdmin/modules/consumers/store/consumer.store.ts b/src/app/domains/superAdmin/modules/consumers/store/consumer.store.ts index f65d9a7..cd89396 100644 --- a/src/app/domains/superAdmin/modules/consumers/store/consumer.store.ts +++ b/src/app/domains/superAdmin/modules/consumers/store/consumer.store.ts @@ -3,17 +3,17 @@ import { computed, inject, Injectable } from '@angular/core'; import { MenuItem } from 'primeng/api'; import { catchError, finalize } from 'rxjs'; import { superAdminConsumersNamedRoutes } from '../constants'; -import { IConsumerResponse } from '../models'; +import { IAdminConsumerResponse } from '../models'; import { ConsumersService } from '../services/main.service'; -interface ConsumerState extends EntityState { +interface ConsumerState extends EntityState { breadcrumbItems: MenuItem[]; } @Injectable({ providedIn: 'root', }) -export class ConsumerStore extends EntityStore { +export class ConsumerStore extends EntityStore { private readonly service = inject(ConsumersService); constructor() { super({ @@ -36,7 +36,7 @@ export class ConsumerStore extends EntityStore routerLink: superAdminConsumersNamedRoutes.consumers.meta.pagePath!(), }, { - title: this.entity()?.fullname, + title: this.entity()?.name, routerLink: superAdminConsumersNamedRoutes.consumer.meta.pagePath!(consumerId), }, ], diff --git a/src/app/domains/superAdmin/modules/consumers/views/list.component.html b/src/app/domains/superAdmin/modules/consumers/views/list.component.html index 222d407..56d43f6 100644 --- a/src/app/domains/superAdmin/modules/consumers/views/list.component.html +++ b/src/app/domains/superAdmin/modules/consumers/views/list.component.html @@ -1,14 +1,10 @@ { +export class ConsumersComponent extends AbstractList { private readonly service = inject(ConsumersService); private readonly router = inject(Router); override setColumns(): void { this.columns = [ { field: 'id', header: 'شناسه', type: 'id' }, - { field: 'fullname', header: 'نام' }, - { field: 'mobile_number', header: 'شماره موبایل' }, - { field: 'status', header: 'وضعیت' }, + { field: 'name', header: 'نام' }, + { field: 'business_counts', header: 'تعداد فعالیت‌های اقتصادی' }, { - field: 'license_expires_at', - header: 'تاریخ انقضای لایسنس', - customDataModel(item) { - if (item.license_info) { - return formatJalali(item.license_info.expires_at); - } - return 'بدون لایسنس'; - }, + field: 'partner', + header: 'ایجاد شده توسط', + type: 'nested', + nestedOption: { path: 'partner.name' }, }, + { field: 'status', header: 'وضعیت' }, + // { + // field: 'license_expires_at', + // header: 'تاریخ انقضای لایسنس', + // customDataModel(item) { + // if (item.license_info) { + // return formatJalali(item.license_info.expires_at); + // } + // return 'بدون لایسنس'; + // }, + // }, { field: 'created_at', header: 'تاریخ ایجاد', @@ -46,7 +51,7 @@ export class ConsumersComponent extends AbstractList { return this.service.getAll(); } - toSinglePage(consumer: IConsumerResponse) { + toSinglePage(consumer: IAdminConsumerResponse) { this.router.navigateByUrl(superAdminConsumersNamedRoutes.consumer.meta.pagePath!(consumer.id)); } } diff --git a/src/app/domains/superAdmin/modules/consumers/views/single.component.html b/src/app/domains/superAdmin/modules/consumers/views/single.component.html index 6f77378..e94e1a4 100644 --- a/src/app/domains/superAdmin/modules/consumers/views/single.component.html +++ b/src/app/domains/superAdmin/modules/consumers/views/single.component.html @@ -3,28 +3,9 @@
- - + + - @if (licenseStatus() === "EXPIRED") { - - - منقضی شده در - - - - } @else if (licenseStatus() === "ACTIVE") { - - - تا تاریخ - - - - } @else { - - ارایه نشده - - }
@@ -39,13 +20,4 @@ [initialValues]="consumer() || undefined" (onSubmit)="getData()" /> - -
diff --git a/src/app/domains/superAdmin/modules/consumers/views/single.component.ts b/src/app/domains/superAdmin/modules/consumers/views/single.component.ts index 2760d17..5be204a 100644 --- a/src/app/domains/superAdmin/modules/consumers/views/single.component.ts +++ b/src/app/domains/superAdmin/modules/consumers/views/single.component.ts @@ -1,14 +1,10 @@ import { BreadcrumbService } from '@/core/services'; import { AppCardComponent, KeyValueComponent } from '@/shared/components'; -import { JalaliDateDirective } from '@/shared/directives'; -import { getLicenseStatus } from '@/utils'; import { Component, computed, effect, inject, signal } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Button } from 'primeng/button'; import { ConsumerAccountListComponent } from '../components/accounts/list.component'; import { ConsumerBusinessActivitiesComponent } from '../components/businessActivities/list.component'; import { ConsumerUserFormComponent } from '../components/form.component'; -import { ConsumerLicenseFormComponent } from '../components/licenses/form.component'; import { ConsumerStore } from '../store/consumer.store'; @Component({ @@ -20,9 +16,6 @@ import { ConsumerStore } from '../store/consumer.store'; ConsumerUserFormComponent, ConsumerAccountListComponent, ConsumerBusinessActivitiesComponent, - Button, - JalaliDateDirective, - ConsumerLicenseFormComponent, ], }) export class ConsumerComponent { @@ -37,10 +30,6 @@ export class ConsumerComponent { readonly loading = computed(() => this.store.loading()); readonly consumer = computed(() => this.store.entity()); - readonly license = computed(() => this.store.entity()?.license_info); - readonly licenseStatus = computed(() => - getLicenseStatus(this.store.entity()?.license_info?.expires_at), - ); constructor() { effect(() => { diff --git a/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.html b/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.html index ad9ed30..fb5f139 100644 --- a/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.html +++ b/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.html @@ -1,4 +1,4 @@ - - +
diff --git a/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.ts b/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.ts index 0663180..9aff03d 100644 --- a/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/deviceBrands/components/form.component.ts @@ -3,14 +3,14 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, computed, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IDeviceBrandRequest, IDeviceBrandResponse } from '../models'; import { DeviceBrandsService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'deviceBrand-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent], }) export class DeviceBrandFormComponent extends AbstractFormDialog< IDeviceBrandRequest, diff --git a/src/app/domains/superAdmin/modules/devices/components/form.component.html b/src/app/domains/superAdmin/modules/devices/components/form.component.html index 15586bd..6f31857 100644 --- a/src/app/domains/superAdmin/modules/devices/components/form.component.html +++ b/src/app/domains/superAdmin/modules/devices/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/devices/components/form.component.ts b/src/app/domains/superAdmin/modules/devices/components/form.component.ts index 5fa4c80..548e8c3 100644 --- a/src/app/domains/superAdmin/modules/devices/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/devices/components/form.component.ts @@ -5,16 +5,16 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IDeviceRequest, IDeviceResponse } from '../models'; import { SuperAdminDeviceService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-device-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, + SharedDialogComponent, InputComponent, FormFooterActionsComponent, CatalogDeviceBrandSelectComponent, diff --git a/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.html b/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.html index 68dfbcb..217b091 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.html +++ b/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.html @@ -1,7 +1,7 @@ - +
-
+ diff --git a/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.ts b/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.ts index e7d925f..3204425 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.ts +++ b/src/app/domains/superAdmin/modules/guilds/components/categories/form.component.ts @@ -3,15 +3,15 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IGoodCategoriesResponse, IGoodCategoryRequest } from '../../models'; import { GuildGoodCategoriesService } from '../../services/goodCategories.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'admin-guild-good-categories-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent], }) export class GuildGoodCategoryFormComponent extends AbstractFormDialog< IGoodCategoryRequest, diff --git a/src/app/domains/superAdmin/modules/guilds/components/form.component.html b/src/app/domains/superAdmin/modules/guilds/components/form.component.html index b5743da..540c1fe 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/form.component.html +++ b/src/app/domains/superAdmin/modules/guilds/components/form.component.html @@ -1,8 +1,8 @@ - +
-
+ diff --git a/src/app/domains/superAdmin/modules/guilds/components/form.component.ts b/src/app/domains/superAdmin/modules/guilds/components/form.component.ts index 708d875..109b391 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/guilds/components/form.component.ts @@ -3,9 +3,9 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IGuildRequest, IGuildResponse } from '../models'; import { GuildsService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; interface T { name: string; @@ -15,7 +15,7 @@ interface T { @Component({ selector: 'admin-guild-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent], }) export class GuildFormComponent extends AbstractFormDialog { @Input() guildId?: string; diff --git a/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html b/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html index 5208e21..ffed12d 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html +++ b/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.html @@ -1,4 +1,10 @@ - +
@@ -9,10 +15,10 @@ [control]="form.controls.category_id" name="category_id" /> - - + + -
+ diff --git a/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.ts b/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.ts index 9f26c6c..f347130 100644 --- a/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.ts +++ b/src/app/domains/superAdmin/modules/guilds/components/goods/form.component.ts @@ -3,7 +3,6 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input, signal } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { Maybe } from '@/core'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; @@ -13,13 +12,14 @@ import { buildFormData } from '@/utils'; import { IGuildGoodRequest, IGuildGoodsResponse } from '../../models'; import { GuildGoodsService } from '../../services/goods.service'; import { GuildCategoriesSelectComponent } from '../categories/select.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'admin-guild-good-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, + SharedDialogComponent, InputComponent, FormFooterActionsComponent, EnumSelectComponent, diff --git a/src/app/domains/superAdmin/modules/licenses/components/form.component.html b/src/app/domains/superAdmin/modules/licenses/components/form.component.html index c79416b..dd6f0d4 100644 --- a/src/app/domains/superAdmin/modules/licenses/components/form.component.html +++ b/src/app/domains/superAdmin/modules/licenses/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/licenses/components/form.component.ts b/src/app/domains/superAdmin/modules/licenses/components/form.component.ts index 2afdae1..1e3f8a0 100644 --- a/src/app/domains/superAdmin/modules/licenses/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/licenses/components/form.component.ts @@ -3,14 +3,14 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { ILicenseRequest, ILicenseResponse } from '../models'; import { LicensesService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'license-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent], }) export class LicenseFormComponent extends AbstractFormDialog { private service = inject(LicensesService); diff --git a/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.html b/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.html index ba914bf..d550faa 100644 --- a/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.html +++ b/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.html @@ -1,4 +1,4 @@ -
- - + + -
+ diff --git a/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.ts b/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.ts index a2cc6d2..59506f7 100644 --- a/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.ts +++ b/src/app/domains/superAdmin/modules/partners/components/accounts/form.component.ts @@ -2,21 +2,22 @@ import { MustMatch, password } from '@/core/validators'; // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { SharedPasswordInputComponent, UsernameComponent } from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IPartnerAccountRequest, IPartnerAccountResponse } from '../../models'; import { AdminPartnerAccountsService } from '../../services/accounts.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-partner-account-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + UsernameComponent, FormFooterActionsComponent, SharedPasswordInputComponent, EnumSelectComponent, @@ -34,8 +35,8 @@ export class PartnerAccountFormComponent extends AbstractFormDialog< initForm = () => { return this.fb.group( { - username: [this.initialValues?.username || '', [Validators.required]], - type: ['', [Validators.required]], + username: fieldControl.username(this.initialValues?.account?.username || ''), + role: [this.initialValues?.role || '', [Validators.required]], password: ['', [Validators.required, password()]], confirmPassword: ['', [Validators.required]], }, diff --git a/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.html b/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.html index 2c43221..fdbca2a 100644 --- a/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.html +++ b/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.ts b/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.ts index 1e6ba68..bd1ae64 100644 --- a/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.ts +++ b/src/app/domains/superAdmin/modules/partners/components/charge-license-form-dialog.component.ts @@ -5,15 +5,15 @@ import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { nowJalali } from '@/utils'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IPartnerChargeLicenseTransactionRequest } from '../models/chargeLicenseTransactions_io'; import { AdminPartnerChargeLicenseTransactionsService } from '../services/chargeLicenseTransactions.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-charge-license-form-dialog', templateUrl: './charge-license-form-dialog.component.html', imports: [ - Dialog, + SharedDialogComponent, ReactiveFormsModule, InputComponent, FormFooterActionsComponent, diff --git a/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.html b/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.html index b08ad5d..c9b8d66 100644 --- a/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.html +++ b/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.ts b/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.ts index 975760b..88c0dd6 100644 --- a/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.ts +++ b/src/app/domains/superAdmin/modules/partners/components/chargeAccount/charge-account-form-dialog.component.ts @@ -5,15 +5,15 @@ import { UikitFlatpickrJalaliComponent } from '@/uikit'; import { nowJalali } from '@/utils'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IAdminPartnerChargeAccountRequest } from '../../models/chargeAccount_io'; import { AdminPartnerChargeAccountService } from '../../services/chargeAccount.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'admin-partner-charge-account-form-dialog', templateUrl: './charge-account-form-dialog.component.html', imports: [ - Dialog, + SharedDialogComponent, ReactiveFormsModule, InputComponent, FormFooterActionsComponent, diff --git a/src/app/domains/superAdmin/modules/partners/components/form.component.html b/src/app/domains/superAdmin/modules/partners/components/form.component.html index 0c18700..8aeeb86 100644 --- a/src/app/domains/superAdmin/modules/partners/components/form.component.html +++ b/src/app/domains/superAdmin/modules/partners/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/partners/components/form.component.ts b/src/app/domains/superAdmin/modules/partners/components/form.component.ts index e168cc7..f5809a9 100644 --- a/src/app/domains/superAdmin/modules/partners/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/partners/components/form.component.ts @@ -4,17 +4,17 @@ import { InputComponent, SharedPasswordInputComponent } from '@/shared/component import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, computed, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { Divider } from 'primeng/divider'; import { IPartnerRequest, IPartnerResponse } from '../models'; import { PartnersService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'partner-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, + SharedDialogComponent, InputComponent, FormFooterActionsComponent, Divider, diff --git a/src/app/domains/superAdmin/modules/partners/models/accounts_io.d.ts b/src/app/domains/superAdmin/modules/partners/models/accounts_io.d.ts index ecefbf1..1e80a4e 100644 --- a/src/app/domains/superAdmin/modules/partners/models/accounts_io.d.ts +++ b/src/app/domains/superAdmin/modules/partners/models/accounts_io.d.ts @@ -1,13 +1,23 @@ import { TAccountType } from '@/core/constants/accountTypes.const'; export interface IPartnerAccountRawResponse { - username: string; id: string; + role: string; + created_at: string; + account: AccountInfo; } export interface IPartnerAccountResponse extends IPartnerAccountRawResponse {} export interface IPartnerAccountRequest { username: string; password?: string; - type: TAccountType; + role?: string; + status?: string; + // Keep type optional for current form compatibility. + type?: TAccountType; +} + +interface AccountInfo { + username: string; + status: string; } diff --git a/src/app/domains/superAdmin/modules/partners/models/chargeAccount_io.d.ts b/src/app/domains/superAdmin/modules/partners/models/chargeAccount_io.d.ts index 521b72e..c16ec2f 100644 --- a/src/app/domains/superAdmin/modules/partners/models/chargeAccount_io.d.ts +++ b/src/app/domains/superAdmin/modules/partners/models/chargeAccount_io.d.ts @@ -4,7 +4,7 @@ export interface IAdminPartnerChargeAccountRawResponse { tracking_code: string; activation_expires_at: string; charged_license_count: number; - activated_license_count: number; + activation_count: number; remained_license_count: number; } diff --git a/src/app/domains/superAdmin/modules/partners/models/chargeLicenseTransactions_io.d.ts b/src/app/domains/superAdmin/modules/partners/models/chargeLicenseTransactions_io.d.ts index 5190373..3db82fa 100644 --- a/src/app/domains/superAdmin/modules/partners/models/chargeLicenseTransactions_io.d.ts +++ b/src/app/domains/superAdmin/modules/partners/models/chargeLicenseTransactions_io.d.ts @@ -4,7 +4,7 @@ export interface IPartnerChargeLicenseTransactionRawResponse { tracking_code: string; activation_expires_at: string; charged_license_count: number; - activated_license_count: number; + activation_count: number; remained_license_count: number; } export interface IPartnerChargeLicenseTransactionResponse extends IPartnerChargeLicenseTransactionRawResponse {} diff --git a/src/app/domains/superAdmin/modules/partners/services/chargeLicenseTransactions.service.ts b/src/app/domains/superAdmin/modules/partners/services/chargeLicenseTransactions.service.ts index e42de4c..8563b95 100644 --- a/src/app/domains/superAdmin/modules/partners/services/chargeLicenseTransactions.service.ts +++ b/src/app/domains/superAdmin/modules/partners/services/chargeLicenseTransactions.service.ts @@ -17,8 +17,8 @@ export class AdminPartnerChargeLicenseTransactionsService { getAll( partnerId: string, - ): Observable> { - return this.http.get>( + ): Observable> { + return this.http.get>( this.apiRoutes.list(partnerId), ); } @@ -26,8 +26,8 @@ export class AdminPartnerChargeLicenseTransactionsService { charge( partnerId: string, data: IPartnerChargeLicenseTransactionRequest, - ): Observable { - return this.http.post( + ): Observable { + return this.http.post( this.apiRoutes.list(partnerId), data, ); diff --git a/src/app/domains/superAdmin/modules/providers/components/form.component.html b/src/app/domains/superAdmin/modules/providers/components/form.component.html index 6963472..e31caa7 100644 --- a/src/app/domains/superAdmin/modules/providers/components/form.component.html +++ b/src/app/domains/superAdmin/modules/providers/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/providers/components/form.component.ts b/src/app/domains/superAdmin/modules/providers/components/form.component.ts index ea99c1b..c7d4a89 100644 --- a/src/app/domains/superAdmin/modules/providers/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/providers/components/form.component.ts @@ -4,17 +4,17 @@ import { InputComponent, SharedPasswordInputComponent } from '@/shared/component import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, computed, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { Divider } from 'primeng/divider'; import { IProviderRequest, IProviderResponse } from '../models'; import { ProvidersService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'provider-form', templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, + SharedDialogComponent, InputComponent, FormFooterActionsComponent, Divider, diff --git a/src/app/domains/superAdmin/modules/users/components/accounts/form.component.html b/src/app/domains/superAdmin/modules/users/components/accounts/form.component.html index ba914bf..92d16f8 100644 --- a/src/app/domains/superAdmin/modules/users/components/accounts/form.component.html +++ b/src/app/domains/superAdmin/modules/users/components/accounts/form.component.html @@ -1,4 +1,4 @@ -
- + -
+ diff --git a/src/app/domains/superAdmin/modules/users/components/accounts/form.component.ts b/src/app/domains/superAdmin/modules/users/components/accounts/form.component.ts index 6b2d1ac..c31d9ab 100644 --- a/src/app/domains/superAdmin/modules/users/components/accounts/form.component.ts +++ b/src/app/domains/superAdmin/modules/users/components/accounts/form.component.ts @@ -2,11 +2,12 @@ import { MustMatch, password } from '@/core/validators'; // import { CatalogRolesComponent } from '@/shared/catalog/roles'; import { AbstractFormDialog } from '@/shared/abstractClasses'; import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; -import { InputComponent, SharedPasswordInputComponent } from '@/shared/components'; +import { SharedPasswordInputComponent, UsernameComponent } from '@/shared/components'; +import { fieldControl } from '@/shared/constants/fields'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IAccountRequest, IAccountResponse } from '../../models'; import { AdminUserAccountsService } from '../../services/accounts.service'; @@ -15,8 +16,8 @@ import { AdminUserAccountsService } from '../../services/accounts.service'; templateUrl: './form.component.html', imports: [ ReactiveFormsModule, - Dialog, - InputComponent, + SharedDialogComponent, + UsernameComponent, FormFooterActionsComponent, EnumSelectComponent, SharedPasswordInputComponent, @@ -35,9 +36,8 @@ export class UserAccountFormComponent extends AbstractFormDialog< if (this.editMode) { return this.fb.group( { - username: [this.initialValues?.username || '', [Validators.required]], - // @ts-ignore - type: [this.initialValues?.type, [Validators.required]], + username: fieldControl.username(this.initialValues?.username || ''), + type: [this.initialValues?.type || '', [Validators.required]], password: ['', [password()]], confirmPassword: [''], @@ -49,9 +49,9 @@ export class UserAccountFormComponent extends AbstractFormDialog< } return this.fb.group( { - username: [this.initialValues?.username || '', [Validators.required]], + username: fieldControl.username(this.initialValues?.username || ''), // @ts-ignore - type: [this.initialValues?.type, [Validators.required]], + type: [this.initialValues?.type || '', [Validators.required]], password: ['', [Validators.required, password()]], confirmPassword: ['', [Validators.required]], diff --git a/src/app/domains/superAdmin/modules/users/components/form.component.html b/src/app/domains/superAdmin/modules/users/components/form.component.html index 3c42c1e..e8d6ae1 100644 --- a/src/app/domains/superAdmin/modules/users/components/form.component.html +++ b/src/app/domains/superAdmin/modules/users/components/form.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/domains/superAdmin/modules/users/components/form.component.ts b/src/app/domains/superAdmin/modules/users/components/form.component.ts index 1a2ee26..4d2527e 100644 --- a/src/app/domains/superAdmin/modules/users/components/form.component.ts +++ b/src/app/domains/superAdmin/modules/users/components/form.component.ts @@ -5,14 +5,14 @@ import { InputComponent } from '@/shared/components'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, inject, Input } from '@angular/core'; import { ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { IUserRequest, IUserResponse } from '../models'; import { UsersService } from '../services/main.service'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; @Component({ selector: 'superAdmin-user-form', templateUrl: './form.component.html', - imports: [ReactiveFormsModule, Dialog, InputComponent, FormFooterActionsComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, InputComponent, FormFooterActionsComponent], }) export class UserAccountFormComponent extends AbstractFormDialog { private readonly service = inject(UsersService); diff --git a/src/app/domains/superAdmin/modules/users/models/accounts_io.d.ts b/src/app/domains/superAdmin/modules/users/models/accounts_io.d.ts index 54bdb83..40c8e8a 100644 --- a/src/app/domains/superAdmin/modules/users/models/accounts_io.d.ts +++ b/src/app/domains/superAdmin/modules/users/models/accounts_io.d.ts @@ -1,13 +1,17 @@ import { TAccountType } from '@/core/constants/accountTypes.const'; export interface IAccountRawResponse { - username: string; id: string; + username?: string; + status?: string; + type?: string; + created_at?: string; } export interface IAccountResponse extends IAccountRawResponse {} export interface IAccountRequest { username: string; password?: string; - type: TAccountType; + type?: TAccountType; + status?: string; } diff --git a/src/app/pages/crud/crud.ts b/src/app/pages/crud/crud.ts index 0a14156..4195328 100644 --- a/src/app/pages/crud/crud.ts +++ b/src/app/pages/crud/crud.ts @@ -1,387 +1,488 @@ -import { Component, OnInit, signal, ViewChild } from '@angular/core'; -import { ConfirmationService, MessageService } from 'primeng/api'; -import { Table, TableModule } from 'primeng/table'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; import { CommonModule } from '@angular/common'; +import { Component, OnInit, signal, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { DialogModule } from 'primeng/dialog'; +import { IconFieldModule } from 'primeng/iconfield'; +import { InputIconModule } from 'primeng/inputicon'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { InputTextModule } from 'primeng/inputtext'; +import { RadioButtonModule } from 'primeng/radiobutton'; +import { RatingModule } from 'primeng/rating'; import { RippleModule } from 'primeng/ripple'; +import { SelectModule } from 'primeng/select'; +import { Table, TableModule } from 'primeng/table'; +import { TagModule } from 'primeng/tag'; +import { TextareaModule } from 'primeng/textarea'; import { ToastModule } from 'primeng/toast'; import { ToolbarModule } from 'primeng/toolbar'; -import { RatingModule } from 'primeng/rating'; -import { InputTextModule } from 'primeng/inputtext'; -import { TextareaModule } from 'primeng/textarea'; -import { SelectModule } from 'primeng/select'; -import { RadioButtonModule } from 'primeng/radiobutton'; -import { InputNumberModule } from 'primeng/inputnumber'; -import { DialogModule } from 'primeng/dialog'; -import { TagModule } from 'primeng/tag'; -import { InputIconModule } from 'primeng/inputicon'; -import { IconFieldModule } from 'primeng/iconfield'; -import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { Product, ProductService } from '../service/product.service'; interface Column { - field: string; - header: string; - customExportHeader?: string; + field: string; + header: string; + customExportHeader?: string; } interface ExportColumn { - title: string; - dataKey: string; + title: string; + dataKey: string; } @Component({ - selector: 'app-crud', - standalone: true, - imports: [ - CommonModule, - TableModule, - FormsModule, - ButtonModule, - RippleModule, - ToastModule, - ToolbarModule, - RatingModule, - InputTextModule, - TextareaModule, - SelectModule, - RadioButtonModule, - InputNumberModule, - DialogModule, - TagModule, - InputIconModule, - IconFieldModule, - ConfirmDialogModule - ], - template: ` - - - - - + selector: 'app-crud', + standalone: true, + imports: [ + CommonModule, + TableModule, + FormsModule, + ButtonModule, + RippleModule, + ToastModule, + ToolbarModule, + RatingModule, + InputTextModule, + TextareaModule, + SelectModule, + RadioButtonModule, + InputNumberModule, + DialogModule, + TagModule, + InputIconModule, + IconFieldModule, + ConfirmDialogModule, + SharedDialogComponent, + ], + template: ` + + + + + - - - - + + + + - - -
-
Manage Products
- - - - -
-
- - - - - - Code - - Name - - - Image - - Price - - - - Category - - - - Reviews - - - - Status - - - - - - - - - - - {{ product.code }} - {{ product.name }} - - - - {{ product.price | currency: 'USD' }} - {{ product.category }} - - - - - - - - - - - - -
+ + +
+
Manage Products
+ + + + +
+
+ + + + + + Code + + Name + + + Image + + Price + + + + Category + + + + Reviews + + + + Status + + + + + + + + + + + {{ product.code }} + {{ product.name }} + + + + {{ product.price | currency: 'USD' }} + {{ product.category }} + + + + + + + + + + + + +
- - -
- -
- - - Name is required. -
-
- - -
+ + +
+ +
+ + + Name is required. +
+
+ + +
-
- - -
+
+ + +
-
- Category -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
+
+ Category +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
-
-
- - -
-
- - -
-
-
-
+
+
+ + +
+
+ + +
+
+
+
- - - - -
+ + + + + - - `, - providers: [MessageService, ProductService, ConfirmationService] + + `, + providers: [MessageService, ProductService, ConfirmationService], }) export class Crud implements OnInit { - productDialog: boolean = false; + productDialog: boolean = false; - products = signal([]); + products = signal([]); - product!: Product; + product!: Product; - selectedProducts!: Product[] | null; + selectedProducts!: Product[] | null; - submitted: boolean = false; + submitted: boolean = false; - statuses!: any[]; + statuses!: any[]; - @ViewChild('dt') dt!: Table; + @ViewChild('dt') dt!: Table; - exportColumns!: ExportColumn[]; + exportColumns!: ExportColumn[]; - cols!: Column[]; + cols!: Column[]; - constructor( - private productService: ProductService, - private messageService: MessageService, - private confirmationService: ConfirmationService - ) {} + constructor( + private productService: ProductService, + private messageService: MessageService, + private confirmationService: ConfirmationService, + ) {} - exportCSV() { - this.dt.exportCSV(); - } + exportCSV() { + this.dt.exportCSV(); + } - ngOnInit() { - this.loadDemoData(); - } + ngOnInit() { + this.loadDemoData(); + } - loadDemoData() { - this.productService.getProducts().then((data) => { - this.products.set(data); + loadDemoData() { + this.productService.getProducts().then((data) => { + this.products.set(data); + }); + + this.statuses = [ + { label: 'INSTOCK', value: 'instock' }, + { label: 'LOWSTOCK', value: 'lowstock' }, + { label: 'OUTOFSTOCK', value: 'outofstock' }, + ]; + + this.cols = [ + { field: 'code', header: 'Code', customExportHeader: 'Product Code' }, + { field: 'name', header: 'Name' }, + { field: 'image', header: 'Image' }, + { field: 'price', header: 'Price' }, + { field: 'category', header: 'Category' }, + ]; + + this.exportColumns = this.cols.map((col) => ({ title: col.header, dataKey: col.field })); + } + + onGlobalFilter(table: Table, event: Event) { + table.filterGlobal((event.target as HTMLInputElement).value, 'contains'); + } + + openNew() { + this.product = {}; + this.submitted = false; + this.productDialog = true; + } + + editProduct(product: Product) { + this.product = { ...product }; + this.productDialog = true; + } + + deleteSelectedProducts() { + this.confirmationService.confirm({ + message: 'Are you sure you want to delete the selected products?', + header: 'Confirm', + icon: 'pi pi-exclamation-triangle', + accept: () => { + this.products.set(this.products().filter((val) => !this.selectedProducts?.includes(val))); + this.selectedProducts = null; + this.messageService.add({ + severity: 'success', + summary: 'Successful', + detail: 'Products Deleted', + life: 3000, }); + }, + }); + } - this.statuses = [ - { label: 'INSTOCK', value: 'instock' }, - { label: 'LOWSTOCK', value: 'lowstock' }, - { label: 'OUTOFSTOCK', value: 'outofstock' } - ]; + hideDialog() { + this.productDialog = false; + this.submitted = false; + } - this.cols = [ - { field: 'code', header: 'Code', customExportHeader: 'Product Code' }, - { field: 'name', header: 'Name' }, - { field: 'image', header: 'Image' }, - { field: 'price', header: 'Price' }, - { field: 'category', header: 'Category' } - ]; - - this.exportColumns = this.cols.map((col) => ({ title: col.header, dataKey: col.field })); - } - - onGlobalFilter(table: Table, event: Event) { - table.filterGlobal((event.target as HTMLInputElement).value, 'contains'); - } - - openNew() { + deleteProduct(product: Product) { + this.confirmationService.confirm({ + message: 'Are you sure you want to delete ' + product.name + '?', + header: 'Confirm', + icon: 'pi pi-exclamation-triangle', + accept: () => { + this.products.set(this.products().filter((val) => val.id !== product.id)); this.product = {}; - this.submitted = false; - this.productDialog = true; - } - - editProduct(product: Product) { - this.product = { ...product }; - this.productDialog = true; - } - - deleteSelectedProducts() { - this.confirmationService.confirm({ - message: 'Are you sure you want to delete the selected products?', - header: 'Confirm', - icon: 'pi pi-exclamation-triangle', - accept: () => { - this.products.set(this.products().filter((val) => !this.selectedProducts?.includes(val))); - this.selectedProducts = null; - this.messageService.add({ - severity: 'success', - summary: 'Successful', - detail: 'Products Deleted', - life: 3000 - }); - } + this.messageService.add({ + severity: 'success', + summary: 'Successful', + detail: 'Product Deleted', + life: 3000, }); + }, + }); + } + + findIndexById(id: string): number { + let index = -1; + for (let i = 0; i < this.products().length; i++) { + if (this.products()[i].id === id) { + index = i; + break; + } } - hideDialog() { - this.productDialog = false; - this.submitted = false; - } + return index; + } - deleteProduct(product: Product) { - this.confirmationService.confirm({ - message: 'Are you sure you want to delete ' + product.name + '?', - header: 'Confirm', - icon: 'pi pi-exclamation-triangle', - accept: () => { - this.products.set(this.products().filter((val) => val.id !== product.id)); - this.product = {}; - this.messageService.add({ - severity: 'success', - summary: 'Successful', - detail: 'Product Deleted', - life: 3000 - }); - } + createId(): string { + let id = ''; + var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (var i = 0; i < 5; i++) { + id += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return id; + } + + getSeverity(status: string) { + switch (status) { + case 'INSTOCK': + return 'success'; + case 'LOWSTOCK': + return 'warn'; + case 'OUTOFSTOCK': + return 'danger'; + default: + return 'info'; + } + } + + saveProduct() { + this.submitted = true; + let _products = this.products(); + if (this.product.name?.trim()) { + if (this.product.id) { + _products[this.findIndexById(this.product.id)] = this.product; + this.products.set([..._products]); + this.messageService.add({ + severity: 'success', + summary: 'Successful', + detail: 'Product Updated', + life: 3000, }); + } else { + this.product.id = this.createId(); + this.product.image = 'product-placeholder.svg'; + this.messageService.add({ + severity: 'success', + summary: 'Successful', + detail: 'Product Created', + life: 3000, + }); + this.products.set([..._products, this.product]); + } + + this.productDialog = false; + this.product = {}; } - - findIndexById(id: string): number { - let index = -1; - for (let i = 0; i < this.products().length; i++) { - if (this.products()[i].id === id) { - index = i; - break; - } - } - - return index; - } - - createId(): string { - let id = ''; - var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (var i = 0; i < 5; i++) { - id += chars.charAt(Math.floor(Math.random() * chars.length)); - } - return id; - } - - getSeverity(status: string) { - switch (status) { - case 'INSTOCK': - return 'success'; - case 'LOWSTOCK': - return 'warn'; - case 'OUTOFSTOCK': - return 'danger'; - default: - return 'info'; - } - } - - saveProduct() { - this.submitted = true; - let _products = this.products(); - if (this.product.name?.trim()) { - if (this.product.id) { - _products[this.findIndexById(this.product.id)] = this.product; - this.products.set([..._products]); - this.messageService.add({ - severity: 'success', - summary: 'Successful', - detail: 'Product Updated', - life: 3000 - }); - } else { - this.product.id = this.createId(); - this.product.image = 'product-placeholder.svg'; - this.messageService.add({ - severity: 'success', - summary: 'Successful', - detail: 'Product Created', - life: 3000 - }); - this.products.set([..._products, this.product]); - } - - this.productDialog = false; - this.product = {}; - } - } + } } diff --git a/src/app/pages/uikit/overlaydemo.ts b/src/app/pages/uikit/overlaydemo.ts index 7156449..6f7f154 100644 --- a/src/app/pages/uikit/overlaydemo.ts +++ b/src/app/pages/uikit/overlaydemo.ts @@ -1,230 +1,334 @@ +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; import { Component, OnInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { ConfirmationService, MessageService } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; -import { DialogModule } from 'primeng/dialog'; -import { ToastModule } from 'primeng/toast'; -import { DrawerModule } from 'primeng/drawer'; -import { Popover, PopoverModule } from 'primeng/popover'; import { ConfirmPopupModule } from 'primeng/confirmpopup'; +import { DialogModule } from 'primeng/dialog'; +import { DrawerModule } from 'primeng/drawer'; import { InputTextModule } from 'primeng/inputtext'; -import { FormsModule } from '@angular/forms'; -import { TooltipModule } from 'primeng/tooltip'; +import { Popover, PopoverModule } from 'primeng/popover'; import { TableModule } from 'primeng/table'; +import { ToastModule } from 'primeng/toast'; +import { TooltipModule } from 'primeng/tooltip'; import { Product, ProductService } from '../service/product.service'; @Component({ - selector: 'app-overlay-demo', - standalone: true, - imports: [ToastModule, DialogModule, ButtonModule, DrawerModule, PopoverModule, ConfirmPopupModule, InputTextModule, FormsModule, TooltipModule, TableModule, ToastModule], - template: `
-
-
-
Dialog
- -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. 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. -

- - - -
- -
+ selector: 'app-overlay-demo', + standalone: true, + imports: [ + ToastModule, + DialogModule, + ButtonModule, + DrawerModule, + PopoverModule, + ConfirmPopupModule, + InputTextModule, + FormsModule, + TooltipModule, + TableModule, + ToastModule, + SharedDialogComponent, + ], + template: `
+
+
+
SharedDialogComponent
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 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. +

+ + + +
+ +
-
-
Popover
-
- - - - - - Name - Image - Price - - - - - {{ product.name }} - - {{ product.price }} - - - - - -
-
- -
-
Tooltip
-
- - -
-
+
+
Popover
+
+ + + + + + Name + Image + Price + + + + + {{ product.name }} + + + + {{ product.price }} + + + + +
-
-
-
Drawer
- -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. -

-
+
- -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. -

-
- - -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. -

-
- - -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. -

-
- - -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo - consequat. -

-
- - - - - - -
- -
-
ConfirmPopup
- - -
- -
-
ConfirmDialog
- - -
- - Are you sure you want to proceed? -
- - - - -
-
+
+
Tooltip
+
+ +
-
`, - providers: [ConfirmationService, MessageService, ProductService] +
+
+
+
+
Drawer
+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +

+
+ + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +

+
+ + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +

+
+ + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +

+
+ + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +

+
+ + + + + + +
+ +
+
ConfirmPopup
+ + +
+ +
+
ConfirmDialog
+ + +
+ + Are you sure you want to proceed? +
+ + + + +
+
+
+
`, + providers: [ConfirmationService, MessageService, ProductService], }) export class OverlayDemo implements OnInit { - images: any[] = []; + images: any[] = []; - display: boolean = false; + display: boolean = false; - products: Product[] = []; + products: Product[] = []; - visibleLeft: boolean = false; + visibleLeft: boolean = false; - visibleRight: boolean = false; + visibleRight: boolean = false; - visibleTop: boolean = false; + visibleTop: boolean = false; - visibleBottom: boolean = false; + visibleBottom: boolean = false; - visibleFull: boolean = false; + visibleFull: boolean = false; - displayConfirmation: boolean = false; + displayConfirmation: boolean = false; - selectedProduct!: Product; + selectedProduct!: Product; - constructor( - private productService: ProductService, - private confirmationService: ConfirmationService, - private messageService: MessageService - ) {} + constructor( + private productService: ProductService, + private confirmationService: ConfirmationService, + private messageService: MessageService, + ) {} - ngOnInit() { - this.productService.getProductsSmall().then((products) => (this.products = products)); + ngOnInit() { + this.productService.getProductsSmall().then((products) => (this.products = products)); - this.images = []; - this.images.push({ - source: 'assets/demo/images/sopranos/sopranos1.jpg', - thumbnail: 'assets/demo/images/sopranos/sopranos1_small.jpg', - title: 'Sopranos 1' + this.images = []; + this.images.push({ + source: 'assets/demo/images/sopranos/sopranos1.jpg', + thumbnail: 'assets/demo/images/sopranos/sopranos1_small.jpg', + title: 'Sopranos 1', + }); + this.images.push({ + source: 'assets/demo/images/sopranos/sopranos2.jpg', + thumbnail: 'assets/demo/images/sopranos/sopranos2_small.jpg', + title: 'Sopranos 2', + }); + this.images.push({ + source: 'assets/demo/images/sopranos/sopranos3.jpg', + thumbnail: 'assets/demo/images/sopranos/sopranos3_small.jpg', + title: 'Sopranos 3', + }); + this.images.push({ + source: 'assets/demo/images/sopranos/sopranos4.jpg', + thumbnail: 'assets/demo/images/sopranos/sopranos4_small.jpg', + title: 'Sopranos 4', + }); + } + + confirm(event: Event) { + this.confirmationService.confirm({ + key: 'confirm2', + target: event.target || new EventTarget(), + message: 'Are you sure that you want to proceed?', + icon: 'pi pi-exclamation-triangle', + accept: () => { + this.messageService.add({ + severity: 'info', + summary: 'Confirmed', + detail: 'You have accepted', }); - this.images.push({ - source: 'assets/demo/images/sopranos/sopranos2.jpg', - thumbnail: 'assets/demo/images/sopranos/sopranos2_small.jpg', - title: 'Sopranos 2' + }, + reject: () => { + this.messageService.add({ + severity: 'error', + summary: 'Rejected', + detail: 'You have rejected', }); - this.images.push({ - source: 'assets/demo/images/sopranos/sopranos3.jpg', - thumbnail: 'assets/demo/images/sopranos/sopranos3_small.jpg', - title: 'Sopranos 3' - }); - this.images.push({ - source: 'assets/demo/images/sopranos/sopranos4.jpg', - thumbnail: 'assets/demo/images/sopranos/sopranos4_small.jpg', - title: 'Sopranos 4' - }); - } + }, + }); + } - confirm(event: Event) { - this.confirmationService.confirm({ - key: 'confirm2', - target: event.target || new EventTarget(), - message: 'Are you sure that you want to proceed?', - icon: 'pi pi-exclamation-triangle', - accept: () => { - this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'You have accepted' }); - }, - reject: () => { - this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected' }); - } - }); - } + open() { + this.display = true; + } - open() { - this.display = true; - } + close() { + this.display = false; + } - close() { - this.display = false; - } + toggleDataTable(op: Popover, event: any) { + op.toggle(event); + } - toggleDataTable(op: Popover, event: any) { - op.toggle(event); - } + onProductSelect(op: Popover, event: any) { + op.hide(); + this.messageService.add({ + severity: 'info', + summary: 'Product Selected', + detail: event?.data.name, + life: 3000, + }); + } - onProductSelect(op: Popover, event: any) { - op.hide(); - this.messageService.add({ severity: 'info', summary: 'Product Selected', detail: event?.data.name, life: 3000 }); - } + openConfirmation() { + this.displayConfirmation = true; + } - openConfirmation() { - this.displayConfirmation = true; - } - - closeConfirmation() { - this.displayConfirmation = false; - } + closeConfirmation() { + this.displayConfirmation = false; + } } diff --git a/src/app/shared/apiEnum/select.component.html b/src/app/shared/apiEnum/select.component.html index c2e543d..5c3c80b 100644 --- a/src/app/shared/apiEnum/select.component.html +++ b/src/app/shared/apiEnum/select.component.html @@ -7,6 +7,7 @@ [formControl]="control" [showClear]="showClear" [filter]="true" + [name]="name || type" appendTo="body" /> diff --git a/src/app/shared/apiEnum/select.component.ts b/src/app/shared/apiEnum/select.component.ts index bcb2a93..8cf1e70 100644 --- a/src/app/shared/apiEnum/select.component.ts +++ b/src/app/shared/apiEnum/select.component.ts @@ -15,6 +15,7 @@ import { EnumsService } from './services'; }) export class EnumSelectComponent extends AbstractSelectComponent { @Input({ required: true }) type!: TEnumApi; + @Input() name?: string; @Input() customLabel?: string; private readonly service = inject(EnumsService); diff --git a/src/app/shared/catalog/guild/components/select.component.html b/src/app/shared/catalog/guild/components/select.component.html index 31cf852..7acf987 100644 --- a/src/app/shared/catalog/guild/components/select.component.html +++ b/src/app/shared/catalog/guild/components/select.component.html @@ -8,6 +8,7 @@ [formControl]="control" [showClear]="showClear" [filter]="true" + [name]="name" appendTo="body" /> diff --git a/src/app/shared/catalog/guild/components/select.component.ts b/src/app/shared/catalog/guild/components/select.component.ts index b72d34f..83a28b5 100644 --- a/src/app/shared/catalog/guild/components/select.component.ts +++ b/src/app/shared/catalog/guild/components/select.component.ts @@ -17,6 +17,7 @@ export class CatalogGuildSelectComponent extends AbstractSelectComponent< IListingResponse > { @Input() override showClear: boolean = false; + @Input({ required: true }) name!: string; @Input() label: string = ''; private readonly service = inject(CatalogsService); diff --git a/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.html b/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.html index 02bfcb9..0e809b3 100644 --- a/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.html +++ b/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.html @@ -1,4 +1,4 @@ - - + diff --git a/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.ts b/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.ts index 431d090..5a2a7ac 100644 --- a/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.ts +++ b/src/app/shared/components/changePasswordFormDialog/change-password-form-dialog.component.ts @@ -3,8 +3,8 @@ import { AbstractDialog } from '@/shared/abstractClasses/abstract-dialog'; import { FormFooterActionsComponent } from '@/shared/components/formFooterActions/form-footer-actions.component'; import { Component, EventEmitter, Output, input } from '@angular/core'; import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; -import { Dialog } from 'primeng/dialog'; import { SharedPasswordInputComponent } from '../passwordInput/password-input.component'; +import { SharedDialogComponent } from '@/shared/components/dialog/dialog.component'; export interface IChangePasswordSubmitPayload { password: string; @@ -14,7 +14,7 @@ export interface IChangePasswordSubmitPayload { @Component({ selector: 'shared-change-password-form-dialog', templateUrl: './change-password-form-dialog.component.html', - imports: [ReactiveFormsModule, Dialog, FormFooterActionsComponent, SharedPasswordInputComponent], + imports: [ReactiveFormsModule, SharedDialogComponent, FormFooterActionsComponent, SharedPasswordInputComponent], }) export class ChangePasswordFormDialogComponent extends AbstractDialog { title = input(''); @@ -35,7 +35,7 @@ export class ChangePasswordFormDialogComponent extends AbstractDialog { ); get dialogHeader() { - return this.title() ? `تغییر گذرواژه‌ی ${this.title()}` : 'تغییر گذرواژه'; + return this.title() ? `تغییر گذرواژه ${this.title()}` : 'تغییر گذرواژه'; } submit() { diff --git a/src/app/shared/components/dialog/dialog.component.ts b/src/app/shared/components/dialog/dialog.component.ts new file mode 100644 index 0000000..fb13734 --- /dev/null +++ b/src/app/shared/components/dialog/dialog.component.ts @@ -0,0 +1,34 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Dialog } from 'primeng/dialog'; + +@Component({ + selector: 'shared-dialog', + template: ` + + + + `, + imports: [Dialog], +}) +export class SharedDialogComponent { + @Input() header = ''; + @Input() visible = false; + @Output() visibleChange = new EventEmitter(); + + @Input() modal = true; + @Input() closable = true; + @Input() draggable = false; + @Input() style: Record | undefined; + @Input() breakpoints: Record | undefined; + + @Output() onHide = new EventEmitter(); +} diff --git a/src/app/shared/components/fields/address.component.ts b/src/app/shared/components/fields/address.component.ts new file mode 100644 index 0000000..1a139c1 --- /dev/null +++ b/src/app/shared/components/fields/address.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-address', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class AddressComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'address'; +} diff --git a/src/app/shared/components/fields/branch_code.component.ts b/src/app/shared/components/fields/branch_code.component.ts new file mode 100644 index 0000000..b66a2b5 --- /dev/null +++ b/src/app/shared/components/fields/branch_code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-branch-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class BranchCodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'branch_code'; +} diff --git a/src/app/shared/components/fields/cash.component.ts b/src/app/shared/components/fields/cash.component.ts new file mode 100644 index 0000000..eb64b4b --- /dev/null +++ b/src/app/shared/components/fields/cash.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-cash', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class CashComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'cash'; +} diff --git a/src/app/shared/components/fields/code.component.ts b/src/app/shared/components/fields/code.component.ts new file mode 100644 index 0000000..8807b70 --- /dev/null +++ b/src/app/shared/components/fields/code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class CodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'code'; +} diff --git a/src/app/shared/components/fields/company_name.component.ts b/src/app/shared/components/fields/company_name.component.ts new file mode 100644 index 0000000..23a377d --- /dev/null +++ b/src/app/shared/components/fields/company_name.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-company-name', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class CompanyNameComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'company_name'; +} diff --git a/src/app/shared/components/fields/description.component.ts b/src/app/shared/components/fields/description.component.ts new file mode 100644 index 0000000..a214b30 --- /dev/null +++ b/src/app/shared/components/fields/description.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-description', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class DescriptionComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'description'; +} diff --git a/src/app/shared/components/fields/device_id.component.ts b/src/app/shared/components/fields/device_id.component.ts new file mode 100644 index 0000000..ff4fafa --- /dev/null +++ b/src/app/shared/components/fields/device_id.component.ts @@ -0,0 +1,13 @@ +import { CatalogDeviceSelectComponent } from '@/shared/catalog/devices'; +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; + +@Component({ + selector: 'field-device-id', + template: ``, + imports: [ReactiveFormsModule, CatalogDeviceSelectComponent], +}) +export class DeviceIdComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'device_id'; +} diff --git a/src/app/shared/components/fields/economic_code.component.ts b/src/app/shared/components/fields/economic_code.component.ts new file mode 100644 index 0000000..8c9cd7f --- /dev/null +++ b/src/app/shared/components/fields/economic_code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-economic-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class EconomicCodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'economic_code'; +} diff --git a/src/app/shared/components/fields/email.component.ts b/src/app/shared/components/fields/email.component.ts new file mode 100644 index 0000000..0a576f8 --- /dev/null +++ b/src/app/shared/components/fields/email.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-email', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class EmailComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'email'; +} diff --git a/src/app/shared/components/fields/first_name.component.ts b/src/app/shared/components/fields/first_name.component.ts new file mode 100644 index 0000000..8682ae2 --- /dev/null +++ b/src/app/shared/components/fields/first_name.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-first-name', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class FirstNameComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'first_name'; +} diff --git a/src/app/shared/components/fields/fiscal_code.component.ts b/src/app/shared/components/fields/fiscal_code.component.ts new file mode 100644 index 0000000..2bc842d --- /dev/null +++ b/src/app/shared/components/fields/fiscal_code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-fiscal-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class FiscalCodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'fiscal_code'; +} diff --git a/src/app/shared/components/fields/guild_id.component.ts b/src/app/shared/components/fields/guild_id.component.ts new file mode 100644 index 0000000..d5b1d29 --- /dev/null +++ b/src/app/shared/components/fields/guild_id.component.ts @@ -0,0 +1,13 @@ +import { CatalogGuildSelectComponent } from '@/shared/catalog/guild/components/select.component'; +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; + +@Component({ + selector: 'field-guild-id', + template: ``, + imports: [ReactiveFormsModule, CatalogGuildSelectComponent], +}) +export class GuildIdComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'guild_id'; +} diff --git a/src/app/shared/components/fields/index.ts b/src/app/shared/components/fields/index.ts new file mode 100644 index 0000000..78efc85 --- /dev/null +++ b/src/app/shared/components/fields/index.ts @@ -0,0 +1,34 @@ +export * from './address.component'; +export * from './branch_code.component'; +export * from './cash.component'; +export * from './code.component'; +export * from './company_name.component'; +export * from './description.component'; +export * from './device_id.component'; +export * from './economic_code.component'; +export * from './email.component'; +export * from './first_name.component'; +export * from './fiscal_code.component'; +export * from './guild_id.component'; +export * from './last_name.component'; +export * from './legal_name.component'; +export * from './license_starts_at.component'; +export * from './mobile.component'; +export * from './mobile_number.component'; +export * from './model.component'; +export * from './name.component'; +export * from './national_code.component'; +export * from './national_id.component'; +export * from './partner_token.component'; +export * from './pos_type.component'; +export * from './postal_code.component'; +export * from './provider_id.component'; +export * from './quantity.component'; +export * from './registration_code.component'; +export * from './registration_number.component'; +export * from './serial_number.component'; +export * from './set_off.component'; +export * from './sku.component'; +export * from './terminal.component'; +export * from './unit_price.component'; +export * from './username.component'; diff --git a/src/app/shared/components/fields/last_name.component.ts b/src/app/shared/components/fields/last_name.component.ts new file mode 100644 index 0000000..8164d13 --- /dev/null +++ b/src/app/shared/components/fields/last_name.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-last-name', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class LastNameComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'last_name'; +} diff --git a/src/app/shared/components/fields/legal_name.component.ts b/src/app/shared/components/fields/legal_name.component.ts new file mode 100644 index 0000000..9fba078 --- /dev/null +++ b/src/app/shared/components/fields/legal_name.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-legal-name', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class LegalNameComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'name'; + @Input() label = 'نام شرکت'; +} diff --git a/src/app/shared/components/fields/license_starts_at.component.ts b/src/app/shared/components/fields/license_starts_at.component.ts new file mode 100644 index 0000000..6152936 --- /dev/null +++ b/src/app/shared/components/fields/license_starts_at.component.ts @@ -0,0 +1,18 @@ +import { UikitFlatpickrJalaliComponent } from '@/uikit'; +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; + +@Component({ + selector: 'field-license-starts-at', + template: ``, + imports: [ReactiveFormsModule, UikitFlatpickrJalaliComponent], +}) +export class LicenseStartsAtComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'license_starts_at'; +} diff --git a/src/app/shared/components/fields/mobile.component.ts b/src/app/shared/components/fields/mobile.component.ts new file mode 100644 index 0000000..c512927 --- /dev/null +++ b/src/app/shared/components/fields/mobile.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-mobile', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class MobileComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'mobile'; +} diff --git a/src/app/shared/components/fields/mobile_number.component.ts b/src/app/shared/components/fields/mobile_number.component.ts new file mode 100644 index 0000000..c0156e3 --- /dev/null +++ b/src/app/shared/components/fields/mobile_number.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-mobile-number', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class MobileNumberComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'mobile_number'; +} diff --git a/src/app/shared/components/fields/model.component.ts b/src/app/shared/components/fields/model.component.ts new file mode 100644 index 0000000..c5e5088 --- /dev/null +++ b/src/app/shared/components/fields/model.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-model', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class ModelComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'model'; +} diff --git a/src/app/shared/components/fields/name.component.ts b/src/app/shared/components/fields/name.component.ts new file mode 100644 index 0000000..14d859a --- /dev/null +++ b/src/app/shared/components/fields/name.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-name', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class NameComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'name'; + @Input() label = 'عنوان'; +} diff --git a/src/app/shared/components/fields/national_code.component.ts b/src/app/shared/components/fields/national_code.component.ts new file mode 100644 index 0000000..4859527 --- /dev/null +++ b/src/app/shared/components/fields/national_code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-national-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class NationalCodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'national_code'; +} diff --git a/src/app/shared/components/fields/national_id.component.ts b/src/app/shared/components/fields/national_id.component.ts new file mode 100644 index 0000000..bea1674 --- /dev/null +++ b/src/app/shared/components/fields/national_id.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-national-id', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class NationalIdComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'national_id'; +} diff --git a/src/app/shared/components/fields/partner_token.component.ts b/src/app/shared/components/fields/partner_token.component.ts new file mode 100644 index 0000000..e79e24c --- /dev/null +++ b/src/app/shared/components/fields/partner_token.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-partner-token', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class PartnerTokenComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'partner_token'; +} diff --git a/src/app/shared/components/fields/pos_type.component.ts b/src/app/shared/components/fields/pos_type.component.ts new file mode 100644 index 0000000..6e08517 --- /dev/null +++ b/src/app/shared/components/fields/pos_type.component.ts @@ -0,0 +1,13 @@ +import { EnumSelectComponent } from '@/shared/apiEnum/select.component'; +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; + +@Component({ + selector: 'field-pos-type', + template: ``, + imports: [ReactiveFormsModule, EnumSelectComponent], +}) +export class PosTypeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'pos_type'; +} diff --git a/src/app/shared/components/fields/postal_code.component.ts b/src/app/shared/components/fields/postal_code.component.ts new file mode 100644 index 0000000..894df9d --- /dev/null +++ b/src/app/shared/components/fields/postal_code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-postal-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class PostalCodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'postal_code'; +} diff --git a/src/app/shared/components/fields/provider_id.component.ts b/src/app/shared/components/fields/provider_id.component.ts new file mode 100644 index 0000000..372d359 --- /dev/null +++ b/src/app/shared/components/fields/provider_id.component.ts @@ -0,0 +1,13 @@ +import { CatalogProviderSelectComponent } from '@/shared/catalog'; +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; + +@Component({ + selector: 'field-provider-id', + template: ``, + imports: [ReactiveFormsModule, CatalogProviderSelectComponent], +}) +export class ProviderIdComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'provider_id'; +} diff --git a/src/app/shared/components/fields/quantity.component.ts b/src/app/shared/components/fields/quantity.component.ts new file mode 100644 index 0000000..9e739cb --- /dev/null +++ b/src/app/shared/components/fields/quantity.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-quantity', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class QuantityComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'quantity'; +} diff --git a/src/app/shared/components/fields/registration_code.component.ts b/src/app/shared/components/fields/registration_code.component.ts new file mode 100644 index 0000000..ba5a968 --- /dev/null +++ b/src/app/shared/components/fields/registration_code.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-registration-code', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class RegistrationCodeComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'registration_code'; +} diff --git a/src/app/shared/components/fields/registration_number.component.ts b/src/app/shared/components/fields/registration_number.component.ts new file mode 100644 index 0000000..24be7d0 --- /dev/null +++ b/src/app/shared/components/fields/registration_number.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-registration-number', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class RegistrationNumberComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'registration_number'; +} diff --git a/src/app/shared/components/fields/serial_number.component.ts b/src/app/shared/components/fields/serial_number.component.ts new file mode 100644 index 0000000..7d17a46 --- /dev/null +++ b/src/app/shared/components/fields/serial_number.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-serial-number', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class SerialNumberComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'serial_number'; +} diff --git a/src/app/shared/components/fields/set_off.component.ts b/src/app/shared/components/fields/set_off.component.ts new file mode 100644 index 0000000..e5d28a0 --- /dev/null +++ b/src/app/shared/components/fields/set_off.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-set-off', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class SetOffComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'set_off'; +} diff --git a/src/app/shared/components/fields/sku.component.ts b/src/app/shared/components/fields/sku.component.ts new file mode 100644 index 0000000..fc60faa --- /dev/null +++ b/src/app/shared/components/fields/sku.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-sku', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class SkuComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'sku'; +} diff --git a/src/app/shared/components/fields/terminal.component.ts b/src/app/shared/components/fields/terminal.component.ts new file mode 100644 index 0000000..0549ec2 --- /dev/null +++ b/src/app/shared/components/fields/terminal.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-terminal', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class TerminalComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'terminal'; +} diff --git a/src/app/shared/components/fields/unit_price.component.ts b/src/app/shared/components/fields/unit_price.component.ts new file mode 100644 index 0000000..48c9ab5 --- /dev/null +++ b/src/app/shared/components/fields/unit_price.component.ts @@ -0,0 +1,13 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-unit-price', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class UnitPriceComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'unit_price'; +} diff --git a/src/app/shared/components/fields/username.component.ts b/src/app/shared/components/fields/username.component.ts new file mode 100644 index 0000000..8430ed4 --- /dev/null +++ b/src/app/shared/components/fields/username.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; +import { FormControl, ReactiveFormsModule } from '@angular/forms'; +import { InputComponent } from '../input/input.component'; + +@Component({ + selector: 'field-username', + template: ``, + imports: [ReactiveFormsModule, InputComponent], +}) +export class UsernameComponent { + @Input({ required: true }) control = new FormControl(''); + @Input() name = 'username'; + @Input() label = 'نام کاربری'; +} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index 74de5f4..3812deb 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -2,6 +2,8 @@ export * from './breadcrumb.component'; export * from './card-data.component'; export * from './changePasswordFormDialog/change-password-form-dialog.component'; +export * from './dialog/dialog.component'; +export * from './fields'; export * from './inlineConfirmation/inline-confirmation.component'; export * from './inlineEdit/inline-edit.component'; export * from './input/input.component'; diff --git a/src/app/shared/components/passwordInput/password-input.component.html b/src/app/shared/components/passwordInput/password-input.component.html index 0fe40a0..f409da6 100644 --- a/src/app/shared/components/passwordInput/password-input.component.html +++ b/src/app/shared/components/passwordInput/password-input.component.html @@ -2,7 +2,7 @@ [Validators.required] as ValidatorFn[]; +type ControlConfig = [string, ValidatorFn[]]; + +export const fieldControl = { + name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + code: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + description: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + company_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + first_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + firstName: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + last_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + lastName: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + legal_name: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + registration_code: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? required() : [], + ], + registration_number: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? required() : [], + ], + mobile_number: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()], + ], + mobile: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? [Validators.required, mobileValidator()] : [mobileValidator()], + ], + email: (value = '', isRequired = false): ControlConfig => [ + value, + isRequired ? [Validators.required, Validators.email] : [Validators.email], + ], + national_code: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? [Validators.required, nationalIdValidator()] : [nationalIdValidator()], + ], + national_id: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? [Validators.required, nationalIdValidator()] : [nationalIdValidator()], + ], + postal_code: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? [Validators.required, postalCodeValidator()] : [postalCodeValidator()], + ], + username: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + economic_code: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? required() : [], + ], + fiscal_code: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? [Validators.required, fiscalCodeValidator()] : [fiscalCodeValidator()], + ], + partner_token: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? required() : [], + ], + guild_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + license_starts_at: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? required() : [], + ], + branch_code: (value = '', isRequired = false): ControlConfig => [ + value, + isRequired ? required() : [], + ], + address: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + pos_type: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + type: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + role: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + unit_type: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + pricing_model: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + gender: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + legal: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + individual: (value = '', isRequired = false): ControlConfig => [ + value, + isRequired ? required() : [], + ], + partner_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + brand_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + category_id: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + starts_at: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + expires_at: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + activated_expires_at: (value = '', isRequired = true): ControlConfig => [ + value, + isRequired ? required() : [], + ], + invoiceDate: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + quantity: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + unit_price: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + cash: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + set_off: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + setOff: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + terminal: (value = '', isRequired = false): ControlConfig => [value, isRequired ? required() : []], + sku: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + model: (value = '', isRequired = true): ControlConfig => [value, isRequired ? required() : []], + serial_number: (value = '', isRequired = false): ControlConfig => [ + value, + isRequired ? required() : [], + ], + device_id: (value = '', isRequired = false): ControlConfig => [ + value, + isRequired ? required() : [], + ], + provider_id: (value = '', isRequired = false): ControlConfig => [ + value, + isRequired ? required() : [], + ], +}; diff --git a/src/app/shared/constants/index.ts b/src/app/shared/constants/index.ts new file mode 100644 index 0000000..cbc6c8c --- /dev/null +++ b/src/app/shared/constants/index.ts @@ -0,0 +1 @@ +export * from './fields'; diff --git a/src/app/shared/models/consumer.type.ts b/src/app/shared/models/consumer.type.ts new file mode 100644 index 0000000..d1bf45d --- /dev/null +++ b/src/app/shared/models/consumer.type.ts @@ -0,0 +1,40 @@ +import { Maybe } from '@/core'; + +export interface IConsumerRawResponse { + id: string; + status: string; + created_at: string; + name: string; + type: 'INDIVIDUAL' | 'LEGAL'; + partner: { + id: string; + name: string; + code: string; + }; + individual?: Maybe; + legal?: Maybe; +} +export interface IConsumerResponse extends IConsumerRawResponse {} + +export interface IConsumerRequest { + type?: 'INDIVIDUAL' | 'LEGAL'; + username?: string; + password?: string; + status?: string; + customer?: { + individual?: IConsumerIndividual; + legal?: IConsumerLegal; + }; +} + +interface IConsumerIndividual { + first_name: string; + last_name: string; + mobile_number: string; + national_code?: string; +} + +interface IConsumerLegal { + name: string; + registration_code?: string; +} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 9bad5de..f801336 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -6,4 +6,5 @@ export const environment = { port: 5000, enableLogging: false, enableDebug: false, + enableNativeBridge: false, }; diff --git a/src/environments/environment.staging.ts b/src/environments/environment.staging.ts index 5cc17e6..21d8a7b 100644 --- a/src/environments/environment.staging.ts +++ b/src/environments/environment.staging.ts @@ -4,6 +4,7 @@ export const environment = { apiBaseUrl: 'https://staging-api.yourdomain.com', host: 'staging.yourdomain.com', port: 443, + enableNativeBridge: false, apiEndpoints: { auth: '/api/auth', users: '/api/users', diff --git a/src/environments/environment.tis.ts b/src/environments/environment.tis.ts new file mode 100644 index 0000000..ffb4abc --- /dev/null +++ b/src/environments/environment.tis.ts @@ -0,0 +1,10 @@ +// TIS tenant environment configuration +export const environment = { + production: true, + apiBaseUrl: 'http://194.59.214.243:5002', + host: 'localhost', + port: 5000, + enableLogging: false, + enableDebug: false, + enableNativeBridge: true, +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 122bf58..3f7ba4d 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -8,4 +8,5 @@ export const environment = { port: 5000, enableLogging: true, enableDebug: true, + enableNativeBridge: false, }; diff --git a/src/index.html b/src/index.html index 17efe0d..aed0bf8 100644 --- a/src/index.html +++ b/src/index.html @@ -5,6 +5,7 @@ پنل مدیریت صورت‌حساب‌های مالیاتی + diff --git a/src/main.ts b/src/main.ts index d1aee1e..3cccf51 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,7 @@ import { bootstrapApplication } from '@angular/platform-browser'; import flatpickr from 'flatpickr-wrap'; import faLocales from 'flatpickr-wrap/dist/l10n/fa.js'; +import { brandingConfig } from './app/branding/branding.config'; import { AppComponent } from './app.component'; import { appConfig } from './app.config'; @@ -19,4 +20,16 @@ if (flatpickr.defaultConfig) { }); } +document.title = brandingConfig.appTitle; + +const manifestLink = document.querySelector('link[rel="manifest"]'); +if (manifestLink) { + manifestLink.setAttribute('href', brandingConfig.manifestPath); +} + +const themeMeta = document.querySelector('meta[name="theme-color"]'); +if (themeMeta) { + themeMeta.setAttribute('content', brandingConfig.themeColor); +} + bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err)); diff --git a/src/tenants/default/app.routes.ts b/src/tenants/default/app.routes.ts new file mode 100644 index 0000000..4965e3d --- /dev/null +++ b/src/tenants/default/app.routes.ts @@ -0,0 +1,35 @@ +import { CONSUMER_ROUTES } from '@/domains/consumer/routes'; +import { PARTNER_ROUTES } from '@/domains/partner/routes'; +import { POS_ROUTES } from '@/domains/pos/routes'; +import { PROVIDER_ROUTES } from '@/domains/provider/routes'; +import { SUPER_ADMIN_ROUTES } from '@/domains/superAdmin/routes'; +import { AppLayout } from '@/layout/default/app.layout.component'; +import { AuthComponent } from '@/modules/auth/pages/auth.component'; +import { Dashboard } from '@/pages/dashboard/dashboard'; +import { Documentation } from '@/pages/documentation/documentation'; +import { Notfound } from '@/pages/notfound/notfound.component'; +import { Routes } from '@angular/router'; + +export const appRoutes: Routes = [ + { + path: '', + component: AppLayout, + children: [ + SUPER_ADMIN_ROUTES, + CONSUMER_ROUTES, + PROVIDER_ROUTES, + PARTNER_ROUTES, + { path: 'ng', component: Dashboard }, + { path: 'uikit', loadChildren: () => import('@/pages/uikit/uikit.routes') }, + { path: 'documentation', component: Documentation }, + { path: 'pages', loadChildren: () => import('@/pages/pages.routes') }, + ], + }, + POS_ROUTES, + { + path: 'auth', + component: AuthComponent, + }, + { path: 'notfound', component: Notfound }, + { path: '**', redirectTo: '/notfound' }, +]; diff --git a/src/tenants/default/branding.config.ts b/src/tenants/default/branding.config.ts new file mode 100644 index 0000000..a1223a1 --- /dev/null +++ b/src/tenants/default/branding.config.ts @@ -0,0 +1,11 @@ +export interface BrandingConfig { + appTitle: string; + manifestPath: string; + themeColor: string; +} + +export const brandingConfig: BrandingConfig = { + appTitle: 'پنل مدیریت صورت‌حساب‌های مالیاتی', + manifestPath: '/favicon/site.webmanifest', + themeColor: '#ffffff', +}; diff --git a/src/tenants/tis/app.routes.ts b/src/tenants/tis/app.routes.ts new file mode 100644 index 0000000..abb2745 --- /dev/null +++ b/src/tenants/tis/app.routes.ts @@ -0,0 +1,15 @@ +import { POS_ROUTES } from '@/domains/pos/routes'; +import { AuthComponent } from '@/modules/auth/pages/auth.component'; +import { Notfound } from '@/pages/notfound/notfound.component'; +import { Routes } from '@angular/router'; + +export const appRoutes: Routes = [ + { path: '', redirectTo: 'pos', pathMatch: 'full' }, + POS_ROUTES, + { + path: 'auth', + component: AuthComponent, + }, + { path: 'notfound', component: Notfound }, + { path: '**', redirectTo: '/notfound' }, +]; diff --git a/src/tenants/tis/branding.config.ts b/src/tenants/tis/branding.config.ts new file mode 100644 index 0000000..a6dfd0f --- /dev/null +++ b/src/tenants/tis/branding.config.ts @@ -0,0 +1,7 @@ +import { BrandingConfig } from '../default/branding.config'; + +export const brandingConfig: BrandingConfig = { + appTitle: 'PSP A - مدیریت صورت‌حساب‌های مالیاتی', + manifestPath: '/favicon/site.webmanifest', + themeColor: '#ffffff', +};