@team-codeme/crud-forge 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/agents/generator-engineer.md +31 -0
- package/.cursor/agents/keycloak-engineer.md +33 -0
- package/.cursor/agents/qa-engineer.md +32 -0
- package/.cursor/agents/security-reviewer.md +30 -0
- package/.cursor/mcp.json +13 -0
- package/.cursor/rules/crudforge-precautions.mdc +16 -0
- package/.cursor/rules/playwright-browser.mdc +30 -0
- package/.cursor/rules/readme-todo-sync.mdc +13 -0
- package/.env.qa.example +19 -0
- package/.github/workflows/qa-live.yml +32 -0
- package/.github/workflows/qa.yml +27 -0
- package/.vscode/settings.json +32 -0
- package/QA/IAM-PLAN.md +25 -0
- package/QA/IAM-RESULTS.md +39 -0
- package/QA/IAM-TESTCASES.md +36 -0
- package/QA/IAM-TODO.md +7 -0
- package/QA/KEYCLOAK-BOOTSTRAP.md +105 -0
- package/QA/PLAN.md +43 -0
- package/QA/RESULTS.md +59 -0
- package/QA/TESTCASES.md +65 -0
- package/QA/TODO.md +14 -0
- package/QA/pmb-dev/README.md +49 -0
- package/QA/pmb-dev/config/config.yml +152 -0
- package/QA/pmb-dev/gdl/i.gdl +17 -0
- package/README.md +851 -2
- package/Todo.md +207 -0
- package/assets/branding/crudforge-logo.png +0 -0
- package/assets/branding/crudforge-mark.png +0 -0
- package/branding/poster-crudforge.jpg +0 -0
- package/chat/last-chat/Investigating Mock Data Configuration.md +428 -0
- package/cli.js +167 -0
- package/config/default-config.yml +245 -0
- package/docs/performance/README.md +217 -0
- package/docs/performance/after.json +111 -0
- package/docs/performance/before.json +111 -0
- package/docs/quality/README.md +276 -0
- package/e2e/crudforge.e2e.yml +47 -0
- package/e2e/crudforge.field-profile.yml +33 -0
- package/e2e/crudforge.iam-live.yml +124 -0
- package/e2e/crudforge.iam.yml +62 -0
- package/e2e/tests/field-profile.spec.js +152 -0
- package/e2e/tests/generated-crud.spec.js +251 -0
- package/e2e/tests/iam-live.spec.js +149 -0
- package/e2e/tests/iam-pack.spec.js +151 -0
- package/e2e/tests/list-state.spec.js +73 -0
- package/features.csv +27 -0
- package/fuse-starter-pack/branding/crudforge-mark-source.jpg +0 -0
- package/fuse-starter-pack/branding/crudforge-mark-source.png +0 -0
- package/fuse-starter-pack/branding/crudforge-mark.png +0 -0
- package/fuse-starter-pack/branding/crudforge-wordmark-source.jpg +0 -0
- package/fuse-starter-pack/branding/crudforge-wordmark-source.png +0 -0
- package/fuse-starter-pack/branding/crudforge-wordmark.png +0 -0
- package/fuse-starter-pack/fuse-v21.1/Dockerfile +14 -0
- package/fuse-starter-pack/fuse-v21.1/LICENSE.md +6 -0
- package/fuse-starter-pack/fuse-v21.1/README.md +59 -0
- package/fuse-starter-pack/fuse-v21.1/angular.json +113 -0
- package/fuse-starter-pack/fuse-v21.1/eslint.config.ts +150 -0
- package/fuse-starter-pack/fuse-v21.1/nginx.conf +23 -0
- package/fuse-starter-pack/fuse-v21.1/nginx.dev.conf +18 -0
- package/fuse-starter-pack/fuse-v21.1/package-lock.json +11513 -0
- package/fuse-starter-pack/fuse-v21.1/package.json +69 -0
- package/fuse-starter-pack/fuse-v21.1/postcss.config.json +5 -0
- package/fuse-starter-pack/fuse-v21.1/prettier.config.ts +15 -0
- package/fuse-starter-pack/fuse-v21.1/proxy.conf.json +8 -0
- package/fuse-starter-pack/fuse-v21.1/proxy.dev.conf.json +8 -0
- package/fuse-starter-pack/fuse-v21.1/public/favicon.png +0 -0
- package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/Geist-Italic.woff2 +0 -0
- package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/Geist.woff2 +0 -0
- package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/GeistMono-Italic.woff2 +0 -0
- package/fuse-starter-pack/fuse-v21.1/public/fonts/geist/GeistMono.woff2 +0 -0
- package/fuse-starter-pack/fuse-v21.1/public/i18n/en.json +8 -0
- package/fuse-starter-pack/fuse-v21.1/public/i18n/es.json +8 -0
- package/fuse-starter-pack/fuse-v21.1/public/images/flags/ES.svg +79 -0
- package/fuse-starter-pack/fuse-v21.1/public/images/flags/US.svg +14 -0
- package/fuse-starter-pack/fuse-v21.1/public/images/flags/where-to-find-other-flags.txt +5 -0
- package/fuse-starter-pack/fuse-v21.1/public/images/logo/logo-mark.png +0 -0
- package/fuse-starter-pack/fuse-v21.1/public/images/logo/logo.png +0 -0
- package/fuse-starter-pack/fuse-v21.1/push.sh +27 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/app.config.server.ts +10 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/app.config.ts +64 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/app.routes.server.ts +8 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/app.routes.ts +39 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/app.ts +22 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/icons/index.ts +1 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/icons/lucide.ts +1 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/icons/provider.ts +23 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/local-storage/index.ts +2 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/local-storage/local-storage.ts +109 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/local-storage/provider.ts +15 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/media/index.ts +2 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/media/media.ts +64 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/media/provider.ts +15 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/index.ts +3 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/models/theming.ts +13 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/palette.ts +209 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/provider.ts +24 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/theming/theming.ts +129 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/core/transloco/transloco-http-loader.ts +12 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/data/navigation.ts +176 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/layout.ts +77 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/language-switcher.ts +91 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/navigation.ts +238 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/notifications.ts +252 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/scheme-switcher.ts +58 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/sidebar.ts +31 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/layout/ui/user.ts +132 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/data/academy.ts +753 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/data/model.ts +27 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/features/course.ts +318 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/features/courses.ts +228 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/academy/routes.ts +14 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/data/contacts.ts +4372 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/data/model.ts +34 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/features/contact.ts +239 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/features/contacts.ts +185 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/contacts/routes.ts +16 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/data/file-manager.ts +261 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/data/model.ts +12 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/features/file-manager.ts +131 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/routes.ts +15 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/file-manager/ui/list.ts +164 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/faq.ts +288 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/guide.ts +134 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/home.ts +219 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/features/support.ts +161 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/help-center/routes.ts +33 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/data/model.ts +18 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/data/notes.ts +324 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/features/note.ts +152 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/features/notes.ts +118 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/notes/routes.ts +16 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/data/model.ts +17 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/data/tasks.ts +562 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/features/task.ts +195 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/features/tasks.ts +271 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/apps/tasks/routes.ts +16 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/data/analytics.ts +1620 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/data/finance.ts +1065 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/data/project.ts +376 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/analytics/analytics.ts +1096 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/finance/finance.ts +658 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/project/budget-table.ts +188 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/features/project/project.ts +565 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/dashboards/routes.ts +23 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/changelog/changelog.ts +932 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/getting-started/building.ts +33 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/getting-started/development.ts +42 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/features/getting-started/installation.ts +47 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/layout.ts +9 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/documentation/routes.ts +45 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/error/features/error-page.ts +70 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/error/routes.ts +21 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/notifications/features/notifications.ts +262 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/notifications/routes.ts +10 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/account.ts +238 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/notifications.ts +134 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/plan-and-billing.ts +238 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/security.ts +211 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/features/team.ts +188 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/layout.ts +109 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/settings/routes.ts +37 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/modules/extras/welcome/welcome.component.ts +22 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/admin/routes.ts +95 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/forgot-password/forgot-password.html +54 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/forgot-password/forgot-password.ts +51 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/reset-password/reset-password.html +82 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/reset-password/reset-password.ts +69 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-in/sign-in.html +132 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-in/sign-in.ts +54 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-up/sign-up.html +183 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/features/sign-up/sign-up.ts +56 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/layout.ts +9 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/auth/routes.ts +34 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/coming-soon/features/coming-soon.ts +156 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/coming-soon/layout.ts +9 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/coming-soon/routes.ts +16 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/layout/layout.ts +9 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/modules/home/features/home.ts +23 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/modules/home/routes.ts +10 -0
- package/fuse-starter-pack/fuse-v21.1/src/app/domains/website/routes.ts +30 -0
- package/fuse-starter-pack/fuse-v21.1/src/environments/environment.mock.ts +5 -0
- package/fuse-starter-pack/fuse-v21.1/src/environments/environment.prod.ts +5 -0
- package/fuse-starter-pack/fuse-v21.1/src/environments/environment.ts +5 -0
- package/fuse-starter-pack/fuse-v21.1/src/index.html +66 -0
- package/fuse-starter-pack/fuse-v21.1/src/main.server.ts +11 -0
- package/fuse-starter-pack/fuse-v21.1/src/main.ts +5 -0
- package/fuse-starter-pack/fuse-v21.1/src/server.ts +70 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/base/base.css +63 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/base/typography.css +54 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/components/a.css +18 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/components/input.css +22 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/components/kbd.css +55 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/components/material.css +1149 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/styles.css +31 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/tailwind/theme.css +147 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/tailwind/utilities.css +563 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/tailwind/variants.css +4 -0
- package/fuse-starter-pack/fuse-v21.1/src/styles/third-party/lucide-icons.css +12 -0
- package/fuse-starter-pack/fuse-v21.1/tsconfig.app.json +16 -0
- package/fuse-starter-pack/fuse-v21.1/tsconfig.json +37 -0
- package/fuse-starter-pack/fuse-v21.1/tsconfig.spec.json +10 -0
- package/generator/README.md +12 -0
- package/generator/issues/.gitkeep +0 -0
- package/package.json +56 -4
- package/playwright.config.js +29 -0
- package/playwright.field-profile.config.js +6 -0
- package/playwright.iam-live.config.js +31 -0
- package/playwright.iam.config.js +29 -0
- package/playwright.list-state.config.js +6 -0
- package/playwright.performance.config.js +1 -0
- package/run-config-tests.sh +5 -0
- package/scripts/e2e-paths.js +89 -0
- package/scripts/e2e-prepare-field-profile.js +17 -0
- package/scripts/e2e-prepare-iam-live.js +135 -0
- package/scripts/e2e-prepare-iam.js +65 -0
- package/scripts/e2e-prepare.js +72 -0
- package/scripts/e2e-serve-iam-live.js +28 -0
- package/scripts/e2e-serve-iam.js +28 -0
- package/scripts/e2e-serve.js +28 -0
- package/scripts/performance/generate.js +17 -0
- package/scripts/performance/measure.js +55 -0
- package/scripts/postinstall.js +9 -0
- package/scripts/prepare-quality-apps.js +34 -0
- package/scripts/run-config-tests.js +63 -0
- package/src/assets/logo-with-lable.jpeg +0 -0
- package/src/assets/logo.jpeg +0 -0
- package/src/commands/bootstrap.js +92 -0
- package/src/commands/doctor.js +403 -0
- package/src/commands/generate.js +301 -0
- package/src/commands/init-config.js +310 -0
- package/src/commands/keycloak-options.js +182 -0
- package/src/commands/validate.js +52 -0
- package/src/config/auth-config.js +183 -0
- package/src/config/default-values.js +235 -0
- package/src/config/env-refs.js +71 -0
- package/src/config/field-preferences.js +99 -0
- package/src/config/input-sources.js +104 -0
- package/src/config/load-config.js +166 -0
- package/src/config/proxy-routes.js +44 -0
- package/src/generator/access-control-tools.js +334 -0
- package/src/generator/ai-docs.js +691 -0
- package/src/generator/application-tests.js +252 -0
- package/src/generator/auth-pack.js +965 -0
- package/src/generator/embed-config.js +431 -0
- package/src/generator/entity-layout.js +30 -0
- package/src/generator/fuse-build.js +57 -0
- package/src/generator/fuse-runtime.js +344 -0
- package/src/generator/generated-banner.js +137 -0
- package/src/generator/index.js +874 -0
- package/src/generator/project-output.js +126 -0
- package/src/generator/team-pack.js +220 -0
- package/src/index.js +68 -0
- package/src/parser/gdl-parser.js +169 -0
- package/src/parser/jdl-parser.js +171 -0
- package/src/presets/pmb-test.js +139 -0
- package/src/presets/static-source.js +73 -0
- package/src/presets/store.js +96 -0
- package/src/provision/admin-service.js +148 -0
- package/src/provision/bootstrap.js +147 -0
- package/src/provision/http.js +123 -0
- package/src/provision/keycloak-admin-rest.js +335 -0
- package/src/provision/state.js +51 -0
- package/src/provision/token.js +52 -0
- package/src/templates/angular/auth/access-control.service.ts.ejs +179 -0
- package/src/templates/angular/auth/access-ctrl.pipe.ts.ejs +20 -0
- package/src/templates/angular/auth/auth.guard.ts.ejs +27 -0
- package/src/templates/angular/auth/auth.interceptor.ts.ejs +53 -0
- package/src/templates/angular/auth/auth.service.ts.ejs +594 -0
- package/src/templates/angular/auth/auth.utils.ts.ejs +28 -0
- package/src/templates/angular/auth/current-user.ts.ejs +24 -0
- package/src/templates/angular/auth/disabled-account-overlay.component.css.ejs +95 -0
- package/src/templates/angular/auth/disabled-account-overlay.component.html.ejs +33 -0
- package/src/templates/angular/auth/disabled-account-overlay.component.ts.ejs +23 -0
- package/src/templates/angular/auth/fuse-account.ts.ejs +234 -0
- package/src/templates/angular/auth/fuse-user.ts.ejs +264 -0
- package/src/templates/angular/auth/has-permission.directive.ts.ejs +51 -0
- package/src/templates/angular/auth/index.ts.ejs +24 -0
- package/src/templates/angular/auth/keycloak-admin.service.ts.ejs +670 -0
- package/src/templates/angular/auth/landing.guard.ts.ejs +61 -0
- package/src/templates/angular/auth/no-auth.guard.ts.ejs +19 -0
- package/src/templates/angular/auth/permission-attribute.util.ts.ejs +182 -0
- package/src/templates/angular/auth/sign-in.ts.ejs +60 -0
- package/src/templates/angular/entity/_entity_.model.ts.ejs +64 -0
- package/src/templates/angular/entity/_entity_.routes.ts.ejs +9 -0
- package/src/templates/angular/entity/components/forms/form/_entity_-form.component.html.ejs +168 -0
- package/src/templates/angular/entity/components/forms/form/_entity_-form.component.ts.ejs +175 -0
- package/src/templates/angular/entity/components/forms/form-page/_entity_-form-page.component.html.ejs +10 -0
- package/src/templates/angular/entity/components/forms/form-page/_entity_-form-page.component.ts.ejs +86 -0
- package/src/templates/angular/entity/components/forms/update/_entity_-form.service.ts.ejs +136 -0
- package/src/templates/angular/entity/components/interaction/actions/_entity_-actions.component.html.ejs +68 -0
- package/src/templates/angular/entity/components/interaction/actions/_entity_-actions.component.ts.ejs +69 -0
- package/src/templates/angular/entity/components/interaction/filter/_entity_-filter.component.html.ejs +111 -0
- package/src/templates/angular/entity/components/interaction/filter/_entity_-filter.component.ts.ejs +102 -0
- package/src/templates/angular/entity/components/presentation/card/_entity_-card.component.css.ejs +33 -0
- package/src/templates/angular/entity/components/presentation/card/_entity_-card.component.html.ejs +29 -0
- package/src/templates/angular/entity/components/presentation/card/_entity_-card.component.ts.ejs +93 -0
- package/src/templates/angular/entity/components/presentation/detail/_entity_-detail.component.css.ejs +54 -0
- package/src/templates/angular/entity/components/presentation/detail/_entity_-detail.component.html.ejs +58 -0
- package/src/templates/angular/entity/components/presentation/detail/_entity_-detail.component.ts.ejs +47 -0
- package/src/templates/angular/entity/components/presentation/list/_entity_-list.component.html.ejs +419 -0
- package/src/templates/angular/entity/components/presentation/list/_entity_-list.component.scss.ejs +48 -0
- package/src/templates/angular/entity/components/presentation/list/_entity_-list.component.ts.ejs +592 -0
- package/src/templates/angular/entity/service/_entity_.service.ts.ejs +153 -0
- package/src/templates/angular/entity/settings/_entity_.settings.ts.ejs +49 -0
- package/src/templates/angular/enums/_enum_.model.ts.ejs +11 -0
- package/src/templates/angular/iam/change-password.component.html.ejs +39 -0
- package/src/templates/angular/iam/change-password.component.ts.ejs +44 -0
- package/src/templates/angular/iam/no-access.component.ts.ejs +17 -0
- package/src/templates/angular/iam/role-list.component.html.ejs +331 -0
- package/src/templates/angular/iam/role-list.component.scss.ejs +107 -0
- package/src/templates/angular/iam/role-list.component.ts.ejs +597 -0
- package/src/templates/angular/iam/role-management/role-header.component.html.ejs +51 -0
- package/src/templates/angular/iam/role-management/role-header.component.ts.ejs +45 -0
- package/src/templates/angular/iam/role-management/role-inheritance.component.html.ejs +15 -0
- package/src/templates/angular/iam/role-management/role-inheritance.component.ts.ejs +19 -0
- package/src/templates/angular/iam/role-management/role-permissions-panel.component.css.ejs +24 -0
- package/src/templates/angular/iam/role-management/role-permissions-panel.component.html.ejs +108 -0
- package/src/templates/angular/iam/role-management/role-permissions-panel.component.ts.ejs +122 -0
- package/src/templates/angular/iam/role-management/role-users.component.html.ejs +104 -0
- package/src/templates/angular/iam/role-management/role-users.component.ts.ejs +53 -0
- package/src/templates/angular/iam/role-management/user-select-dialog.component.html.ejs +113 -0
- package/src/templates/angular/iam/role-management/user-select-dialog.component.ts.ejs +157 -0
- package/src/templates/angular/iam/role-permission-matrix.component.html.ejs +355 -0
- package/src/templates/angular/iam/role-permission-matrix.component.scss.ejs +217 -0
- package/src/templates/angular/iam/role-permission-matrix.component.ts.ejs +800 -0
- package/src/templates/angular/iam/screen-catalog.ts.ejs +147 -0
- package/src/templates/angular/iam/shared/confirm-dialog.component.ts.ejs +83 -0
- package/src/templates/angular/iam/shared/matrix-hierarchy.util.ts.ejs +196 -0
- package/src/templates/angular/iam/shared/permission-matrix-sound.ts.ejs +59 -0
- package/src/templates/angular/iam/shared/role-display.util.ts.ejs +43 -0
- package/src/templates/angular/iam/shared/role-permission-skeleton.component.ts.ejs +54 -0
- package/src/templates/angular/iam/shared/screen-matrix.util.ts.ejs +554 -0
- package/src/templates/angular/iam/shared/screen-navigation-map.ts.ejs +187 -0
- package/src/templates/angular/iam/shared/screen-navigation.registry.ts.ejs +64 -0
- package/src/templates/angular/iam/shared/text-highlight.pipe.ts.ejs +39 -0
- package/src/templates/angular/iam/user-access-matrix.component.html.ejs +212 -0
- package/src/templates/angular/iam/user-access-matrix.component.scss.ejs +34 -0
- package/src/templates/angular/iam/user-access-matrix.component.ts.ejs +239 -0
- package/src/templates/angular/iam/user-detail.component.html.ejs +143 -0
- package/src/templates/angular/iam/user-detail.component.ts.ejs +108 -0
- package/src/templates/angular/iam/user-form.component.html.ejs +63 -0
- package/src/templates/angular/iam/user-form.component.ts.ejs +100 -0
- package/src/templates/angular/iam/user-list.component.html.ejs +369 -0
- package/src/templates/angular/iam/user-list.component.scss.ejs +242 -0
- package/src/templates/angular/iam/user-list.component.ts.ejs +446 -0
- package/src/templates/angular/iam/user-management.routes.ts.ejs +47 -0
- package/src/templates/angular/iam/user-permission-matrix.component.html.ejs +231 -0
- package/src/templates/angular/iam/user-permission-matrix.component.scss.ejs +216 -0
- package/src/templates/angular/iam/user-permission-matrix.component.ts.ejs +516 -0
- package/src/templates/angular/shared/base/audit-user-name.pipe.ts.ejs +69 -0
- package/src/templates/angular/shared/base/base-filter.component.ts.ejs +245 -0
- package/src/templates/angular/shared/base/base-form.component.ts.ejs +109 -0
- package/src/templates/angular/shared/base/base-list.component.ts.ejs +507 -0
- package/src/templates/angular/shared/base/filter-types.ts.ejs +55 -0
- package/src/templates/angular/shared/base/input-format.directive.ts.ejs +47 -0
- package/src/templates/angular/shared/base/input-format.ts.ejs +15 -0
- package/src/templates/angular/shared/confirm-with-input-dialog.component.ts.ejs +91 -0
- package/src/templates/angular/shared/confirmation-dialog.component.ts.ejs +80 -0
- package/src/templates/angular/shared/list-styles.css.ejs +103 -0
- package/src/templates/angular/shared/loading-overlay.component.ts.ejs +24 -0
- package/src/templates/angular/shared/resizable.directive.ts.ejs +115 -0
- package/src/templates/angular/shared/snackbar-styles.css.ejs +11 -0
- package/src/templates/fuse-packs/access-control-tools/access-control-sync.js +819 -0
- package/src/templates/fuse-packs/access-control-tools/build-rename-map.js +67 -0
- package/src/templates/fuse-packs/access-control-tools/inject-qa-ids.js +14 -0
- package/src/templates/fuse-packs/access-control-tools/lib/hierarchy-scanner.js +628 -0
- package/src/templates/fuse-packs/access-control-tools/lib/hierarchy-scanner.test.js +323 -0
- package/src/templates/fuse-packs/access-control-tools/lib/html-scanner.js +362 -0
- package/src/templates/fuse-packs/access-control-tools/lib/id-rules.duplicate.test.js +136 -0
- package/src/templates/fuse-packs/access-control-tools/lib/id-rules.js +1095 -0
- package/src/templates/fuse-packs/access-control-tools/lib/ts-component-index.js +482 -0
- package/src/templates/fuse-packs/access-control-tools/output/.gitkeep +0 -0
- package/src/templates/fuse-packs/access-control-tools/run.sh +223 -0
- package/src/templates/fuse-packs/access-control-tools/scan-access-control.js +14 -0
- package/src/templates/fuse-packs/iam-assets/sound/apple-play.mp3 +0 -0
- package/src/templates/fuse-packs/iam-assets/sound/remove.mp3 +0 -0
- package/src/templates/generator/README.md +12 -0
- package/src/templates/testing/ci.cjs +45 -0
- package/src/templates/testing/mock.cjs +149 -0
- package/src/templates/testing/playwright.config.cjs +25 -0
- package/src/templates/testing/serve.cjs +22 -0
- package/src/templates/testing/smoke.spec.cjs +180 -0
- package/src/utils/branding.js +39 -0
- package/src/utils/colors.js +18 -0
- package/src/utils/diff.js +109 -0
- package/src/utils/merger.js +115 -0
- package/src/validator/backend-diagnostics.js +30 -0
- package/src/validator/blueprint-validator.js +276 -0
- package/src/validator/diagnostics.js +608 -0
- package/src/validator/pattern.js +28 -0
- package/test/auth-interceptor.js +84 -0
- package/test/auth-provision-checks.js +297 -0
- package/test/field-preferences.js +145 -0
- package/test/fixtures/field-profile.gdl +7 -0
- package/test/fixtures/mock.jdl +17 -0
- package/test/fixtures/mock.png +0 -0
- package/test/fixtures/performance/config.yml +33 -0
- package/test/fixtures/performance/large.gdl +199 -0
- package/test/fixtures/performance/small.gdl +7 -0
- package/test/fixtures/quality/custom.yml +22 -0
- package/test/fixtures/quality/go-duck.yml +22 -0
- package/test/fixtures/quality/jhipster.yml +30 -0
- package/test/fixtures/quality/representative.gdl +14 -0
- package/test/list-state.js +138 -0
- package/test/micro-functions-layout.js +95 -0
- package/test/performance-generation.js +32 -0
- package/test/pmb-preset.js +127 -0
- package/test/project-workflow.js +111 -0
- package/test/quality-features.js +250 -0
- package/test/verify-commands.js +83 -0
- package/test/verify.js +959 -0
- package/test-files/GDL/example.gdl +15 -0
- package/test-files/JDL/example.jdl +15 -0
- package/test-files/config/crudforge.config.yml +127 -0
- package/test-run.sh +272 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
<mat-drawer-container class="absolute h-full w-full bg-transparent">
|
|
2
|
+
<mat-drawer-content class="flex flex-col">
|
|
3
|
+
<div class="absolute inset-0 flex min-w-0 flex-col overflow-hidden">
|
|
4
|
+
|
|
5
|
+
<!-- Header (fixed, does not scroll) — PMB User Management layout -->
|
|
6
|
+
<div class="flex flex-0 flex-col border-b border-slate-200 bg-slate-100 p-6 pb-22 md:flex-row md:items-center md:justify-between md:px-10 md:pb-28 md:pt-12 dark:border-slate-800 dark:bg-slate-900/60">
|
|
7
|
+
<div class="min-w-0 flex-1">
|
|
8
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
9
|
+
<h2 class="truncate text-3xl font-extrabold leading-7 tracking-tight sm:leading-10 md:text-4xl">
|
|
10
|
+
User Management <span class="text-2xl font-bold text-primary-800 tabular-nums dark:text-primary-400 md:text-3xl">({{ totalItems }})</span>
|
|
11
|
+
</h2>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div class="mt-6 grid w-full grid-cols-2 gap-3 md:ml-4 md:mt-0 md:flex md:w-auto md:shrink-0 md:items-center">
|
|
16
|
+
<mat-form-field appearance="outline" subscriptSizing="dynamic" class="col-span-2 w-full md:w-64">
|
|
17
|
+
<mat-icon matPrefix svgIcon="search" class="mr-2 text-neutral-500"></mat-icon>
|
|
18
|
+
<input matInput placeholder="Search users..." [formControl]="searchControl" autocomplete="off" />
|
|
19
|
+
@if (searchControl.value) {
|
|
20
|
+
<button mat-icon-button matSuffix type="button" (click)="searchControl.setValue('')" aria-label="Clear search" class="flex h-8 w-8 min-w-0 items-center justify-center p-0">
|
|
21
|
+
<mat-icon svgIcon="x" class="text-neutral-500"></mat-icon>
|
|
22
|
+
</button>
|
|
23
|
+
}
|
|
24
|
+
</mat-form-field>
|
|
25
|
+
|
|
26
|
+
<button mat-stroked-button type="button" [matMenuTriggerFor]="columnPreferenceMenu" class="h-10 w-12 min-w-0 p-0" title="Column Preferences" matTooltip="Column Preferences" *ngIf="SCREEN | accessCtrl: 'open-columns'" data-screen-id="user-management" data-component-id="open-columns">
|
|
27
|
+
<span class="flex items-center justify-center">
|
|
28
|
+
<mat-icon svgIcon="menu" class="m-0"></mat-icon>
|
|
29
|
+
</span>
|
|
30
|
+
</button>
|
|
31
|
+
|
|
32
|
+
<button mat-stroked-button type="button" class="h-10 w-12 min-w-0 p-0" (click)="setViewMode(viewMode === 'table' ? 'card' : 'table')" [attr.aria-label]="viewMode === 'table' ? 'Switch to Card View' : 'Switch to Table View'" [matTooltip]="viewMode === 'table' ? 'Switch to Card View' : 'Switch to Table View'" *ngIf="SCREEN | accessCtrl: 'toggle-view'" data-screen-id="user-management" data-component-id="toggle-view">
|
|
33
|
+
<span class="flex items-center justify-center">
|
|
34
|
+
<mat-icon [svgIcon]="viewMode === 'table' ? 'credit-card' : 'table'" class="m-0"></mat-icon>
|
|
35
|
+
</span>
|
|
36
|
+
</button>
|
|
37
|
+
|
|
38
|
+
<% if (iam.roles !== false) { %>
|
|
39
|
+
<a mat-stroked-button class="flex h-10 w-12 min-w-0 items-center justify-center p-0" routerLink="roles" matTooltip="Role & Permission Matrix" *ngIf="SCREEN | accessCtrl: 'roles'" data-screen-id="user-management" data-component-id="roles">
|
|
40
|
+
<mat-icon svgIcon="shield-check" class="m-0"></mat-icon>
|
|
41
|
+
</a>
|
|
42
|
+
<% } %>
|
|
43
|
+
|
|
44
|
+
<button mat-flat-button color="primary" type="button" class="col-span-2 h-10 md:w-auto" (click)="openDrawer('create')" *ngIf="SCREEN | accessCtrl: 'create'" data-screen-id="user-management" data-component-id="create">
|
|
45
|
+
<mat-icon svgIcon="plus" class="mr-1"></mat-icon>
|
|
46
|
+
New
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Main panel -->
|
|
52
|
+
<div class="-mt-16 flex min-h-0 flex-auto flex-col overflow-hidden rounded-t-2xl border border-slate-200 bg-white shadow dark:border-slate-800 dark:bg-neutral-950" cdkScrollable>
|
|
53
|
+
<div class="flex min-h-0 flex-1 flex-col p-6 sm:p-10">
|
|
54
|
+
@if (viewMode === 'table') {
|
|
55
|
+
<div class="-m-6 min-h-0 flex-auto overflow-auto pb-[100px] sm:-m-10" cdkScrollable>
|
|
56
|
+
<table mat-table [dataSource]="tableRows" matSort (matSortChange)="onSortChange($event)" class="min-w-full">
|
|
57
|
+
|
|
58
|
+
<ng-container matColumnDef="state">
|
|
59
|
+
<th mat-header-cell *matHeaderCellDef class="w-28">State</th>
|
|
60
|
+
<td mat-cell *matCellDef="let element">
|
|
61
|
+
@if (isSearching) {
|
|
62
|
+
<div class="flex items-center gap-1.5">
|
|
63
|
+
<div class="h-8 w-8 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
64
|
+
<div class="h-8 w-8 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
65
|
+
</div>
|
|
66
|
+
} @else {
|
|
67
|
+
<div class="flex items-center gap-1.5">
|
|
68
|
+
<button type="button" mat-icon-button class="h-8 w-8" title="Pin row" aria-label="Pin row" [matTooltip]="isPinned(element.id) ? 'Unpin row' : 'Pin row'" (click)="togglePin(element)">
|
|
69
|
+
<mat-icon svgIcon="pin" [ngClass]="isPinned(element.id) ? 'text-red-500' : 'text-neutral-400'"></mat-icon>
|
|
70
|
+
</button>
|
|
71
|
+
<button type="button" mat-icon-button class="h-8 w-8" title="Set color flag" aria-label="Set color flag" matTooltip="Set color flag" [matMenuTriggerFor]="flagMenu" (click)="setFlagMenuRow(element)">
|
|
72
|
+
<mat-icon svgIcon="flag" [ngClass]="getFlagClass(element.id)"></mat-icon>
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
}
|
|
76
|
+
</td>
|
|
77
|
+
</ng-container>
|
|
78
|
+
|
|
79
|
+
<ng-container matColumnDef="username">
|
|
80
|
+
<th mat-header-cell *matHeaderCellDef mat-sort-header>Username</th>
|
|
81
|
+
<td mat-cell *matCellDef="let element">
|
|
82
|
+
@if (isSearching) {
|
|
83
|
+
<div class="h-4 w-3/4 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
84
|
+
} @else {
|
|
85
|
+
<button type="button" class="cursor-pointer text-left text-sm font-semibold text-primary-800 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300" title="Copy username" matTooltip="Copy username" (click)="copyText(element.username)" [innerHTML]="highlightText(formatUsername(element.username), searchControl.value)" *ngIf="SCREEN | accessCtrl: 'copy-username'; else usernameText" data-screen-id="user-management" data-component-id="copy-username"></button>
|
|
86
|
+
<ng-template #usernameText>
|
|
87
|
+
<span class="text-sm font-semibold text-primary-800 dark:text-primary-400" [innerHTML]="highlightText(formatUsername(element.username), searchControl.value)"></span>
|
|
88
|
+
</ng-template>
|
|
89
|
+
}
|
|
90
|
+
</td>
|
|
91
|
+
</ng-container>
|
|
92
|
+
|
|
93
|
+
<ng-container matColumnDef="email">
|
|
94
|
+
<th mat-header-cell *matHeaderCellDef mat-sort-header>Email</th>
|
|
95
|
+
<td mat-cell *matCellDef="let element">
|
|
96
|
+
@if (isSearching) {
|
|
97
|
+
<div class="h-4 w-2/3 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
98
|
+
} @else {
|
|
99
|
+
<button type="button" class="cursor-pointer text-left hover:text-primary" title="Copy email" matTooltip="Copy email" (click)="copyText(element.email)" [innerHTML]="highlightText(element.email, searchControl.value)" *ngIf="SCREEN | accessCtrl: 'copy-email'" data-screen-id="user-management" data-component-id="copy-email"></button>
|
|
100
|
+
}
|
|
101
|
+
</td>
|
|
102
|
+
</ng-container>
|
|
103
|
+
|
|
104
|
+
<ng-container matColumnDef="firstName">
|
|
105
|
+
<th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>
|
|
106
|
+
<td mat-cell *matCellDef="let element">
|
|
107
|
+
@if (isSearching) {
|
|
108
|
+
<div class="h-4 w-1/2 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
109
|
+
} @else {
|
|
110
|
+
<span [innerHTML]="highlightText(element.firstName, searchControl.value)"></span>
|
|
111
|
+
}
|
|
112
|
+
</td>
|
|
113
|
+
</ng-container>
|
|
114
|
+
|
|
115
|
+
<ng-container matColumnDef="lastName">
|
|
116
|
+
<th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>
|
|
117
|
+
<td mat-cell *matCellDef="let element">
|
|
118
|
+
@if (isSearching) {
|
|
119
|
+
<div class="h-4 w-1/2 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
120
|
+
} @else {
|
|
121
|
+
<span [innerHTML]="highlightText(element.lastName, searchControl.value)"></span>
|
|
122
|
+
}
|
|
123
|
+
</td>
|
|
124
|
+
</ng-container>
|
|
125
|
+
|
|
126
|
+
<ng-container matColumnDef="enabled">
|
|
127
|
+
<th mat-header-cell *matHeaderCellDef mat-sort-header class="w-40">Enabled</th>
|
|
128
|
+
<td mat-cell *matCellDef="let element">
|
|
129
|
+
@if (isSearching) {
|
|
130
|
+
<div class="h-8 w-14 animate-pulse rounded-md bg-slate-200 dark:bg-slate-700"></div>
|
|
131
|
+
} @else {
|
|
132
|
+
<div class="flex items-center gap-3" (click)="$event.stopPropagation()">
|
|
133
|
+
<label class="rocker-switch" [class.opacity-70]="isTogglingEnabled[element.id!]" [matTooltip]="element.enabled !== false ? 'Click to disable user' : 'Click to enable user'">
|
|
134
|
+
<input
|
|
135
|
+
type="checkbox"
|
|
136
|
+
title="Toggle user enabled state"
|
|
137
|
+
aria-label="Toggle user enabled state"
|
|
138
|
+
[checked]="element.enabled !== false"
|
|
139
|
+
[disabled]="!!isTogglingEnabled[element.id!]"
|
|
140
|
+
(click)="onToggleUserEnabled(element, element.enabled === false, $event)"
|
|
141
|
+
*ngIf="SCREEN | accessCtrl: 'toggle-enabled'"
|
|
142
|
+
data-screen-id="user-management"
|
|
143
|
+
data-component-id="toggle-enabled"
|
|
144
|
+
/>
|
|
145
|
+
<div class="rocker-button">
|
|
146
|
+
<div class="rocker-light"></div>
|
|
147
|
+
<div class="rocker-dots"></div>
|
|
148
|
+
<div class="rocker-characters"></div>
|
|
149
|
+
<div class="rocker-shine"></div>
|
|
150
|
+
<div class="rocker-shadow"></div>
|
|
151
|
+
</div>
|
|
152
|
+
</label>
|
|
153
|
+
@if (isTogglingEnabled[element.id!]) {
|
|
154
|
+
<svg class="h-3.5 w-3.5 animate-spin text-amber-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
155
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
156
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
157
|
+
</svg>
|
|
158
|
+
}
|
|
159
|
+
</div>
|
|
160
|
+
}
|
|
161
|
+
</td>
|
|
162
|
+
</ng-container>
|
|
163
|
+
|
|
164
|
+
<ng-container matColumnDef="actions" stickyEnd>
|
|
165
|
+
<th mat-header-cell *matHeaderCellDef class="w-40">Actions</th>
|
|
166
|
+
<td mat-cell *matCellDef="let element">
|
|
167
|
+
@if (isSearching) {
|
|
168
|
+
<div class="flex items-center gap-1">
|
|
169
|
+
<div class="h-8 w-8 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
170
|
+
<div class="h-8 w-8 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
171
|
+
<div class="h-8 w-8 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
172
|
+
</div>
|
|
173
|
+
} @else {
|
|
174
|
+
<div class="flex items-center gap-0.5">
|
|
175
|
+
<button mat-icon-button type="button" class="flex h-8 w-8 items-center justify-center text-primary-700 hover:bg-primary-50 dark:text-primary-400 dark:hover:bg-primary-500/10" (click)="openDrawer('view', element)" matTooltip="View User" title="View user account" *ngIf="SCREEN | accessCtrl: 'view'" data-screen-id="user-management" data-component-id="view">
|
|
176
|
+
<mat-icon svgIcon="eye"></mat-icon>
|
|
177
|
+
</button>
|
|
178
|
+
<button mat-icon-button type="button" class="flex h-8 w-8 items-center justify-center text-amber-600 hover:bg-amber-50 dark:text-amber-400 dark:hover:bg-amber-500/10" (click)="openDrawer('edit', element)" matTooltip="Edit User" title="Edit user" *ngIf="SCREEN | accessCtrl: 'edit'" data-screen-id="user-management" data-component-id="edit">
|
|
179
|
+
<mat-icon svgIcon="square-pen"></mat-icon>
|
|
180
|
+
</button>
|
|
181
|
+
<button mat-icon-button type="button" class="flex h-8 w-8 items-center justify-center text-sky-600 hover:bg-sky-50 dark:text-sky-400 dark:hover:bg-sky-500/10" (click)="changePassword(element)" matTooltip="Reset Password" title="Reset user password" *ngIf="SCREEN | accessCtrl: 'change-password'" data-screen-id="user-management" data-component-id="change-password">
|
|
182
|
+
<mat-icon svgIcon="key"></mat-icon>
|
|
183
|
+
</button>
|
|
184
|
+
<% if (iam.userAccess !== false) { %>
|
|
185
|
+
<button mat-icon-button type="button" class="flex h-8 w-8 items-center justify-center text-primary-700 hover:bg-primary-50 dark:text-primary-400 dark:hover:bg-primary-500/10" (click)="assignRoleMatrix(element)" matTooltip="Assign Role Matrix" title="Open role matrix" *ngIf="SCREEN | accessCtrl: 'access'" data-screen-id="user-management" data-component-id="access">
|
|
186
|
+
<mat-icon svgIcon="shield-check"></mat-icon>
|
|
187
|
+
</button>
|
|
188
|
+
<% } %>
|
|
189
|
+
<% if (iam.userPermissions !== false) { %>
|
|
190
|
+
<button mat-icon-button type="button" class="flex h-8 w-8 items-center justify-center text-indigo-600 hover:bg-indigo-50 dark:text-indigo-400 dark:hover:bg-indigo-500/10" (click)="assignUserMatrix(element)" matTooltip="Assign User Matrix" title="Open user permissions matrix" *ngIf="SCREEN | accessCtrl: 'permissions'" data-screen-id="user-management" data-component-id="permissions">
|
|
191
|
+
<mat-icon svgIcon="lock-keyhole"></mat-icon>
|
|
192
|
+
</button>
|
|
193
|
+
<% } %>
|
|
194
|
+
<button mat-icon-button type="button" class="flex h-8 w-8 items-center justify-center text-rose-600 hover:bg-rose-50 dark:text-rose-400 dark:hover:bg-rose-500/10" (click)="remove(element)" matTooltip="Delete User" title="Delete user" *ngIf="SCREEN | accessCtrl: 'delete'" data-screen-id="user-management" data-component-id="delete">
|
|
195
|
+
<mat-icon svgIcon="trash"></mat-icon>
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
}
|
|
199
|
+
</td>
|
|
200
|
+
</ng-container>
|
|
201
|
+
|
|
202
|
+
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
|
203
|
+
<tr mat-row *matRowDef="let row; columns: displayedColumns" [ngClass]="isSearching ? '' : getRowClass(row)"></tr>
|
|
204
|
+
<tr class="mat-row" *matNoDataRow>
|
|
205
|
+
<td class="mat-cell" [attr.colspan]="displayedColumns.length">No users found.</td>
|
|
206
|
+
</tr>
|
|
207
|
+
</table>
|
|
208
|
+
</div>
|
|
209
|
+
} @else {
|
|
210
|
+
<div class="-m-6 min-h-0 flex-auto overflow-y-auto pb-[100px] sm:-m-10">
|
|
211
|
+
<div class="grid grid-cols-1 gap-6 p-6 sm:grid-cols-2 sm:p-10 xl:grid-cols-3">
|
|
212
|
+
@if (isSearching) {
|
|
213
|
+
@for (row of skeletonRows; track row) {
|
|
214
|
+
<div class="rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-neutral-950">
|
|
215
|
+
<div class="mb-4 h-5 w-1/2 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
216
|
+
<div class="mb-2 h-4 w-3/4 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
217
|
+
<div class="mb-6 h-4 w-2/3 animate-pulse rounded bg-slate-200 dark:bg-slate-700"></div>
|
|
218
|
+
<div class="flex gap-2">
|
|
219
|
+
<div class="h-9 w-9 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
220
|
+
<div class="h-9 w-9 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
221
|
+
<div class="h-9 w-9 animate-pulse rounded-full bg-slate-200 dark:bg-slate-700"></div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
}
|
|
225
|
+
} @else {
|
|
226
|
+
@for (user of users; track user.id || user.username) {
|
|
227
|
+
<article class="group flex h-full min-h-64 flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm transition duration-200 hover:-translate-y-0.5 hover:shadow-lg dark:border-slate-800 dark:bg-neutral-900">
|
|
228
|
+
<div class="h-1 bg-gradient-to-r" [class.from-primary-500]="user.enabled !== false" [class.to-primary-400]="user.enabled !== false" [class.from-slate-400]="user.enabled === false" [class.to-slate-300]="user.enabled === false"></div>
|
|
229
|
+
|
|
230
|
+
<div class="flex items-start gap-3 px-5 pb-4 pt-5">
|
|
231
|
+
<div
|
|
232
|
+
class="flex h-12 w-12 shrink-0 items-center justify-center rounded-xl text-lg font-bold ring-1 ring-inset"
|
|
233
|
+
[class.bg-primary-50]="user.enabled !== false"
|
|
234
|
+
[class.text-primary-700]="user.enabled !== false"
|
|
235
|
+
[class.ring-primary-100]="user.enabled !== false"
|
|
236
|
+
[class.bg-slate-100]="user.enabled === false"
|
|
237
|
+
[class.text-slate-600]="user.enabled === false"
|
|
238
|
+
[class.ring-slate-200]="user.enabled === false"
|
|
239
|
+
>
|
|
240
|
+
{{ displayName(user).charAt(0).toUpperCase() }}
|
|
241
|
+
</div>
|
|
242
|
+
<div class="min-w-0 flex-1">
|
|
243
|
+
<h3 class="truncate text-base font-semibold text-slate-900 dark:text-white">{{ displayName(user) }}</h3>
|
|
244
|
+
<p class="mt-0.5 truncate text-sm text-slate-500 dark:text-slate-400">{{ user.username || 'No username' }}</p>
|
|
245
|
+
</div>
|
|
246
|
+
<span
|
|
247
|
+
class="inline-flex shrink-0 items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-semibold"
|
|
248
|
+
[ngClass]="user.enabled !== false ? 'bg-primary-50 text-primary-700 dark:bg-primary-500/10 dark:text-primary-300' : 'bg-slate-100 text-slate-600 dark:bg-slate-800 dark:text-slate-300'"
|
|
249
|
+
>
|
|
250
|
+
<span class="h-1.5 w-1.5 rounded-full" [class.bg-primary-500]="user.enabled !== false" [class.bg-slate-400]="user.enabled === false"></span>
|
|
251
|
+
{{ user.enabled !== false ? 'Enabled' : 'Disabled' }}
|
|
252
|
+
</span>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div class="mx-5 border-t border-slate-100 dark:border-slate-800"></div>
|
|
256
|
+
|
|
257
|
+
<div class="grid flex-1 gap-3 px-5 py-4">
|
|
258
|
+
<div class="flex min-w-0 items-center gap-3">
|
|
259
|
+
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-sky-50 text-sky-600 dark:bg-sky-500/10 dark:text-sky-400">
|
|
260
|
+
<mat-icon svgIcon="mail" class="icon-size-4"></mat-icon>
|
|
261
|
+
</div>
|
|
262
|
+
<div class="min-w-0">
|
|
263
|
+
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Email</p>
|
|
264
|
+
@if (user.email) {
|
|
265
|
+
<button type="button" class="block max-w-full truncate text-left text-sm font-medium text-slate-700 hover:text-primary dark:text-slate-200" title="Copy email" (click)="copyText(user.email)">{{ user.email }}</button>
|
|
266
|
+
} @else {
|
|
267
|
+
<p class="truncate text-sm text-slate-500">No email address</p>
|
|
268
|
+
}
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
<div class="flex min-w-0 items-center gap-3">
|
|
272
|
+
<div class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-violet-50 text-violet-600 dark:bg-violet-500/10 dark:text-violet-400">
|
|
273
|
+
<mat-icon svgIcon="phone" class="icon-size-4"></mat-icon>
|
|
274
|
+
</div>
|
|
275
|
+
<div class="min-w-0">
|
|
276
|
+
<p class="text-xs font-medium uppercase tracking-wide text-slate-400">Username / phone</p>
|
|
277
|
+
<p class="truncate text-sm font-medium text-slate-700 dark:text-slate-200">{{ user.username || 'Not provided' }}</p>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<div class="flex items-center justify-between gap-3 border-t border-slate-100 bg-slate-50/70 px-3 py-2.5 dark:border-slate-800 dark:bg-slate-900/60">
|
|
283
|
+
<button mat-button type="button" class="!min-w-0 !px-2.5 !text-primary-700 dark:!text-primary-400" (click)="openDrawer('view', user)" *ngIf="SCREEN | accessCtrl: 'view'" data-screen-id="user-management" data-component-id="view">
|
|
284
|
+
<mat-icon svgIcon="eye" class="icon-size-4"></mat-icon>
|
|
285
|
+
<span class="ml-1.5">View</span>
|
|
286
|
+
</button>
|
|
287
|
+
<div class="flex items-center gap-0.5">
|
|
288
|
+
<button mat-icon-button type="button" class="h-8 w-8 text-amber-600" title="Edit" (click)="openDrawer('edit', user)" *ngIf="SCREEN | accessCtrl: 'edit'" data-screen-id="user-management" data-component-id="edit">
|
|
289
|
+
<mat-icon svgIcon="square-pen" class="icon-size-4"></mat-icon>
|
|
290
|
+
</button>
|
|
291
|
+
<button mat-icon-button type="button" class="h-8 w-8 text-sky-600" title="Reset password" (click)="changePassword(user)" *ngIf="SCREEN | accessCtrl: 'change-password'" data-screen-id="user-management" data-component-id="change-password">
|
|
292
|
+
<mat-icon svgIcon="key" class="icon-size-4"></mat-icon>
|
|
293
|
+
</button>
|
|
294
|
+
<% if (iam.userAccess !== false) { %>
|
|
295
|
+
<button mat-icon-button type="button" class="h-8 w-8 text-primary-700 dark:text-primary-400" title="Assign roles" (click)="assignRoleMatrix(user)" *ngIf="SCREEN | accessCtrl: 'access'" data-screen-id="user-management" data-component-id="access">
|
|
296
|
+
<mat-icon svgIcon="shield-check" class="icon-size-4"></mat-icon>
|
|
297
|
+
</button>
|
|
298
|
+
<% } %>
|
|
299
|
+
<% if (iam.userPermissions !== false) { %>
|
|
300
|
+
<button mat-icon-button type="button" class="h-8 w-8 text-indigo-600 dark:text-indigo-400" title="Assign permissions" (click)="assignUserMatrix(user)" *ngIf="SCREEN | accessCtrl: 'permissions'" data-screen-id="user-management" data-component-id="permissions">
|
|
301
|
+
<mat-icon svgIcon="lock-keyhole" class="icon-size-4"></mat-icon>
|
|
302
|
+
</button>
|
|
303
|
+
<% } %>
|
|
304
|
+
<button mat-icon-button type="button" class="h-8 w-8 text-rose-600" title="Delete user" (click)="remove(user)" *ngIf="SCREEN | accessCtrl: 'delete'" data-screen-id="user-management" data-component-id="delete">
|
|
305
|
+
<mat-icon svgIcon="trash" class="icon-size-4"></mat-icon>
|
|
306
|
+
</button>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</article>
|
|
310
|
+
} @empty {
|
|
311
|
+
<div class="col-span-full rounded-2xl border border-dashed border-slate-300 bg-slate-50 px-6 py-16 text-center text-slate-500">No users found.</div>
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
}
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<mat-paginator
|
|
320
|
+
class="sticky bottom-0 z-10 flex-0 shrink-0 border-t border-slate-200 bg-white [&_.mat-mdc-paginator-container]:justify-start dark:border-slate-800 dark:bg-neutral-950"
|
|
321
|
+
[length]="totalItems"
|
|
322
|
+
[pageIndex]="pageIndex"
|
|
323
|
+
[pageSize]="pageSize"
|
|
324
|
+
[pageSizeOptions]="pageSizeOptions"
|
|
325
|
+
(page)="onPage($event)"
|
|
326
|
+
></mat-paginator>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</mat-drawer-content>
|
|
330
|
+
|
|
331
|
+
<mat-menu #flagMenu="matMenu">
|
|
332
|
+
@for (option of rowFlagOptions; track option.key) {
|
|
333
|
+
<button mat-menu-item type="button" (click)="setFlagColor(option.key)">
|
|
334
|
+
<mat-icon svgIcon="flag" class="mr-3" [ngClass]="option.iconClass"></mat-icon>
|
|
335
|
+
<span>{{ option.label }}</span>
|
|
336
|
+
</button>
|
|
337
|
+
}
|
|
338
|
+
<button mat-menu-item type="button" (click)="setFlagColor(null)">
|
|
339
|
+
<mat-icon svgIcon="x" class="mr-3 text-neutral-500"></mat-icon>
|
|
340
|
+
<span>Clear Flag</span>
|
|
341
|
+
</button>
|
|
342
|
+
</mat-menu>
|
|
343
|
+
|
|
344
|
+
<mat-menu #columnPreferenceMenu="matMenu" class="min-w-72">
|
|
345
|
+
<div class="px-4 py-3">
|
|
346
|
+
<div class="text-sm font-semibold">Column Preferences</div>
|
|
347
|
+
<div class="text-secondary text-xs">Choose which columns appear in the table.</div>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="max-h-80 overflow-y-auto px-2 pb-2">
|
|
350
|
+
@for (column of allColumns; track column.key) {
|
|
351
|
+
<div class="flex items-center justify-between gap-3 rounded-lg px-3 py-2 hover:bg-neutral-100 dark:hover:bg-white/5">
|
|
352
|
+
<span class="text-sm font-medium">{{ column.label }}</span>
|
|
353
|
+
<mat-slide-toggle [checked]="isColumnVisible(column.key)" (click)="$event.stopPropagation()" (change)="toggleColumn(column.key)"></mat-slide-toggle>
|
|
354
|
+
</div>
|
|
355
|
+
}
|
|
356
|
+
</div>
|
|
357
|
+
</mat-menu>
|
|
358
|
+
|
|
359
|
+
<mat-drawer #drawer mode="over" position="end" class="cforge-form-drawer w-full max-w-3xl">
|
|
360
|
+
<div class="flex h-full w-full min-w-0 flex-col bg-card" [class.p-0]="drawerMode === 'view'" [class.p-6]="drawerMode !== 'view'">
|
|
361
|
+
@if (drawer.opened && drawerMode !== 'view') {
|
|
362
|
+
<app-user-form class="block h-full w-full min-w-0 flex-1" [user]="selectedUser" [mode]="drawerMode === 'edit' ? 'edit' : 'create'" (saved)="onFormSaved()" (cancelled)="closeDrawer()"></app-user-form>
|
|
363
|
+
}
|
|
364
|
+
@if (drawer.opened && drawerMode === 'view') {
|
|
365
|
+
<app-user-detail class="block h-full w-full min-w-0 flex-1 overflow-auto" [userId]="selectedUser?.id || null" (closed)="closeDrawer()"></app-user-detail>
|
|
366
|
+
}
|
|
367
|
+
</div>
|
|
368
|
+
</mat-drawer>
|
|
369
|
+
</mat-drawer-container>
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex: 1 1 auto;
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
min-width: 0;
|
|
7
|
+
min-height: 0;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
mat-drawer-container {
|
|
12
|
+
background: transparent;
|
|
13
|
+
min-height: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
mat-drawer {
|
|
17
|
+
background-color: var(--fuse-bg-card);
|
|
18
|
+
border-left: 1px solid var(--fuse-border);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.icon-size-4 {
|
|
22
|
+
width: 16px;
|
|
23
|
+
height: 16px;
|
|
24
|
+
font-size: 16px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Table chrome — match PMB / auditor staff profile */
|
|
28
|
+
.mat-mdc-header-row {
|
|
29
|
+
background: var(--mat-sys-surface-bright) !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.mat-mdc-header-cell {
|
|
33
|
+
background: var(--mat-sys-surface-bright) !important;
|
|
34
|
+
z-index: 10 !important;
|
|
35
|
+
border-bottom: 1px solid var(--mat-sys-outline-variant) !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
tr:nth-child(even) {
|
|
39
|
+
background: var(--mat-sys-surface-container-low) !important;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
tr:nth-child(odd) {
|
|
43
|
+
background: var(--mat-sys-surface-bright) !important;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
table {
|
|
47
|
+
width: 100%;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
td.mat-mdc-cell,
|
|
51
|
+
th.mat-mdc-header-cell {
|
|
52
|
+
padding: 12px 16px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
th.mat-column-actions.mat-mdc-table-sticky,
|
|
56
|
+
td.mat-column-actions.mat-mdc-table-sticky {
|
|
57
|
+
z-index: 11 !important;
|
|
58
|
+
border-left: 1px solid var(--mat-sys-outline-variant);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
tr:nth-child(odd) th.mat-column-actions.mat-mdc-table-sticky,
|
|
62
|
+
tr:nth-child(odd) td.mat-column-actions.mat-mdc-table-sticky {
|
|
63
|
+
background: var(--mat-sys-surface-bright) !important;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
tr:nth-child(even) td.mat-column-actions.mat-mdc-table-sticky {
|
|
67
|
+
background: var(--mat-sys-surface-container-low) !important;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.mat-mdc-header-cell.mat-column-actions.mat-mdc-table-sticky {
|
|
71
|
+
z-index: 12 !important;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.bg-card {
|
|
75
|
+
background-color: var(--fuse-bg-card);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* 3D rocker switch (Uiverse.io by Nawsome) — PMB enabled column */
|
|
79
|
+
.rocker-switch {
|
|
80
|
+
display: inline-block;
|
|
81
|
+
background-color: black;
|
|
82
|
+
width: 32px;
|
|
83
|
+
height: 42px;
|
|
84
|
+
box-shadow:
|
|
85
|
+
0 0 4px 1px rgba(0, 0, 0, 0.3),
|
|
86
|
+
0 0 1px 1px black,
|
|
87
|
+
inset 0 1px 1px -1px white,
|
|
88
|
+
inset 0 0 1px 3px #47434c,
|
|
89
|
+
inset 0 0 1px 5px black;
|
|
90
|
+
border-radius: 5px;
|
|
91
|
+
padding: 4px;
|
|
92
|
+
perspective: 160px;
|
|
93
|
+
vertical-align: middle;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
user-select: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.rocker-switch input {
|
|
99
|
+
display: none;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.rocker-switch input:checked + .rocker-button {
|
|
103
|
+
transform: translateZ(5px) rotateX(25deg);
|
|
104
|
+
box-shadow: 0 -4px 10px #ff1818;
|
|
105
|
+
background: linear-gradient(#980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.rocker-switch input:checked + .rocker-button .rocker-light {
|
|
109
|
+
animation: flicker 0.2s infinite 0.3s;
|
|
110
|
+
opacity: 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.rocker-switch input:checked + .rocker-button .rocker-shine {
|
|
114
|
+
opacity: 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.rocker-switch input:checked + .rocker-button .rocker-shadow {
|
|
118
|
+
opacity: 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.rocker-switch .rocker-button {
|
|
122
|
+
display: block;
|
|
123
|
+
transition: all 0.3s cubic-bezier(1, 0, 1, 1);
|
|
124
|
+
transform-origin: center center -5px;
|
|
125
|
+
transform: translateZ(5px) rotateX(-25deg);
|
|
126
|
+
transform-style: preserve-3d;
|
|
127
|
+
height: 100%;
|
|
128
|
+
position: relative;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
background: linear-gradient(#3a3a3a 0%, #222222 30%, #222222 70%, #3a3a3a 100%);
|
|
131
|
+
background-repeat: no-repeat;
|
|
132
|
+
border-radius: 3px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.rocker-switch .rocker-button::before {
|
|
136
|
+
content: "";
|
|
137
|
+
background:
|
|
138
|
+
linear-gradient(rgba(255, 255, 255, 0.6) 10%, rgba(255, 255, 255, 0.2) 30%, #444 75%, #111) 50% 50% / 97% 97%,
|
|
139
|
+
#333;
|
|
140
|
+
background-repeat: no-repeat;
|
|
141
|
+
width: 100%;
|
|
142
|
+
height: 10px;
|
|
143
|
+
transform-origin: top;
|
|
144
|
+
transform: rotateX(-90deg);
|
|
145
|
+
position: absolute;
|
|
146
|
+
top: 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.rocker-switch .rocker-button::after {
|
|
150
|
+
content: "";
|
|
151
|
+
background-image: linear-gradient(#333, #111);
|
|
152
|
+
width: 100%;
|
|
153
|
+
height: 10px;
|
|
154
|
+
transform-origin: top;
|
|
155
|
+
transform: translateY(10px) rotateX(-90deg);
|
|
156
|
+
position: absolute;
|
|
157
|
+
bottom: 0;
|
|
158
|
+
box-shadow:
|
|
159
|
+
0 10px 3px 0 black,
|
|
160
|
+
0 15px 5px 0 rgba(0, 0, 0, 0.5);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.rocker-switch input:checked + .rocker-button::before {
|
|
164
|
+
background:
|
|
165
|
+
linear-gradient(rgba(255, 255, 255, 0.8) 10%, rgba(255, 255, 255, 0.3) 30%, #650000 75%, #320000) 50% 50% / 97% 97%,
|
|
166
|
+
#b10000;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.rocker-switch input:checked + .rocker-button::after {
|
|
170
|
+
background-image: linear-gradient(#650000, #320000);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.rocker-switch .rocker-light {
|
|
174
|
+
opacity: 0;
|
|
175
|
+
animation: light-off 1s;
|
|
176
|
+
position: absolute;
|
|
177
|
+
width: 100%;
|
|
178
|
+
height: 100%;
|
|
179
|
+
background-image: radial-gradient(#ffc97e, #ff1818 40%, transparent 70%);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.rocker-switch .rocker-dots {
|
|
183
|
+
position: absolute;
|
|
184
|
+
width: 100%;
|
|
185
|
+
height: 100%;
|
|
186
|
+
background-image: radial-gradient(transparent 30%, rgba(0, 0, 0, 0.5) 70%);
|
|
187
|
+
background-size: 3px 3px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.rocker-switch .rocker-characters {
|
|
191
|
+
position: absolute;
|
|
192
|
+
width: 100%;
|
|
193
|
+
height: 100%;
|
|
194
|
+
background:
|
|
195
|
+
linear-gradient(white, white) 50% 18% / 6% 22%,
|
|
196
|
+
radial-gradient(circle, transparent 48%, white 50%, white 68%, transparent 70%) 50% 80% / 34% 24%;
|
|
197
|
+
background-repeat: no-repeat;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.rocker-switch .rocker-shine {
|
|
201
|
+
transition: all 0.3s cubic-bezier(1, 0, 1, 1);
|
|
202
|
+
opacity: 0.3;
|
|
203
|
+
position: absolute;
|
|
204
|
+
width: 100%;
|
|
205
|
+
height: 100%;
|
|
206
|
+
background:
|
|
207
|
+
linear-gradient(white, transparent 3%) 50% 50% / 97% 97%,
|
|
208
|
+
linear-gradient(rgba(255, 255, 255, 0.5), transparent 50%, transparent 80%, rgba(255, 255, 255, 0.5)) 50% 50% / 97%
|
|
209
|
+
97%;
|
|
210
|
+
background-repeat: no-repeat;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.rocker-switch .rocker-shadow {
|
|
214
|
+
transition: all 0.3s cubic-bezier(1, 0, 1, 1);
|
|
215
|
+
opacity: 1;
|
|
216
|
+
position: absolute;
|
|
217
|
+
width: 100%;
|
|
218
|
+
height: 100%;
|
|
219
|
+
background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8));
|
|
220
|
+
background-repeat: no-repeat;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@keyframes flicker {
|
|
224
|
+
0% {
|
|
225
|
+
opacity: 1;
|
|
226
|
+
}
|
|
227
|
+
80% {
|
|
228
|
+
opacity: 0.8;
|
|
229
|
+
}
|
|
230
|
+
100% {
|
|
231
|
+
opacity: 1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@keyframes light-off {
|
|
236
|
+
0% {
|
|
237
|
+
opacity: 1;
|
|
238
|
+
}
|
|
239
|
+
80% {
|
|
240
|
+
opacity: 0;
|
|
241
|
+
}
|
|
242
|
+
}
|