From dde2e8aebf07b910ef0a20484fad941ca52b36a2 Mon Sep 17 00:00:00 2001 From: OnlyPapy98 Date: Tue, 16 Dec 2025 14:20:02 +0100 Subject: [PATCH] first commit --- .editorconfig | 17 + .gitignore | 45 + .postcssrc.json | 5 + .vscode/extensions.json | 4 + .vscode/launch.json | 20 + .vscode/tasks.json | 42 + README.md | 59 ++ angular.json | 94 ++ components.json | 13 + package.json | 61 ++ public/assets/images/avatar.svg | 5 + public/assets/logos/pmu_logo.png | Bin 0 -> 35379 bytes public/assets/logos/pmu_logo_dark.png | Bin 0 -> 464359 bytes public/assets/logos/pmu_logo_light.png | Bin 0 -> 832899 bytes public/favicon.ico | Bin 0 -> 15406 bytes scripts/init-permissions.js | 200 ++++ scripts/init-permissions.ts | 127 +++ src/app/app.config.ts | 22 + src/app/app.css | 0 src/app/app.html | 2 + src/app/app.routes.ts | 14 + src/app/app.spec.ts | 25 + src/app/app.ts | 13 + src/app/auth/auth-layout/auth-layout.css | 3 + src/app/auth/auth-layout/auth-layout.html | 74 ++ src/app/auth/auth-layout/auth-layout.spec.ts | 23 + src/app/auth/auth-layout/auth-layout.ts | 16 + src/app/auth/auth-module.ts | 26 + src/app/auth/auth-routing-module.ts | 24 + src/app/auth/pages/login/login.css | 0 src/app/auth/pages/login/login.html | 84 ++ src/app/auth/pages/login/login.spec.ts | 23 + src/app/auth/pages/login/login.ts | 47 + src/app/core/core-module.ts | 17 + src/app/core/guards/auth-guard.spec.ts | 17 + src/app/core/guards/auth-guard.ts | 9 + src/app/core/guards/role-guard.ts | 33 + .../interceptors/api-prefix-interceptor.ts | 32 + .../auth-token-interceptor.spec.ts | 17 + .../interceptors/auth-token-interceptor.ts | 14 + .../http-error-interceptor.spec.ts | 17 + .../interceptors/http-error-interceptor.ts | 24 + src/app/core/interfaces/agent-limit.ts | 23 + src/app/core/interfaces/agent.ts | 65 ++ src/app/core/interfaces/course.ts | 58 ++ src/app/core/interfaces/hippodrome.ts | 13 + src/app/core/interfaces/menu-item.ts | 7 + src/app/core/interfaces/report.ts | 26 + src/app/core/interfaces/resultat.ts | 58 ++ src/app/core/interfaces/reunion.ts | 21 + src/app/core/interfaces/role.ts | 14 + src/app/core/interfaces/tpe.ts | 27 + src/app/core/interfaces/user.ts | 39 + src/app/core/mocks/agent-limit.mocks.ts | 38 + src/app/core/mocks/agent.mocks.ts | 65 ++ src/app/core/mocks/course.mocks.ts | 197 ++++ src/app/core/mocks/hippodrome.mocks.ts | 421 +++++++++ src/app/core/mocks/report.mocks.ts | 110 +++ src/app/core/mocks/reunion.mocks.ts | 61 ++ src/app/core/mocks/role.mocks.ts | 144 +++ src/app/core/mocks/tpe.mocks.ts | 36 + src/app/core/mocks/user.mocks.ts | 69 ++ src/app/core/services/agent-family-member.ts | 236 +++++ src/app/core/services/agent-limit.ts | 335 +++++++ src/app/core/services/agent.ts | 484 ++++++++++ src/app/core/services/auth.spec.ts | 16 + src/app/core/services/auth.ts | 103 +++ src/app/core/services/course-sample.spec.ts | 16 + src/app/core/services/course-sample.ts | 38 + src/app/core/services/course.spec.ts | 16 + src/app/core/services/course.ts | 874 ++++++++++++++++++ src/app/core/services/hippodrome.spec.ts | 16 + src/app/core/services/hippodrome.ts | 547 +++++++++++ src/app/core/services/non-partant.ts | 40 + src/app/core/services/report.ts | 76 ++ src/app/core/services/resultat.ts | 282 ++++++ src/app/core/services/reunion.spec.ts | 16 + src/app/core/services/reunion.ts | 537 +++++++++++ src/app/core/services/role.ts | 283 ++++++ src/app/core/services/theme.spec.ts | 16 + src/app/core/services/theme.ts | 90 ++ src/app/core/services/tpe.ts | 467 ++++++++++ src/app/core/services/user.ts | 162 ++++ src/app/dashboard/dashboard-module.ts | 36 + src/app/dashboard/dashboard-routing-module.ts | 67 ++ src/app/dashboard/layout/layout.css | 3 + src/app/dashboard/layout/layout.html | 182 ++++ src/app/dashboard/layout/layout.spec.ts | 23 + src/app/dashboard/layout/layout.ts | 119 +++ src/app/dashboard/pages/agents/agents.html | 405 ++++++++ src/app/dashboard/pages/agents/agents.ts | 483 ++++++++++ src/app/dashboard/pages/courses/courses.css | 0 src/app/dashboard/pages/courses/courses.html | 171 ++++ .../dashboard/pages/courses/courses.spec.ts | 23 + src/app/dashboard/pages/courses/courses.ts | 554 +++++++++++ .../dashboard/pages/hippodrome/hippodrome.css | 0 .../pages/hippodrome/hippodrome.html | 132 +++ .../pages/hippodrome/hippodrome.spec.ts | 23 + .../dashboard/pages/hippodrome/hippodrome.ts | 204 ++++ src/app/dashboard/pages/limits/limits.html | 63 ++ src/app/dashboard/pages/limits/limits.ts | 294 ++++++ src/app/dashboard/pages/main/main.css | 0 src/app/dashboard/pages/main/main.html | 333 +++++++ src/app/dashboard/pages/main/main.spec.ts | 23 + src/app/dashboard/pages/main/main.ts | 257 +++++ src/app/dashboard/pages/profile/profile.html | 121 +++ src/app/dashboard/pages/profile/profile.ts | 75 ++ .../pages/report-courses/report-detail.html | 114 +++ .../pages/report-courses/report-detail.ts | 132 +++ .../pages/report-courses/report-list.html | 19 + .../pages/report-courses/report-list.ts | 61 ++ src/app/dashboard/pages/reunion/reunion.css | 0 src/app/dashboard/pages/reunion/reunion.html | 87 ++ .../dashboard/pages/reunion/reunion.spec.ts | 23 + src/app/dashboard/pages/reunion/reunion.ts | 234 +++++ src/app/dashboard/pages/roles/roles.html | 111 +++ src/app/dashboard/pages/roles/roles.ts | 241 +++++ src/app/dashboard/pages/tpe/tpe.html | 194 ++++ src/app/dashboard/pages/tpe/tpe.ts | 487 ++++++++++ src/app/dashboard/pages/users/users.html | 41 + src/app/dashboard/pages/users/users.ts | 198 ++++ .../accordion/accordion-item.component.ts | 76 ++ .../accordion/accordion.component.ts | 85 ++ .../alert-dialog/alert-dialog-ref.ts | 104 +++ .../alert-dialog/alert-dialog.component.html | 35 + .../alert-dialog/alert-dialog.component.ts | 154 +++ .../alert-dialog/alert-dialog.service.ts | 144 +++ .../alert-dialog/alert-dialog.variants.ts | 16 + .../components/alert/alert.component.ts | 50 + .../shared/components/alert/alert.variants.ts | 23 + .../components/avatar/avatar.component.ts | 127 +++ .../components/avatar/avatar.variants.ts | 65 ++ .../components/badge/badge.component.ts | 26 + .../shared/components/badge/badge.variants.ts | 24 + .../breadcrumb/breadcrumb.component.ts | 159 ++++ .../breadcrumb/breadcrumb.module.ts | 27 + .../breadcrumb/breadcrumb.variants.ts | 111 +++ .../components/button/button.component.ts | 49 + .../components/button/button.variants.ts | 40 + .../components/calendar/calendar.component.ts | 591 ++++++++++++ .../components/calendar/calendar.variants.ts | 105 +++ .../shared/components/card/card.component.ts | 55 ++ .../shared/components/card/card.variants.ts | 19 + .../components/checkbox/checkbox.component.ts | 83 ++ .../components/checkbox/checkbox.variants.ts | 42 + .../components/combobox/combobox.component.ts | 407 ++++++++ .../components/combobox/combobox.variants.ts | 18 + .../command/command-divider.component.ts | 39 + .../command/command-empty.component.ts | 47 + .../command/command-input.component.ts | 173 ++++ .../command/command-json.component.ts | 286 ++++++ .../command/command-list.component.ts | 24 + .../command/command-option-group.component.ts | 59 ++ .../command/command-option.component.ts | 104 +++ .../components/command/command.component.ts | 257 +++++ .../components/command/command.module.ts | 28 + .../components/command/command.variants.ts | 71 ++ .../string-template-outlet.directive.ts | 83 ++ .../components/data-table/data-table.css | 5 + .../components/data-table/data-table.html | 245 +++++ .../components/data-table/data-table.spec.ts | 23 + .../components/data-table/data-table.ts | 265 ++++++ .../date-picker/date-picker.component.ts | 132 +++ .../date-picker/date-picker.variants.ts | 23 + .../shared/components/dialog/dialog-ref.ts | 89 ++ .../components/dialog/dialog.component.ts | 172 ++++ .../components/dialog/dialog.service.ts | 99 ++ .../components/dialog/dialog.variants.ts | 6 + .../components/divider/divider.component.ts | 34 + .../components/divider/divider.variants.ts | 54 ++ .../dropdown/dropdown-item.component.ts | 56 ++ .../dropdown/dropdown-label.component.ts | 31 + .../dropdown-menu-content.component.ts | 27 + .../dropdown/dropdown-shortcut.component.ts | 22 + .../dropdown/dropdown-trigger.directive.ts | 101 ++ .../components/dropdown/dropdown.component.ts | 279 ++++++ .../components/dropdown/dropdown.module.ts | 24 + .../components/dropdown/dropdown.service.ts | 203 ++++ .../components/dropdown/dropdown.variants.ts | 40 + .../components/empty/empty.component.ts | 66 ++ .../shared/components/empty/empty.variants.ts | 16 + .../shared/components/form/form.component.ts | 92 ++ src/app/shared/components/form/form.module.ts | 11 + .../shared/components/form/form.variants.ts | 32 + .../input-group/input-group.component.ts | 136 +++ .../input-group/input-group.variants.ts | 151 +++ .../components/input/input.directive.ts | 29 + .../shared/components/input/input.variants.ts | 33 + .../components/layout/content.component.ts | 27 + .../components/layout/footer.component.ts | 25 + .../components/layout/header.component.ts | 25 + .../components/layout/layout.component.ts | 45 + .../shared/components/layout/layout.module.ts | 15 + .../components/layout/layout.variants.ts | 48 + .../components/layout/sidebar.component.ts | 127 +++ .../components/loader/loader.component.ts | 55 ++ .../components/loader/loader.variants.ts | 15 + .../components/menu/menu-content.directive.ts | 21 + .../components/menu/menu-item.directive.ts | 85 ++ .../components/menu/menu-manager.service.ts | 30 + .../shared/components/menu/menu-positions.ts | 210 +++++ .../shared/components/menu/menu.directive.ts | 186 ++++ src/app/shared/components/menu/menu.module.ts | 13 + .../shared/components/menu/menu.variants.ts | 23 + src/app/shared/components/modal/modal.css | 0 src/app/shared/components/modal/modal.html | 35 + src/app/shared/components/modal/modal.spec.ts | 23 + src/app/shared/components/modal/modal.ts | 56 ++ .../components/mode-toggle/mode-toggle.css | 0 .../components/mode-toggle/mode-toggle.html | 13 + .../mode-toggle/mode-toggle.spec.ts | 23 + .../components/mode-toggle/mode-toggle.ts | 25 + .../pagination/pagination.component.ts | 245 +++++ .../pagination/pagination.module.ts | 27 + .../pagination/pagination.variants.ts | 19 + .../shared/components/paginator/paginator.css | 0 .../components/paginator/paginator.html | 84 ++ .../components/paginator/paginator.spec.ts | 23 + .../shared/components/paginator/paginator.ts | 68 ++ .../shared/components/pmu-logo/pmu-logo.css | 3 + .../shared/components/pmu-logo/pmu-logo.html | 9 + .../components/pmu-logo/pmu-logo.spec.ts | 23 + .../shared/components/pmu-logo/pmu-logo.ts | 40 + .../components/popover/popover.component.ts | 368 ++++++++ .../components/popover/popover.variants.ts | 7 + .../progress-bar/progress-bar.component.ts | 66 ++ .../progress-bar/progress-bar.variants.ts | 54 ++ .../components/radio/radio.component.ts | 108 +++ .../shared/components/radio/radio.variants.ts | 45 + .../resizable/resizable-handle.component.ts | 188 ++++ .../resizable/resizable-panel.component.ts | 38 + .../resizable/resizable.component.ts | 277 ++++++ .../resizable/resizable.variants.ts | 61 ++ .../components/search-bar/search-bar.css | 0 .../components/search-bar/search-bar.html | 52 ++ .../components/search-bar/search-bar.spec.ts | 23 + .../components/search-bar/search-bar.ts | 62 ++ .../segmented/segmented.component.ts | 156 ++++ .../segmented/segmented.variants.ts | 38 + .../select/select-item.component.ts | 71 ++ .../components/select/select.component.ts | 478 ++++++++++ .../components/select/select.variants.ts | 29 + src/app/shared/components/sheet/sheet-ref.ts | 97 ++ .../components/sheet/sheet.component.ts | 177 ++++ .../shared/components/sheet/sheet.module.ts | 22 + .../shared/components/sheet/sheet.service.ts | 96 ++ .../shared/components/sheet/sheet.variants.ts | 58 ++ .../components/skeleton/skeleton.component.ts | 21 + .../components/skeleton/skeleton.variants.ts | 4 + .../components/slider/slider.component.ts | 389 ++++++++ .../components/slider/slider.variants.ts | 74 ++ .../components/switch/switch.component.ts | 109 +++ .../components/switch/switch.variants.ts | 27 + .../components/table/table.component.ts | 144 +++ .../shared/components/table/table.module.ts | 27 + .../shared/components/table/table.variants.ts | 61 ++ .../shared/components/tabs/tabs.component.ts | 272 ++++++ .../shared/components/tabs/tabs.variants.ts | 80 ++ .../components/toast/toast.component.ts | 44 + .../shared/components/toast/toast.variants.ts | 15 + .../toggle-group/toggle-group.component.ts | 181 ++++ .../toggle-group/toggle-group.variants.ts | 43 + .../components/toggle/toggle.component.ts | 99 ++ .../components/toggle/toggle.variants.ts | 23 + .../components/tooltip/tooltip-positions.ts | 34 + src/app/shared/components/tooltip/tooltip.ts | 194 ++++ .../components/tooltip/tooltip.variants.ts | 6 + src/app/shared/directives/tooltip.spec.ts | 8 + src/app/shared/directives/tooltip.ts | 132 +++ .../shared/forms/agent-form/agent-form.html | 91 ++ src/app/shared/forms/agent-form/agent-form.ts | 87 ++ .../agent-full-form/agent-full-form.html | 368 ++++++++ .../forms/agent-full-form/agent-full-form.ts | 417 +++++++++ .../shared/forms/course-form/course-form.css | 0 .../shared/forms/course-form/course-form.html | 393 ++++++++ .../forms/course-form/course-form.spec.ts | 23 + .../shared/forms/course-form/course-form.ts | 447 +++++++++ .../forms/hippodrome-form/hippodrome-form.css | 0 .../hippodrome-form/hippodrome-form.html | 85 ++ .../hippodrome-form/hippodrome-form.spec.ts | 23 + .../forms/hippodrome-form/hippodrome-form.ts | 151 +++ .../shared/forms/limit-form/limit-form.html | 74 ++ src/app/shared/forms/limit-form/limit-form.ts | 112 +++ .../forms/nonpartant-form/nonpartant-form.css | 0 .../nonpartant-form/nonpartant-form.html | 74 ++ .../nonpartant-form/nonpartant-form.spec.ts | 23 + .../forms/nonpartant-form/nonpartant-form.ts | 221 +++++ .../permission-form/permission-form.html | 18 + .../forms/permission-form/permission-form.ts | 73 ++ .../forms/resultat-form/resultat-form.css | 0 .../forms/resultat-form/resultat-form.html | 141 +++ .../forms/resultat-form/resultat-form.spec.ts | 23 + .../forms/resultat-form/resultat-form.ts | 543 +++++++++++ .../forms/reunion-form/reunion-form.css | 0 .../forms/reunion-form/reunion-form.html | 99 ++ .../forms/reunion-form/reunion-form.spec.ts | 23 + .../shared/forms/reunion-form/reunion-form.ts | 310 +++++++ src/app/shared/forms/role-form/role-form.html | 36 + src/app/shared/forms/role-form/role-form.ts | 120 +++ src/app/shared/forms/tpe-form/tpe-form.html | 42 + src/app/shared/forms/tpe-form/tpe-form.ts | 125 +++ src/app/shared/forms/user-form/user-form.css | 3 + src/app/shared/forms/user-form/user-form.html | 96 ++ src/app/shared/forms/user-form/user-form.ts | 148 +++ src/app/shared/paging/data-source.ts | 28 + src/app/shared/paging/normalize-page.ts | 75 ++ .../shared/paging/paginated-http.service.ts | 61 ++ src/app/shared/paging/paging.ts | 32 + src/app/shared/shared-module.ts | 8 + src/app/shared/utils/cn.ts | 6 + src/app/shared/utils/merge-classes.ts | 17 + src/app/shared/utils/number.ts | 13 + src/environments/environment.development.ts | 4 + src/environments/environment.ts | 4 + src/index.html | 28 + src/main.ts | 6 + src/styles.css | 253 +++++ tsconfig.app.json | 15 + tsconfig.json | 40 + tsconfig.spec.json | 14 + 320 files changed, 30462 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .postcssrc.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 README.md create mode 100644 angular.json create mode 100644 components.json create mode 100644 package.json create mode 100644 public/assets/images/avatar.svg create mode 100644 public/assets/logos/pmu_logo.png create mode 100644 public/assets/logos/pmu_logo_dark.png create mode 100644 public/assets/logos/pmu_logo_light.png create mode 100644 public/favicon.ico create mode 100644 scripts/init-permissions.js create mode 100644 scripts/init-permissions.ts create mode 100644 src/app/app.config.ts create mode 100644 src/app/app.css create mode 100644 src/app/app.html create mode 100644 src/app/app.routes.ts create mode 100644 src/app/app.spec.ts create mode 100644 src/app/app.ts create mode 100644 src/app/auth/auth-layout/auth-layout.css create mode 100644 src/app/auth/auth-layout/auth-layout.html create mode 100644 src/app/auth/auth-layout/auth-layout.spec.ts create mode 100644 src/app/auth/auth-layout/auth-layout.ts create mode 100644 src/app/auth/auth-module.ts create mode 100644 src/app/auth/auth-routing-module.ts create mode 100644 src/app/auth/pages/login/login.css create mode 100644 src/app/auth/pages/login/login.html create mode 100644 src/app/auth/pages/login/login.spec.ts create mode 100644 src/app/auth/pages/login/login.ts create mode 100644 src/app/core/core-module.ts create mode 100644 src/app/core/guards/auth-guard.spec.ts create mode 100644 src/app/core/guards/auth-guard.ts create mode 100644 src/app/core/guards/role-guard.ts create mode 100644 src/app/core/interceptors/api-prefix-interceptor.ts create mode 100644 src/app/core/interceptors/auth-token-interceptor.spec.ts create mode 100644 src/app/core/interceptors/auth-token-interceptor.ts create mode 100644 src/app/core/interceptors/http-error-interceptor.spec.ts create mode 100644 src/app/core/interceptors/http-error-interceptor.ts create mode 100644 src/app/core/interfaces/agent-limit.ts create mode 100644 src/app/core/interfaces/agent.ts create mode 100644 src/app/core/interfaces/course.ts create mode 100644 src/app/core/interfaces/hippodrome.ts create mode 100644 src/app/core/interfaces/menu-item.ts create mode 100644 src/app/core/interfaces/report.ts create mode 100644 src/app/core/interfaces/resultat.ts create mode 100644 src/app/core/interfaces/reunion.ts create mode 100644 src/app/core/interfaces/role.ts create mode 100644 src/app/core/interfaces/tpe.ts create mode 100644 src/app/core/interfaces/user.ts create mode 100644 src/app/core/mocks/agent-limit.mocks.ts create mode 100644 src/app/core/mocks/agent.mocks.ts create mode 100644 src/app/core/mocks/course.mocks.ts create mode 100644 src/app/core/mocks/hippodrome.mocks.ts create mode 100644 src/app/core/mocks/report.mocks.ts create mode 100644 src/app/core/mocks/reunion.mocks.ts create mode 100644 src/app/core/mocks/role.mocks.ts create mode 100644 src/app/core/mocks/tpe.mocks.ts create mode 100644 src/app/core/mocks/user.mocks.ts create mode 100644 src/app/core/services/agent-family-member.ts create mode 100644 src/app/core/services/agent-limit.ts create mode 100644 src/app/core/services/agent.ts create mode 100644 src/app/core/services/auth.spec.ts create mode 100644 src/app/core/services/auth.ts create mode 100644 src/app/core/services/course-sample.spec.ts create mode 100644 src/app/core/services/course-sample.ts create mode 100644 src/app/core/services/course.spec.ts create mode 100644 src/app/core/services/course.ts create mode 100644 src/app/core/services/hippodrome.spec.ts create mode 100644 src/app/core/services/hippodrome.ts create mode 100644 src/app/core/services/non-partant.ts create mode 100644 src/app/core/services/report.ts create mode 100644 src/app/core/services/resultat.ts create mode 100644 src/app/core/services/reunion.spec.ts create mode 100644 src/app/core/services/reunion.ts create mode 100644 src/app/core/services/role.ts create mode 100644 src/app/core/services/theme.spec.ts create mode 100644 src/app/core/services/theme.ts create mode 100644 src/app/core/services/tpe.ts create mode 100644 src/app/core/services/user.ts create mode 100644 src/app/dashboard/dashboard-module.ts create mode 100644 src/app/dashboard/dashboard-routing-module.ts create mode 100644 src/app/dashboard/layout/layout.css create mode 100644 src/app/dashboard/layout/layout.html create mode 100644 src/app/dashboard/layout/layout.spec.ts create mode 100644 src/app/dashboard/layout/layout.ts create mode 100644 src/app/dashboard/pages/agents/agents.html create mode 100644 src/app/dashboard/pages/agents/agents.ts create mode 100644 src/app/dashboard/pages/courses/courses.css create mode 100644 src/app/dashboard/pages/courses/courses.html create mode 100644 src/app/dashboard/pages/courses/courses.spec.ts create mode 100644 src/app/dashboard/pages/courses/courses.ts create mode 100644 src/app/dashboard/pages/hippodrome/hippodrome.css create mode 100644 src/app/dashboard/pages/hippodrome/hippodrome.html create mode 100644 src/app/dashboard/pages/hippodrome/hippodrome.spec.ts create mode 100644 src/app/dashboard/pages/hippodrome/hippodrome.ts create mode 100644 src/app/dashboard/pages/limits/limits.html create mode 100644 src/app/dashboard/pages/limits/limits.ts create mode 100644 src/app/dashboard/pages/main/main.css create mode 100644 src/app/dashboard/pages/main/main.html create mode 100644 src/app/dashboard/pages/main/main.spec.ts create mode 100644 src/app/dashboard/pages/main/main.ts create mode 100644 src/app/dashboard/pages/profile/profile.html create mode 100644 src/app/dashboard/pages/profile/profile.ts create mode 100644 src/app/dashboard/pages/report-courses/report-detail.html create mode 100644 src/app/dashboard/pages/report-courses/report-detail.ts create mode 100644 src/app/dashboard/pages/report-courses/report-list.html create mode 100644 src/app/dashboard/pages/report-courses/report-list.ts create mode 100644 src/app/dashboard/pages/reunion/reunion.css create mode 100644 src/app/dashboard/pages/reunion/reunion.html create mode 100644 src/app/dashboard/pages/reunion/reunion.spec.ts create mode 100644 src/app/dashboard/pages/reunion/reunion.ts create mode 100644 src/app/dashboard/pages/roles/roles.html create mode 100644 src/app/dashboard/pages/roles/roles.ts create mode 100644 src/app/dashboard/pages/tpe/tpe.html create mode 100644 src/app/dashboard/pages/tpe/tpe.ts create mode 100644 src/app/dashboard/pages/users/users.html create mode 100644 src/app/dashboard/pages/users/users.ts create mode 100644 src/app/shared/components/accordion/accordion-item.component.ts create mode 100644 src/app/shared/components/accordion/accordion.component.ts create mode 100644 src/app/shared/components/alert-dialog/alert-dialog-ref.ts create mode 100644 src/app/shared/components/alert-dialog/alert-dialog.component.html create mode 100644 src/app/shared/components/alert-dialog/alert-dialog.component.ts create mode 100644 src/app/shared/components/alert-dialog/alert-dialog.service.ts create mode 100644 src/app/shared/components/alert-dialog/alert-dialog.variants.ts create mode 100644 src/app/shared/components/alert/alert.component.ts create mode 100644 src/app/shared/components/alert/alert.variants.ts create mode 100644 src/app/shared/components/avatar/avatar.component.ts create mode 100644 src/app/shared/components/avatar/avatar.variants.ts create mode 100644 src/app/shared/components/badge/badge.component.ts create mode 100644 src/app/shared/components/badge/badge.variants.ts create mode 100644 src/app/shared/components/breadcrumb/breadcrumb.component.ts create mode 100644 src/app/shared/components/breadcrumb/breadcrumb.module.ts create mode 100644 src/app/shared/components/breadcrumb/breadcrumb.variants.ts create mode 100644 src/app/shared/components/button/button.component.ts create mode 100644 src/app/shared/components/button/button.variants.ts create mode 100644 src/app/shared/components/calendar/calendar.component.ts create mode 100644 src/app/shared/components/calendar/calendar.variants.ts create mode 100644 src/app/shared/components/card/card.component.ts create mode 100644 src/app/shared/components/card/card.variants.ts create mode 100644 src/app/shared/components/checkbox/checkbox.component.ts create mode 100644 src/app/shared/components/checkbox/checkbox.variants.ts create mode 100644 src/app/shared/components/combobox/combobox.component.ts create mode 100644 src/app/shared/components/combobox/combobox.variants.ts create mode 100644 src/app/shared/components/command/command-divider.component.ts create mode 100644 src/app/shared/components/command/command-empty.component.ts create mode 100644 src/app/shared/components/command/command-input.component.ts create mode 100644 src/app/shared/components/command/command-json.component.ts create mode 100644 src/app/shared/components/command/command-list.component.ts create mode 100644 src/app/shared/components/command/command-option-group.component.ts create mode 100644 src/app/shared/components/command/command-option.component.ts create mode 100644 src/app/shared/components/command/command.component.ts create mode 100644 src/app/shared/components/command/command.module.ts create mode 100644 src/app/shared/components/command/command.variants.ts create mode 100644 src/app/shared/components/core/directives/string-template-outlet/string-template-outlet.directive.ts create mode 100644 src/app/shared/components/data-table/data-table.css create mode 100644 src/app/shared/components/data-table/data-table.html create mode 100644 src/app/shared/components/data-table/data-table.spec.ts create mode 100644 src/app/shared/components/data-table/data-table.ts create mode 100644 src/app/shared/components/date-picker/date-picker.component.ts create mode 100644 src/app/shared/components/date-picker/date-picker.variants.ts create mode 100644 src/app/shared/components/dialog/dialog-ref.ts create mode 100644 src/app/shared/components/dialog/dialog.component.ts create mode 100644 src/app/shared/components/dialog/dialog.service.ts create mode 100644 src/app/shared/components/dialog/dialog.variants.ts create mode 100644 src/app/shared/components/divider/divider.component.ts create mode 100644 src/app/shared/components/divider/divider.variants.ts create mode 100644 src/app/shared/components/dropdown/dropdown-item.component.ts create mode 100644 src/app/shared/components/dropdown/dropdown-label.component.ts create mode 100644 src/app/shared/components/dropdown/dropdown-menu-content.component.ts create mode 100644 src/app/shared/components/dropdown/dropdown-shortcut.component.ts create mode 100644 src/app/shared/components/dropdown/dropdown-trigger.directive.ts create mode 100644 src/app/shared/components/dropdown/dropdown.component.ts create mode 100644 src/app/shared/components/dropdown/dropdown.module.ts create mode 100644 src/app/shared/components/dropdown/dropdown.service.ts create mode 100644 src/app/shared/components/dropdown/dropdown.variants.ts create mode 100644 src/app/shared/components/empty/empty.component.ts create mode 100644 src/app/shared/components/empty/empty.variants.ts create mode 100644 src/app/shared/components/form/form.component.ts create mode 100644 src/app/shared/components/form/form.module.ts create mode 100644 src/app/shared/components/form/form.variants.ts create mode 100644 src/app/shared/components/input-group/input-group.component.ts create mode 100644 src/app/shared/components/input-group/input-group.variants.ts create mode 100644 src/app/shared/components/input/input.directive.ts create mode 100644 src/app/shared/components/input/input.variants.ts create mode 100644 src/app/shared/components/layout/content.component.ts create mode 100644 src/app/shared/components/layout/footer.component.ts create mode 100644 src/app/shared/components/layout/header.component.ts create mode 100644 src/app/shared/components/layout/layout.component.ts create mode 100644 src/app/shared/components/layout/layout.module.ts create mode 100644 src/app/shared/components/layout/layout.variants.ts create mode 100644 src/app/shared/components/layout/sidebar.component.ts create mode 100644 src/app/shared/components/loader/loader.component.ts create mode 100644 src/app/shared/components/loader/loader.variants.ts create mode 100644 src/app/shared/components/menu/menu-content.directive.ts create mode 100644 src/app/shared/components/menu/menu-item.directive.ts create mode 100644 src/app/shared/components/menu/menu-manager.service.ts create mode 100644 src/app/shared/components/menu/menu-positions.ts create mode 100644 src/app/shared/components/menu/menu.directive.ts create mode 100644 src/app/shared/components/menu/menu.module.ts create mode 100644 src/app/shared/components/menu/menu.variants.ts create mode 100644 src/app/shared/components/modal/modal.css create mode 100644 src/app/shared/components/modal/modal.html create mode 100644 src/app/shared/components/modal/modal.spec.ts create mode 100644 src/app/shared/components/modal/modal.ts create mode 100644 src/app/shared/components/mode-toggle/mode-toggle.css create mode 100644 src/app/shared/components/mode-toggle/mode-toggle.html create mode 100644 src/app/shared/components/mode-toggle/mode-toggle.spec.ts create mode 100644 src/app/shared/components/mode-toggle/mode-toggle.ts create mode 100644 src/app/shared/components/pagination/pagination.component.ts create mode 100644 src/app/shared/components/pagination/pagination.module.ts create mode 100644 src/app/shared/components/pagination/pagination.variants.ts create mode 100644 src/app/shared/components/paginator/paginator.css create mode 100644 src/app/shared/components/paginator/paginator.html create mode 100644 src/app/shared/components/paginator/paginator.spec.ts create mode 100644 src/app/shared/components/paginator/paginator.ts create mode 100644 src/app/shared/components/pmu-logo/pmu-logo.css create mode 100644 src/app/shared/components/pmu-logo/pmu-logo.html create mode 100644 src/app/shared/components/pmu-logo/pmu-logo.spec.ts create mode 100644 src/app/shared/components/pmu-logo/pmu-logo.ts create mode 100644 src/app/shared/components/popover/popover.component.ts create mode 100644 src/app/shared/components/popover/popover.variants.ts create mode 100644 src/app/shared/components/progress-bar/progress-bar.component.ts create mode 100644 src/app/shared/components/progress-bar/progress-bar.variants.ts create mode 100644 src/app/shared/components/radio/radio.component.ts create mode 100644 src/app/shared/components/radio/radio.variants.ts create mode 100644 src/app/shared/components/resizable/resizable-handle.component.ts create mode 100644 src/app/shared/components/resizable/resizable-panel.component.ts create mode 100644 src/app/shared/components/resizable/resizable.component.ts create mode 100644 src/app/shared/components/resizable/resizable.variants.ts create mode 100644 src/app/shared/components/search-bar/search-bar.css create mode 100644 src/app/shared/components/search-bar/search-bar.html create mode 100644 src/app/shared/components/search-bar/search-bar.spec.ts create mode 100644 src/app/shared/components/search-bar/search-bar.ts create mode 100644 src/app/shared/components/segmented/segmented.component.ts create mode 100644 src/app/shared/components/segmented/segmented.variants.ts create mode 100644 src/app/shared/components/select/select-item.component.ts create mode 100644 src/app/shared/components/select/select.component.ts create mode 100644 src/app/shared/components/select/select.variants.ts create mode 100644 src/app/shared/components/sheet/sheet-ref.ts create mode 100644 src/app/shared/components/sheet/sheet.component.ts create mode 100644 src/app/shared/components/sheet/sheet.module.ts create mode 100644 src/app/shared/components/sheet/sheet.service.ts create mode 100644 src/app/shared/components/sheet/sheet.variants.ts create mode 100644 src/app/shared/components/skeleton/skeleton.component.ts create mode 100644 src/app/shared/components/skeleton/skeleton.variants.ts create mode 100644 src/app/shared/components/slider/slider.component.ts create mode 100644 src/app/shared/components/slider/slider.variants.ts create mode 100644 src/app/shared/components/switch/switch.component.ts create mode 100644 src/app/shared/components/switch/switch.variants.ts create mode 100644 src/app/shared/components/table/table.component.ts create mode 100644 src/app/shared/components/table/table.module.ts create mode 100644 src/app/shared/components/table/table.variants.ts create mode 100644 src/app/shared/components/tabs/tabs.component.ts create mode 100644 src/app/shared/components/tabs/tabs.variants.ts create mode 100644 src/app/shared/components/toast/toast.component.ts create mode 100644 src/app/shared/components/toast/toast.variants.ts create mode 100644 src/app/shared/components/toggle-group/toggle-group.component.ts create mode 100644 src/app/shared/components/toggle-group/toggle-group.variants.ts create mode 100644 src/app/shared/components/toggle/toggle.component.ts create mode 100644 src/app/shared/components/toggle/toggle.variants.ts create mode 100644 src/app/shared/components/tooltip/tooltip-positions.ts create mode 100644 src/app/shared/components/tooltip/tooltip.ts create mode 100644 src/app/shared/components/tooltip/tooltip.variants.ts create mode 100644 src/app/shared/directives/tooltip.spec.ts create mode 100644 src/app/shared/directives/tooltip.ts create mode 100644 src/app/shared/forms/agent-form/agent-form.html create mode 100644 src/app/shared/forms/agent-form/agent-form.ts create mode 100644 src/app/shared/forms/agent-full-form/agent-full-form.html create mode 100644 src/app/shared/forms/agent-full-form/agent-full-form.ts create mode 100644 src/app/shared/forms/course-form/course-form.css create mode 100644 src/app/shared/forms/course-form/course-form.html create mode 100644 src/app/shared/forms/course-form/course-form.spec.ts create mode 100644 src/app/shared/forms/course-form/course-form.ts create mode 100644 src/app/shared/forms/hippodrome-form/hippodrome-form.css create mode 100644 src/app/shared/forms/hippodrome-form/hippodrome-form.html create mode 100644 src/app/shared/forms/hippodrome-form/hippodrome-form.spec.ts create mode 100644 src/app/shared/forms/hippodrome-form/hippodrome-form.ts create mode 100644 src/app/shared/forms/limit-form/limit-form.html create mode 100644 src/app/shared/forms/limit-form/limit-form.ts create mode 100644 src/app/shared/forms/nonpartant-form/nonpartant-form.css create mode 100644 src/app/shared/forms/nonpartant-form/nonpartant-form.html create mode 100644 src/app/shared/forms/nonpartant-form/nonpartant-form.spec.ts create mode 100644 src/app/shared/forms/nonpartant-form/nonpartant-form.ts create mode 100644 src/app/shared/forms/permission-form/permission-form.html create mode 100644 src/app/shared/forms/permission-form/permission-form.ts create mode 100644 src/app/shared/forms/resultat-form/resultat-form.css create mode 100644 src/app/shared/forms/resultat-form/resultat-form.html create mode 100644 src/app/shared/forms/resultat-form/resultat-form.spec.ts create mode 100644 src/app/shared/forms/resultat-form/resultat-form.ts create mode 100644 src/app/shared/forms/reunion-form/reunion-form.css create mode 100644 src/app/shared/forms/reunion-form/reunion-form.html create mode 100644 src/app/shared/forms/reunion-form/reunion-form.spec.ts create mode 100644 src/app/shared/forms/reunion-form/reunion-form.ts create mode 100644 src/app/shared/forms/role-form/role-form.html create mode 100644 src/app/shared/forms/role-form/role-form.ts create mode 100644 src/app/shared/forms/tpe-form/tpe-form.html create mode 100644 src/app/shared/forms/tpe-form/tpe-form.ts create mode 100644 src/app/shared/forms/user-form/user-form.css create mode 100644 src/app/shared/forms/user-form/user-form.html create mode 100644 src/app/shared/forms/user-form/user-form.ts create mode 100644 src/app/shared/paging/data-source.ts create mode 100644 src/app/shared/paging/normalize-page.ts create mode 100644 src/app/shared/paging/paginated-http.service.ts create mode 100644 src/app/shared/paging/paging.ts create mode 100644 src/app/shared/shared-module.ts create mode 100644 src/app/shared/utils/cn.ts create mode 100644 src/app/shared/utils/merge-classes.ts create mode 100644 src/app/shared/utils/number.ts create mode 100644 src/environments/environment.development.ts create mode 100644 src/environments/environment.ts create mode 100644 src/index.html create mode 100644 src/main.ts create mode 100644 src/styles.css create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.spec.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a1257e --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +/package-lock.json + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings +__screenshots__/ + +# System files +.DS_Store +Thumbs.db diff --git a/.postcssrc.json b/.postcssrc.json new file mode 100644 index 0000000..e092dc7 --- /dev/null +++ b/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..925af83 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test", + "url": "http://localhost:9876/debug.html" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..a298b5b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ae8115 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Pjp + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.6. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..5f885f4 --- /dev/null +++ b/angular.json @@ -0,0 +1,94 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "pjp": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "pjp:build:production" + }, + "development": { + "buildTarget": "pjp:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n" + }, + "test": { + "builder": "@angular/build:karma", + "options": { + "tsConfig": "tsconfig.spec.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ] + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..03e8f9d --- /dev/null +++ b/components.json @@ -0,0 +1,13 @@ +{ + "style": "css", + "packageManager": "npm", + "tailwind": { + "css": "src/styles.css", + "baseColor": "neutral", + "cssVariables": true + }, + "aliases": { + "components": "src/app/shared/components", + "utils": "src/app/shared/utils" + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..72d497f --- /dev/null +++ b/package.json @@ -0,0 +1,61 @@ +{ + "name": "pjp-plr", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "init-permissions": "npx tsx scripts/init-permissions.ts" + }, + "prettier": { + "printWidth": 100, + "singleQuote": true, + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] + }, + "private": true, + "dependencies": { + "@angular/animations": "^20.3.5", + "@angular/cdk": "^20.2.9", + "@angular/common": "^20.3.0", + "@angular/compiler": "^20.3.0", + "@angular/core": "^20.3.0", + "@angular/forms": "^20.3.0", + "@angular/platform-browser": "^20.3.0", + "@angular/router": "^20.3.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "lucide-angular": "^0.546.0", + "lucide-static": "^0.546.0", + "ngx-sonner": "^3.1.0", + "rxjs": "~7.8.0", + "tailwind-merge": "^3.3.1", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^20.3.6", + "@angular/cli": "^20.3.6", + "@angular/compiler-cli": "^20.3.0", + "@tailwindcss/postcss": "^4.1.14", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.9.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.14", + "tailwindcss-animate": "^1.0.7", + "typescript": "~5.9.2" + } +} diff --git a/public/assets/images/avatar.svg b/public/assets/images/avatar.svg new file mode 100644 index 0000000..a2eaff1 --- /dev/null +++ b/public/assets/images/avatar.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/public/assets/logos/pmu_logo.png b/public/assets/logos/pmu_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f331211e1a750c9b3ddb718c32f773350b159dfb GIT binary patch literal 35379 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 th8dlHDMj714jHi_D-*( z8*~~*X_R4zZEMqo=r!oYQpRTjY>t&#Q?^+fV5-m5o+PC$6J^dXbySeM3opDd2RgXw zRfr!8jWN~!!M-9$pG*Pa>L||sa5^~5EM z_c}k41V}=_pj$T%d!ab80@q4$wCUb7B!I1KLy-MF%~^x5T4<(&{TKDsHKjLLw7z{1 z1wdThbEY%Uxifc0Zf2q?HcVJ&=e=kFChH>TsmKSOt^_28&JZ5W+il&(RyhBO>>2Hz8q#Uxnlx$^8|RC!NB)eZwn)h7eG396)>?}6Eh5eE+HK0Qtm**JI| z*Elpb4rURGIPmU1*&$bfhz)hyA4a|04Wk6`dYWk6aYDi~S_h1DwW(iJB?0SxpW^Q7 zZs!0^v$s*9@$rsX*{)^6zcqMv`+j(c-FuD*IsRzYe(s-s0()lznnP3+t~W%ibeDT1 zJ|$4)sE~n?b|RWnK?wR%GaEcEDURlf9r*C|>MmubW+Ji$njjI<{^o1~^#)@6Yay`L zf?fnV%vVWJiBV#_WrbZ)Gz(hLpC}Un#H`PuV#yg_--c^V5alE)kQ8yT<0in3FXwo| z$2#T&kzZLOWZ%m$M~YyG-c+Xm@K^i;E}YfY7YkHb`F@|E*)GA30OaAP0OK>K+mqIe zsp&qw_LtXZ5bdO2HG8HrmsJ#7%Ygd0z&Kk{e~u8Vsc1avDs>EGa>h5lN4S{>z7&(nkDl7FEuo_0(Eatn}{zg&dY&Y zX@Nt5TL@K_@XM10r5>RdPNJq})x&cY#8#teZ z9{6;0#vX4AWt&1`rvv|brw?lYVh%mid|0Kb9ZN$(j9#FRHX$gK<*oXMD$TXU*HnDpr>pqxqW^VLmNsR_B5FT{ zG#c@UjMQcFJP$L-9Sk%@aLymD&F0b>nf9@3EWijlhOoLBj1JPzVdh#T39|=rwPbgXOh?R4RIVKHKm? z^c!(FUkwnGYejYmd`1kl!@~h89YKst=TSNOb<+}2_QE7Y2XCq!nbqYQQ!JKX_}mV9 zi6-xw?{yShggUIuJq1y=fCKGioLt=93f0QPRk_n7RE%D!GnK7u9S<{?+~D2W_=DHN z=s!>q+6~tw!p|w}lBBf+as9f5#t9?`6j72uqI))i?Y}xXXQk2v4x<~ z^N-pbXF|G)&DVr1*b(iZzU%<|qy6JWE0@&At?BL%&#?j7ULvni z+3f9Tsb~LJu|2Hch0N`_L-*VhRX>Q}2d#l;l!SK&#mLACbDE?vk8)HrSccGnHR$OP zGKBJf+k<&rc)^E+o-lJgRBKVbMTFI~?Xnojv<~W13NmK5GLz;fAXN?;kwkPcXhc$O zDKnx@qX46bPTMB%XyI?4hD!}osN;L!LU(0{E}q$u4ZLSp z535KXpxyI_3`Y7lA7_sB6wFvo=J>Y?2Sf`P z?*`-2Ey*fMdViYI1X{lJ=Tq;;UWO*lqq@}Liu2!kxCU?vDLc2l;c~1Myx%%u(G&%< z{kabzgj;E^0gRGb8yYJ`pFMyx|C+aS_%y}r&q~B)(JU!?e6s9Hd^K@is5a{cOdF5D zu$se}%Z2qSJE-glP!Ho4VVBtFbm$$D-;BNcn(2RltG4}H6%P5}ux7YyLO>*oAdUBL zZLe~S`ww6NV0g0`V}A3RLcLOkmCzw~1NI_xALtVHaJDN`cw_Er8K?^?A)+wDBGauG z6uNZftEs88E5p{J7q34~sfG=uzPVTw9}s0wKm{B=lL~75|IoNhNJwbaEv3O%+^aW zLsPtz4(f^S^+X$C(kkT7PZguSiD{5~?>L2s-fqQ{tkb`E8be|`Vza0btf*>c2UNYm z3}^fBNT)Oq0)RNYoA&3i{~zZeHJa#TN{MAu7Ao`F?n5Yvao8-P8~^SlDwCjRJtBvj zap~Q7@j;OtVO-1Z#`}jb<7KCZ1)#0bM2!UEYK0M*ED&K{FV{Z0$u}9r68|%F+moZg zIkh-JM3tLByh!Yh{9XSW!tk%WM!>>qQlv+Xqgns2+0$84C#nB>$a{dPy?-=35`cy}{Cz?+u%B5cG<26&&i?xQlDrt9uANv-9T82iUv`45;|Y0+${5+WH=Gk;ukgB{%z zJHluPeAsLEbcAhjxB4+^b{7-59|J{Z>lhwGNE`meo(|ZlK+umrl36( z6ms69#n+z@j9-MeS~^NPBsCH1#ggiqb39=9Re8_B-3{sTN*Hov>#cHJ}XdL zTISet1NOYzO(EbTN8iCVg5p2EqQ{rNeb@%-m^ibBg@nZL4kM0Od{3c5ex)#SjRryN zk{i-i%5j0(FXhLzSWn&1YeZhtPs&!TsN=5Px6ep6dU_H1{2XlzurR0!%(e9ZgM%|i zN(o8p#AkUOUkSlg|IBi%JlHzk-cN$<0oEq5 zHUATR+lkmwVH9X6O%Zz*GfzP|(uF2?Fdbd-;iuucIL!9r`(5BWO}FONoxwh*3h zC~RXSbI2Uh{C1rLKl0$SwyqQ%ju-Np>jyCAdxTZLi8v>S^I~+N6Ri14=}_{Q3+Y%? z9`t^IFv~uVtifX)d|cny!zezI2GJFICgB&?){cn78c5#zEiJw@fO{W@^1zi8-uw8S z5DyJ29KBre7S~xsQ<5bn!h0v{rx4QtJo5p~64O9k5uuc_obvexxSrHChj> zZM_;aozY0=(d8oD5-n1SPv6y8U)|6;qO3&-WAPDCP(m){pDU|MTSL`{xR1BSB|0T7 zHa5UjUZRIGT$X(Wknjbxc3IOX(dfKkiB~}X)NU1qUL0sx3qp;9WZ!|vStz6V74l^e zLr%kAa`qN99CR`wpYnM9sc;*#TuefBHe!P2wLLS*eP9YGW~=?s*_B}za{)%`mpo=05khgVgh?+@B{pkj$jT1E9reMR-k zP?!(THyK|6Wb_IM_K6L$fBVQowx*wQf*6lWZnLcfq3#`Xr~iVrt~>_X?0smK4oYaG zJpO2YhGG@pp(+rahfcif+&p`Ke+yzjqyY99MWB5SP#L5Qf5;1lon0sEIsjum9VMKO zOCl@;?}?vvA-vUNWy=!bglAh(r^mOfWB@vU0H2DMIqd%X^zXt8O5~Kjts-E??*Es% zIK7k2fe!l?D6QM^7Svex@~bjf90hsQ10+uC-n9@@Zw);tD%w0gQVfN_d%NMLEb6WI zfFs>_2Hvb3aVpH*7xMZOaEsb`r03CS3MKU{zFNC(V>d=Z7^+ae$cbGzy#i(nn=Ln`rc1R3qx!;n%_GIhDJh_YEVU){|qbyKpEPF z23k;~)aB1O4dnXnEZ1(o<-Z$6`=!v}hv@6=D1ynzx7L*(f3f^Wm#h$aPQ45s7xjFe zXu#WTaP!5GRSB>zR;&|c26~Ds+)S7i(HoV0BBFI)J%Q^3p!7FFyF45`^k!di0h*&m z8zhUhyIM3&eYfyY>%M3P{VTzY^l=_5yOo{UKV`&;y|d)V%`F1&_wVtLA#811U2kl| zsBF6P>yC;K?QuiN`gN24s&>a2BpmphHf`W0TRXZT{h8TkGle(vOH%*oaohOGZRlUs z=ufnbZ;G=9Q}cU9c5mPKCELDt|I5xD0VNynVXF#niu~0my!gS|1^=wgVs>ZbdgoY^ zPICTcnzsMl&^p%IsJUcnwkZzK{*rXCtEnJW62?{V%lu1fD|I&Le9-TqwL zeo_9FB2|!Wdqev9GiYUyFh3HM=GtsMRbJc9xo&)T@$J@U4rw|6$oy@NOmsboCa~d; zbj=qRMdKa-$HXvNp4w*JYcerew`KgyI;38tpLyA0cTQMR&1$6OddQcrC5)4z-2>pa z21Q~#c8G7|j&KUTSdGX5FkD?zwl6d?BHg*6pmN|16uFCm8D`d%$A<`!& zy}5h8xT`n4qA@yiyVKh``!_O=P#HYL2h*swe?w+TO1yJOYrbz1RS`-EesvxG<8_8+ zjEo>v+~}+@`z|!2Vt;-r6~#Dc%srDC-J{-Ur@{LJqr27Zop-dB+%gFnwbXK5x!4nl z7h{zaN=k|ec@1}DeMi=H0Ok)oTV`xG0dZa(yTjWl)%Dwli94LNM;jMc$)3OJ%r76( zQ)`+9cMQzT&%R%ELX1wk4O=Du5I%|X#s-&}=S{^JLJv>)e zq)Y7>i>b;zU%6C2c%BHi7KBC{X_M8f=2R9bUn+v#SF7DI!gx6rp`6+1T>X$!a%N_o5x!Wr9x4JbXsc_mq32kxo)#;59L>w32v zOOPEL0Xs;;RVoobSLaKT6Xz0T`WjE4UDIss8DAxUQGGSfE@Bt0&^l_mby`W#Ws{C@ zK%Pqm>vZ-+{2{r;u)t+eLsN_P^%w)N7J79DXeL#B#airWL)-oREaIjxIl1His4gYK z3b)GL0AoEo9P}w(bqKnWn)0)&f7Y_`B;fD*|;4*iZM@CS{C=HFMDca{CdWw4;_K4KJ32yWc%^{ zL`uO8x#5TDYfe<=+C`uG0kQvIQkgb#TTm|w{xvl)>CfZ zw|mlz5-cXS2_t>&nT5Ro__VV_I8EkkcaY~bT3`!`Ujqs!a}l_~~a zW(DW%1mmzR#dO9GLoAA4VCTKxaX%4OBOS)D%DS%q*_D{0eY}4Or~6!4y{&zYuT{D_ zkz3!{xcGCt@JJ4U{?oI=I|5fEbz$ka5IMZ`nJ-YDKG8mnlrJPD@%hAt8w!I#dZF_S?%r}p=Xo+)U!|tS9akS5U z|D$Wnwouf@>*CA0u16LDJ`>*Za$RFvr7)6q%TZs&_9|#ha|*l*CZY`y_HxgG%p;Z8 zkTAlho+ptZkJ*Q8G2Vq-F2W=VxQqwf?(b`On~zL`YM?$zXo?CMdHitsqWc&oH@Ub} zMr7Aja6WMeN$XYkiOck?4MrUR^Qu3MF-f?OwG7tXOOrP?0^H)zY`uHbg|f|Y)fw3^ z#xT-U!_B3vx#x@oe~pc$8xZJ>k0Ra z6+}p1rVa1x5A-Ka?HS%*c+g>0xfq;6(sE*SgXN67Rr+yCu1F5?#Et5eu>{@1oX+N? zf~J_B{1(jrjy0mSNHCDS!l`VICRR=-k(l;iws>HVu)|dAK;#EyBA|enSPT{ zbHF5`^y0;gUO|TQ*-T!bhx9l6VTwgB?(Al+Lg56EBHi{p+A@s(4{9L29W`I=@nZlw zSezh22cs8Py*0Gy#yv8mRaZ+5{td|qUap2=*;ETn-D^3IXAnAgf!nx9rS<)>(k@dl zVx@Zz#bJi#=Hrr4H@05u_S|uBGM0Bsc;h49M7KEJ+X4l5dbn|HJz1yZ%Cfl`!3iRgzpw33mk=hO%dQ`mYf z@b65eo&lI&Z?*^{iJ-(++t`vy)ET@R!jZUruO3^8j5?Tci%0|{p>MWXPx;Z;Cw2GW z1Ry2aDd$#u&NK?IuD0+!Oeru+q&4%BhIF`o!bX&W&#}{ei#;Fwph0}KOPS!=Pg%jQ zkeht+{odlwQbgzTtse+j#T<{B&U7}% z9`O=2+Yc`B^jYDlAT#WFn^^{nMi7B-S-)Lqk&~->br=IssCv3)q>KQzw(JT;x=J)p z2Gw-eUx3qnbq-z|y&=&Zk5~{(QOP@5LsZ8DB`Ecr6cfAPFP_nywVa%a-0nLS8D-bL z-z$3=R58}Eu)44I+4m%Ko#jLu$Xz=vif8&PyyFhNDBMHYR+xB)e$b$x-gin08S}FhZJ9FzMMkO zaaWStQLR(eZAgi_Jl3s5TQ-F0>B2WgZAqBWBYr5}hi*5K1Xsj`-9187fdN+7S_Eg- zJn(0*s!$&b|?j~~U&*t1oT_Ul#qq06yI)tH$4s+GaH+Kt(pDG24Rgd{b zy4-nWD1qUl_4bz<)*$RSGo{1GwU_xAN?=lDAhx+oKq4$o^Czu%IosaUAQgY6%%sj; z$2;YYl(58c>p)uHnQ!9RmH3+bjg?oyCmnQ`JRE zOQcumjv!3Q&*eRb=!?kOuILa?T%G5p-h1@M8s}s8FHv|}04mZPE>65|+`P{s>_xXy z6~)*5SLdT4h$$u+gO`MD)5fPMB5y=|cF4UStdaF#SL;kPiys#nzqH>HQh(6Q_<1|r zWV62OxW8HTuh=i1A{P7(L481dqeUGMT4JKzclHq!rm&Un>O?k_e40hxonH5p#hH;dOq zjDJm2-qu?)w)IJcZ{UuU{HW0)+e9$AEAo@|q!6c~m6%!mPROb-M#s}eTh1teyR<1{ zWcKz~hQm&|RW~|=5;<#iM>fOjL(@t?YQ+=dY?K!v>=k{C57wZ@LYatNX{cDK{$brm z=J)M<7+{BvAqo*epfnf_y@`+65`n|UwnK1X*+pp6HFN}wnX3HZUA{e5VcMg*!B-Ye zjzAzujPB#_d%)>}FY|wNOf7};Ut2c)!~d2-sF|j(9C<ZVYs z6h(w?W(paPBr?`bRKh704W?wuSf(4UDq_^IKv6F^5+w=Xl0$P~ zl#XI_5~t2a3n9-R>72D*Bdz>V8|Arvdwd1nLY$3@qyfNdIouo;#k4=ElIE|N(4#Dz z*>+P;B2!1S{4-cd`0vUr`Gf=jK23O8&H*RFyJ(Kbe19R4wDIg@E<0d zk>2RsWWV+L;&vjYE@q)F8%k%BZY^_`7?wByPj6Fx0Hxhl^D^i` z9S<9pRuS$NV;A!6HL`>|Ki+g%8l1y-X%xDgqhJj5%cne~q{YX^`~EZSwD`{HDqcQ9 z`ia+NSDhnAqR=^&H}#xZWfAIWHr4fBkLs9Gi7flIZiB?ft=EBd*`UPJ{IaG#g3}~AhYN5H zjS>nXP}nw5!vYjL87&!wF46B5?g-VC;ZhVKJ@T>tq+>GcKChz-xR#0Q{e$WoV0jgC z*$o3Upn?N4vxXHTZ#VODx*B^M*9xGk*=;JuRgL(dX1P5Dk#UKqrkB{%uRVr~BUl<- z$%aNllV#6AYPk$z0tHesyK&VD zqdW_Rhvyz|NN#9EOs?Z!#n${-rNd&(D>*J3ltQMk|Oo5LHOGwcbHo-3-j?+S8(_!px;~Ah;$p z-G&f@aQK&~$r2FbP=U2FF1fBhad!EVw4;T7wT$V(yTU7g6_9Ieu~QA8Si!dgMZ^Lt zDuuMqK=VMXSmbO(P+#kIfzX#QOiJU7RHx6Dh@ku9^a5wFSv}3G=7Y|(+e3BR6?%XDL(5Gof^v?W z!igXy6a=@!85{B6a_qOPL_IpMY_NN6z29A6k~VkOsUMJ(gdvk-x3I6uQRWm0Px~K0 ztFMJ4wh>kurCe}GyMQjWa-Gg@oq;AO0vxz_xNN$a=<4A1ZDPH#5w%CZT}j^nt~4XE zco9~3ulICo?Ge}YGZjV!*?h!J38q=;=k_Q#@HssfC?F8Ndd6^paZYqv1dCr6!Lwi9dbdJNdMrOZ_FNklL}wMzKaDXTrB^9DGxbyI~JBV087M+i=+b z2H^|AMC?_+Ar4_E`TfSCW3 z6^k4p@Rcd5DZBpuA*io6zv`JKhX~H?dh3yo!-Rjnh3_@z96thF@56?5(ap;gRJ) zvv&nk4G+b{MCxjf@9TS#AATMPo(iEMw-c9|r>syE3y^XL?g|)IpfIWr$f<7Yp%%s8 zcja?>Z8i7qB5_t8VN;R)g-n?1W{5ouvZWOobRfLC3y&e}CqiKh8E%rAb>k*_ZVYrA zfQ@h=r8OXKR>L#6-5#632e8)fBw6p3o0^I|AUwts8zusD~klX zY@=D)lwH+7=F>sUW&R7e0fMifTQ!jB|G(}1({vCm#Zkp1+fP5Fo zLG^ye;h8NAbHl?rKy=6&0g+J|+Sol1d6_`K!*1Da`tIJwf1w|KadTtkYuFdE-xO}3 ztYcMokY?_>@>W*^z)-1C!C*1z?PK(|@cJEDLQDIu2;?ATK(S>bXm-n1=`HpcrC=>* z7U1mP+CaN&md_fW`nQ!hFw(+?fpugDDjl}2# z2Pw_?5TD~77E@U_)X1vfg2=+}z+df+q?|eZ`w(qwb(xAk<-^0piw20eAD}6^2UlDR z%iaEXLhC6E?U+=pRRzG?#$};pMicp}hv|ZnPryNGgU{or9Y%eJVBwRYomEna{M!00 z4*wWKzV}c z4K)_I65yy^6vYidwleMklUfF#h}j2hz5q*!g1lviYKxq^7_{5vA#bNt`&H>_l|sj0 zjNEn=2H=SbMDGkiSbqVobW=Z}v<3>?v6fZM3E1Z8aeo<09g0dhc&l|VsslSR)A&2* zhpIItrwUI+6u~OvD=SRWaDeoZU8i9jKbjT}5Ha`ivyz5Y^P%{hFi<6KW2U+bRLe5jT^Zt0q21c|j z1%l8)K7of^Io!hpr-Q_mtR;MdyH4O;jn04yM1if~i2gIooPe~b8{XsNU#d-#`6}MJ zaJ`(L^-fEt-LG>y{nlUomQ?=`Vo=94ncrufcX<$&{-OQ!4n}7^ab>}u$nW4OHyB2- znsG#sIU|FAI+;xaq#4;)c=BMGMx!1$#p~0$1CtMm0y4gPLwWNBhfSAco_6C9Sy#UYo%KnJzn*C7z86B9Bjka5X@joS5b$w^Fz>i7O zEr+7JObb;D^(TNc@8!`dJ<{TEd{xs^I1}(CXo4eOgD{VkFZj*rY-iYWltX-4DtoTO z+sw+%c3Jx1LmU7!!Q{QbGLHfP-H&CUX08x1IE&Ha+`g;obWtU^-!`%P&keaUIvoGp6}_AXF{s83GvEgM_?F zo{z##J6VKTb~mK^KIW;pq~8->*{yfBXI1EKB*1?tw}82{fc~CESn?-hIdXj=i+4^O zz|7Et)@J&+@{1lOrXKAPQs41V9)x#4?;dRPHJ0&#;;G&r`-#VZpaV+@jZatHY6Xs= z0C)-9s6X9f4DkL81gv2I@q7g4Uz&^1QAtztAU`4TM9B*3$L9_O@M;lO(uds9p4RO- z;>+0r@^ME6v9-YPPZ_~rh5AX@eJ3zC07n=wc>|&nTx%?OY_mlM2yEmY$5*)Sn3jm! z01lkEANsJb99H=@7z^e<`<1ugfw12%l}EzlUmos%TS5QddlrVDD;LyS9xPN25^H(< z*dXq!)%5ME{+vAP0GY#v_}SRD86OYPJ~pg)n-J04aUVc@Q6$jSsgXh6qneOJg>ozO zc~T8E_vR1F^C#b3xmyG5H5m8wnymIyk>5;^t9OJs7-OZb)*t$x^V05)O=qj#Z!;Cq4KH9BBB7Sr-x&gN90Z z24_GJ4hGt+*n33o{RueYl}Fdm9rw)wz`Q=0Gx zIezs#fB1a-`&Ahs&n`|uiK5B*@c6TbktN^?dQ*_k77xN1{$~+m8hgQmJhTAMo+1n} zh`Z~0*MIxQqy++(yN?h_i|0Ih5=US=%LQdaq%@)B`UOQwjvI&3YjihMvyraV3c8kp zE5IW<^X^ba!NuPE6x)2Yt0T3-cB!8#fL=0(q23jyd_3(lqY;)XL;4D^#+Cv+3F4!! zrRcxIAW@TVL?Gb70bCGlx?R5&y(=Aq`Xq2r_`BT)e|x}i_ZVE zMNZ0!|0h`4-i>85I}G?^aO8*@Mt(~K*|GVpM%o}Uh|ebR8h-(>100~_5l5ws3}z;P z2$(>=j)(`GhDNu`7MV>gdu;=VGk~LiYC?7(DGdUO`3%u3n@oLPSa@&Zx{cz|lnjzJ zN=_{8qlu+#dR7;pY~8JL!808?9Fluxm$}oEe{Wf~utyXQf8{^(Rs*B%05^a5ID_3_ z3seKZywv}y0cR+%0jeJ&|0$Wo$oq(mc3JSFX)@uEcstyL7<2S>N%EDmz)OK=lP{vb z*KAw_&%q`@h4uX(T!s}BR7||v05!)r;ml)(0SZ*PtWU-d)6S=)+4dH%Jx@&|eULc+ zoc2Yr0=TFF_mO(t(Ke)3<4b~8nap;M<(-ZLT@g5d+X5%l=Kvi+QFA*>%%ph1x&sqb zfP8WBw36oam0+6EkYAHzvnlyslVzPImiDui?@u-rcJNR~352OuW$n`R_$7k`0%0Aj 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 HT@U_0pX<9@ literal 0 HcmV?d00001 diff --git a/public/assets/logos/pmu_logo_light.png b/public/assets/logos/pmu_logo_light.png new file mode 100644 index 0000000000000000000000000000000000000000..19fb33c878cf8c10fa9475d2c88f84dfc5fbe2d2 GIT binary patch literal 832899 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 E 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�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

    2cuKi@QPcseHaK9$;F-Hw-zmQd6 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

    t*4duBb1s*?Bhe4b~gBz|3#X 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

    KtN+|Zl0T$o$d1E?S5VMT|U0T3DB_= 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 literal 0 HcmV?d00001 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..065d6765ba172339d8e2482df0242e19337e477d GIT binary patch literal 15406 zcmeHN2V7KF)*sidStVJqBQV1tNU=xFrpL{iwd|^&ajmNxTa3nNqOq+?Y^V{t#4ff3 z1&s}`ptPZkAZ_T)p?4T&1{jL}J@>&3GtOWf+xO-7as55tvEo^#JRm&a?x zYt1t;;nCfKx2O$|_co8mv#@CR+vZIk?S6PXn0RIgAK2! zJ;vkb9yzD91fQHbf$=_fP*_>XjZLpaA%)1QAR29vL5 zpuWBys+wxLR8UqaxMw!N;vLg5VvYrzvNQ3a%PKf7pNd{vMX=j-7SqEMjeQK_~_KCF+ zd?5}nyYjfvK&bC8G@uckD=L+kf9516oIHvBzV|Tox)V?13^WUHB9U3I`2f-T=b zxNAKIOd-C{J*7jR&7aZd-%ZBE`4U_W$kB|;9+(d;t0?7Y<`!k4TwaQr+8W|lf#y5r zV+T&}frZ;XM}A2TGKmMHORF(P6bSp7BO%zj99;uN=e-%5ULY&cB9 z^D!zW6;)MLn)RU}@{|WhqRlX{YvRiHP)&P@E##+6E}w_ptPybdavbc}&O_&*Gzc96 z;NW_&LI2#mAsib7s}DlaQ~U^B(u>iaeBhtL6Y;}C31k%&PodiMQ+`Pk#-wl;Qo@6od3lqD#<=^r6p{m ze?9sK9>8*Q0QjqJW6DlH9s2c;>+#W-c8xsff%dTp8rrRL>zqhVwkEaH*c*iu24Bqr|*U;zD+y#Te^yu%|B}G_i`E$|#Xrr%YkEy1A=%TZR ze(xjeo9L4adM#+;yL$e1Rw3AahxU=Q{?+=|f%Kzonu626r}6)@E%%uhQE4i=|2J7)aIGpK!4Eneyqu0)4SbQ9Tp5G=tmHu?sFl=&{=+I_=6wo?k zc8IM5`?*$Vl_S%|aQO)g6bJO49Si=na9F)V@?m>(de_Axzghai?Li1gF4m*XUOXlp z9P{;1I8N`)T^*ugbm+5n(8)WWyVzQ2lT^UH?Uo#C(?`GBKuWgUzTX)-J|;N`634RH=u9*@jIOD!jKb9KsPHV6?kk%;fB^_WUQumLQ$t$LDP6c7?k=bN*Q`ObM z#xGcB{W1SDtV9Kgk@1vzOk?gW{ZYwJ}2)^{u3=oai^~KZYdd zz+WqFHEN*e{J4_%A6=n#n9Q~&+?5IjGnDvJGgU-85?k4yNULx*rhB*HE~f1C;kL{(#s>-k52 zwLPl|``yS0*zey3yQ8icaN%bR|K$=!T=RydmoI{|b#qUz7LDRVihPYY07D;bxu2mX7AJ&X{GT1wR*5|R228r zQoK=D-=OE=i3#{{_tq!yU*qr#2>-;W1Qf|+T;5P?D2M{fFk+AxOBN=gq`U-I{oS$U z^2TQ{fxEZ!NbmhHbnkY9_!<8P_=Vx0_ks|fo6pf={J!{00p`qzgY%XQl+fRb$_niL zc`K%_9gRiKGdRpM{xb$E_Iv@C^FMGo%%bu_ocBHkFVSU#m??~dg!me$s^j80R%27Q znwrNrc{~e)?EGQ-&QBQd-U-;g>xSX)+{Td6UU1m9vswI>p)u(DFd5554=4v(hdn<^ z@#o_RDuX>Jk9N-ichEl{E#*m95{X*C; zTZ{ffz2V?Q`D-2hZ_*zQekGklS6KJIPxJVxdHkXn%5%m+FjWkTr4kIem58g!X*w8Q z zh+#Y7l0p3Z`=Jo5djP9pAuwAP2a}ir{P9sK2FE`_aY+f(zV2n<*Pe&->9we>tA&bm z=iih&Xq%7@{Zu9Z)bnx3Q8iVR29l^R4$_PIa07i;iR7&Wk84m8oIt+^_ooqJ!Ult^g)J9{7Z8&<1%81{TO+ zomB`kvaKx7*{U(;+A2qmE*G*nT~X)Y?S&yY>y}6BvmOhh;(wF>&Eh{C0Sh`iV(H`$ z{^~p6FTDnLI$vU_W#pz}(2S;k9SCRJ#xo+?d1HqA4a%MO{5k>rC+Zj@ z2aEAQEJHrY+&(D$`T74O{$*ZRa8P7$KgI0L=&u}gWMH=M8~h=&L@&?W(U;EkOr+Wm z>4}=>+V|1#KSXhn3J>YrkYyP8Urzr?e^Q(5&ok-I;qWH@uOa?_a})pA>PPtXCBW9e zvfZ`1N@)6Kcrdw(mbl^E_Tq8K@XT-I--ZaO?)}b{}RzGem0l42ZrIJeGyvt$$zC3 zqN{&Cy17bWx+M+m(y1m$_P=@iqow~G|GlvE7{x>M&rmBaFTm`LW3}*ihxx9Bg#U?} z71^GScMHKEtY1IXmV?ki1WWfQn4gxwY+F3c7sP=7wis5nA+UVs0XjyOKQsQ>M?!G& zeg-^(vkXD!-m!CvYY#2f!Ed`{1X|@*=*fWD(vEiuU_m$x*&(a`gmHuzf`8Dyb4N6+ zMuax4d9qu*=f$I4#*KkL8IlIU+?nF`Z#tn*^byuAMdiTO`^`Dt=S{9Zy7I)};jO z^_L>KK%s{D)cZ|3_ww%i{&1YpS96)%`qdzQCIdEywvnZ1LwktU$+TZelA)E9C^LxP zCbAf(a+N40TFC<-Z+2FVHTSF>450YR z(kBXnNn-RmnW7iJw9To+8|iX^F?mw=i&O^urQ zmtz@H2m?aF_m4)u%j}$!{{GF3Vt|chHOQ@1kEl)Q)jzZNIG^Sqe}X*A9l21Bf#u(t z+yB_nd4SuLLwsFzd1JW2&M-J7BoXiR@psRXVK&LD*a(|w4DFZkt%A;xuZWJ*!#^yu z6er~>D4vkl%b@u($0(zm{}&O_8vW@Pm4aE>#Ym+!^1r0&Moo{|?(p&oTrDhtdtoVV tl`ByB679KBU|#U!|KqCig0pMs-ok+v4zzIK_san~9@NrWIPlyY_%Bit%#r{A literal 0 HcmV?d00001 diff --git a/scripts/init-permissions.js b/scripts/init-permissions.js new file mode 100644 index 0000000..7b08ba5 --- /dev/null +++ b/scripts/init-permissions.js @@ -0,0 +1,200 @@ +/** + * Script to initialize all permissions in the backend API + * Usage: node scripts/init-permissions.js + * Or: npm run init-permissions + */ + +const PERMISSIONS_DATA = [ + // Users + { name: 'USERS_READ', description: 'Voir utilisateurs' }, + { name: 'USERS_CREATE', description: 'Créer utilisateurs' }, + { name: 'USERS_UPDATE', description: 'Modifier utilisateurs' }, + { name: 'USERS_DELETE', description: 'Supprimer utilisateurs' }, + { name: 'USERS_RESET_PASSWORD', description: 'Réinitialiser mot de passe' }, + { name: 'USERS_LOCK', description: 'Verrouiller utilisateurs' }, + { name: 'USERS_UNLOCK', description: 'Déverrouiller utilisateurs' }, + { name: 'USERS_RESET_2FA', description: 'Réinitialiser 2FA' }, + { name: 'USERS_CHANGE_ROLE', description: 'Changer de rôle' }, + { name: 'USERS_CHANGE_STATUS', description: 'Changer de statut' }, + + // Hippodromes + { name: 'HIPPODROMES_READ', description: 'Voir hippodromes' }, + { name: 'HIPPODROMES_CREATE', description: 'Créer hippodromes' }, + { name: 'HIPPODROMES_UPDATE', description: 'Modifier hippodromes' }, + { name: 'HIPPODROMES_DELETE', description: 'Supprimer hippodromes' }, + + // Reunions + { name: 'REUNIONS_READ', description: 'Voir reunions' }, + { name: 'REUNIONS_CREATE', description: 'Créer reunions' }, + { name: 'REUNIONS_UPDATE', description: 'Modifier reunions' }, + { name: 'REUNIONS_DELETE', description: 'Supprimer reunions' }, + { name: 'REUNIONS_PLANIFIEE', description: 'Planifier reunions' }, + { name: 'REUNIONS_TERMINEE', description: 'Terminer les reunions' }, + { name: 'REUNIONS_CANCEL', description: 'Annuler les reunions' }, + + // Courses + { name: 'COURSES_READ', description: 'Voir courses' }, + { name: 'COURSES_CREATE', description: 'Créer courses' }, + { name: 'COURSES_UPDATE', description: 'Modifier courses' }, + { name: 'COURSES_DELETE', description: 'Supprimer courses' }, + { name: 'COURSES_VALIDATE', description: 'Valider courses' }, + { name: 'COURSES_CONFIRM', description: 'Confirmer courses' }, + { name: 'COURSES_CLOSE', description: 'Clôturer courses' }, + { name: 'COURSES_CANCEL', description: 'Annuler courses' }, + + // TPE + { name: 'TPE_READ', description: 'Voir TPE' }, + { name: 'TPE_CREATE', description: 'Créer TPE' }, + { name: 'TPE_UPDATE', description: 'Modifier TPE' }, + { name: 'TPE_DELETE', description: 'Supprimer TPE' }, + { name: 'TPE_ASSIGN', description: 'Assigner TPE' }, + { name: 'TPE_UNASSIGN', description: 'Déassigner TPE' }, + + // Agents + { name: 'AGENTS_READ', description: 'Voir agents' }, + { name: 'AGENTS_CREATE', description: 'Créer agents' }, + { name: 'AGENTS_UPDATE', description: 'Modifier agents' }, + { name: 'AGENTS_DELETE', description: 'Supprimer agents' }, + { name: 'AGENTS_ASSIGN', description: 'Assigner agents' }, + { name: 'AGENTS_UNASSIGN', description: 'Déassigner agents' }, + { name: 'AGENTS_ASSIGN_TPE', description: 'Assigner TPE à agents' }, + { name: 'AGENTS_UNASSIGN_TPE', description: 'Déassigner TPE à agents' }, + + // Familles Agents + { name: 'AGENT_FAMILIES_READ', description: 'Voir familles agents' }, + { name: 'AGENT_FAMILIES_CREATE', description: 'Créer familles agents' }, + { name: 'AGENT_FAMILIES_UPDATE', description: 'Modifier familles agents' }, + { name: 'AGENT_FAMILIES_DELETE', description: 'Supprimer familles agents' }, + + // Limites Agents + { name: 'AGENT_LIMITS_READ', description: 'Voir limites agents' }, + { name: 'AGENT_LIMITS_CREATE', description: 'Créer limites agents' }, + { name: 'AGENT_LIMITS_UPDATE', description: 'Modifier limites agents' }, + { name: 'AGENT_LIMITS_DELETE', description: 'Supprimer limites agents' }, + { name: 'AGENT_LIMITS_DEFAULTED', description: 'Définir limites agents par défaut' }, + + // Permissions + { name: 'PERMISSIONS_READ', description: 'Voir permissions' }, + { name: 'PERMISSIONS_CREATE', description: 'Créer permissions' }, + { name: 'PERMISSIONS_UPDATE', description: 'Modifier permissions' }, + { name: 'PERMISSIONS_DELETE', description: 'Supprimer permissions' }, + { name: 'PERMISSIONS_ASSIGN', description: 'Assigner permissions' }, + { name: 'PERMISSIONS_UNASSIGN', description: 'Déassigner permissions' }, + + // Roles + { name: 'ROLES_READ', description: 'Voir rôles' }, + { name: 'ROLES_CREATE', description: 'Créer rôles' }, + { name: 'ROLES_UPDATE', description: 'Modifier rôles' }, + { name: 'ROLES_DELETE', description: 'Supprimer rôles' }, + { name: 'ROLES_ASSIGN', description: 'Assigner rôles' }, + { name: 'ROLES_UNASSIGN', description: 'Déassigner rôles' }, + { name: 'ROLES_ASSIGN_PERMISSIONS', description: 'Assigner permissions à rôles' }, + { name: 'ROLES_UNASSIGN_PERMISSIONS', description: 'Déassigner permissions à rôles' }, +]; + +// Remove duplicates by name +const uniquePermissions = Array.from(new Map(PERMISSIONS_DATA.map((p) => [p.name, p])).values()); + +const API_BASE_URL = process.env.API_BASE_URL || 'https://b440a25a7658.ngrok-free.app'; +const PERMISSIONS_ENDPOINT = `${API_BASE_URL}/api/v1/permissions`; + +async function createPermission(payload) { + try { + const response = await fetch(PERMISSIONS_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'ngrok-skip-browser-warning': 'true', + }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const errorText = await response.text(); + return { + success: false, + error: `HTTP ${response.status}: ${errorText}`, + }; + } + + const data = await response.json(); + return { success: true, data }; + } catch (error) { + return { + success: false, + error: error.message || 'Unknown error', + }; + } +} + +async function getAllExistingPermissions() { + try { + const response = await fetch(PERMISSIONS_ENDPOINT, { + headers: { + 'ngrok-skip-browser-warning': 'true', + }, + }); + if (response.ok) { + const data = await response.json(); + const permissions = Array.isArray(data) ? data : []; + return new Set(permissions.map((p) => p.name).filter(Boolean)); + } + return new Set(); + } catch (error) { + console.warn('Warning: Could not fetch existing permissions, will try to create all:', error); + return new Set(); + } +} + +async function initAllPermissions() { + console.log(`🚀 Initializing ${uniquePermissions.length} permissions...\n`); + console.log(`API Base URL: ${API_BASE_URL}\n`); + + // Fetch all existing permissions once at the start + console.log('📋 Fetching existing permissions...'); + const existingPermissions = await getAllExistingPermissions(); + console.log(` Found ${existingPermissions.size} existing permission(s)\n`); + + const results = { + created: 0, + skipped: 0, + errors: 0, + }; + + for (const perm of uniquePermissions) { + // Check if permission already exists in the set we fetched + if (existingPermissions.has(perm.name)) { + console.log(`⏭️ Skipped: ${perm.name} (already exists)`); + results.skipped++; + continue; + } + + const result = await createPermission({ + name: perm.name, + description: perm.description || '', + }); + + if (result.success) { + console.log(`✅ Created: ${perm.name}`); + results.created++; + } else { + console.error(`❌ Failed: ${perm.name} - ${result.error}`); + results.errors++; + } + + // Small delay to avoid overwhelming the server + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + console.log(`\n📊 Summary:`); + console.log(` Created: ${results.created}`); + console.log(` Skipped: ${results.skipped}`); + console.log(` Errors: ${results.errors}`); + console.log(` Total: ${uniquePermissions.length}`); +} + +// Run the script +initAllPermissions().catch((error) => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/scripts/init-permissions.ts b/scripts/init-permissions.ts new file mode 100644 index 0000000..32c8d2d --- /dev/null +++ b/scripts/init-permissions.ts @@ -0,0 +1,127 @@ +/** + * Script to initialize all permissions in the backend API + * Usage: npx tsx scripts/init-permissions.ts + * Or: node scripts/init-permissions.js (after compiling) + */ + +import { PERMISSIONS_MOCK } from '../src/app/core/mocks/role.mocks'; + +const API_BASE_URL = process.env.API_BASE_URL || 'https://b440a25a7658.ngrok-free.app'; +const PERMISSIONS_ENDPOINT = `${API_BASE_URL}/api/v1/permissions`; + +// Clean up permissions: remove duplicates by name and fix IDs +const uniquePermissions = Array.from( + new Map( + PERMISSIONS_MOCK.map((p) => [p.name, p]) + ).values() +).map((p, index) => ({ + name: p.name, + description: p.description || '', +})); + +interface PermissionPayload { + name: string; + description: string; +} + +async function createPermission(payload: PermissionPayload): Promise<{ success: boolean; data?: any; error?: string }> { + try { + const response = await fetch(PERMISSIONS_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'ngrok-skip-browser-warning': 'true', + }, + body: JSON.stringify(payload), + }); + + if (!response.ok) { + const errorText = await response.text(); + return { + success: false, + error: `HTTP ${response.status}: ${errorText}`, + }; + } + + const data = await response.json(); + return { success: true, data }; + } catch (error: any) { + return { + success: false, + error: error.message || 'Unknown error', + }; + } +} + +async function getAllExistingPermissions(): Promise> { + try { + const response = await fetch(PERMISSIONS_ENDPOINT, { + headers: { + 'ngrok-skip-browser-warning': 'true', + }, + }); + if (response.ok) { + const data = await response.json(); + const permissions = Array.isArray(data) ? data : []; + return new Set(permissions.map((p: any) => p.name).filter(Boolean)); + } + return new Set(); + } catch (error) { + console.warn('Warning: Could not fetch existing permissions, will try to create all:', error); + return new Set(); + } +} + +async function initAllPermissions() { + console.log(`🚀 Initializing ${uniquePermissions.length} permissions...\n`); + console.log(`API Base URL: ${API_BASE_URL}\n`); + + // Fetch all existing permissions once at the start + console.log('📋 Fetching existing permissions...'); + const existingPermissions = await getAllExistingPermissions(); + console.log(` Found ${existingPermissions.size} existing permission(s)\n`); + + const results = { + created: 0, + skipped: 0, + errors: 0, + }; + + for (const perm of uniquePermissions) { + // Check if permission already exists in the set we fetched + if (existingPermissions.has(perm.name)) { + console.log(`⏭️ Skipped: ${perm.name} (already exists)`); + results.skipped++; + continue; + } + + const result = await createPermission({ + name: perm.name, + description: perm.description, + }); + + if (result.success) { + console.log(`✅ Created: ${perm.name}`); + results.created++; + } else { + console.error(`❌ Failed: ${perm.name} - ${result.error}`); + results.errors++; + } + + // Small delay to avoid overwhelming the server + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + console.log(`\n📊 Summary:`); + console.log(` Created: ${results.created}`); + console.log(` Skipped: ${results.skipped}`); + console.log(` Errors: ${results.errors}`); + console.log(` Total: ${uniquePermissions.length}`); +} + +// Run the script +initAllPermissions().catch((error) => { + console.error('Fatal error:', error); + process.exit(1); +}); + diff --git a/src/app/app.config.ts b/src/app/app.config.ts new file mode 100644 index 0000000..505fc7f --- /dev/null +++ b/src/app/app.config.ts @@ -0,0 +1,22 @@ +import { + ApplicationConfig, + LOCALE_ID, + provideBrowserGlobalErrorListeners, + provideZonelessChangeDetection, +} from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { provideAnimations } from '@angular/platform-browser/animations'; + +import { routes } from './app.routes'; +import { provideHttpClient } from '@angular/common/http'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideBrowserGlobalErrorListeners(), + provideZonelessChangeDetection(), + provideRouter(routes), + provideHttpClient(), + provideAnimations(), + { provide: LOCALE_ID, useValue: 'fr-FR' }, + ], +}; diff --git a/src/app/app.css b/src/app/app.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/app.html b/src/app/app.html new file mode 100644 index 0000000..bde60e1 --- /dev/null +++ b/src/app/app.html @@ -0,0 +1,2 @@ + + diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts new file mode 100644 index 0000000..10ac0cb --- /dev/null +++ b/src/app/app.routes.ts @@ -0,0 +1,14 @@ +import { Routes } from '@angular/router'; + +export const routes: Routes = [ + { + path: 'auth', + // loadChildren: () => import('./auth/auth.module').then((m) => m.AuthModule), + loadChildren: () => import('./auth/auth-module').then((m) => m.AuthModule), + }, + { + path: '', + loadChildren: () => import('./dashboard/dashboard-module').then((m) => m.DashboardModule), + }, + { path: '**', redirectTo: 'auth/login' }, +]; diff --git a/src/app/app.spec.ts b/src/app/app.spec.ts new file mode 100644 index 0000000..88aab59 --- /dev/null +++ b/src/app/app.spec.ts @@ -0,0 +1,25 @@ +import { provideZonelessChangeDetection } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { App } from './app'; + +describe('App', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + providers: [provideZonelessChangeDetection()] + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, pjp'); + }); +}); diff --git a/src/app/app.ts b/src/app/app.ts new file mode 100644 index 0000000..32e77e0 --- /dev/null +++ b/src/app/app.ts @@ -0,0 +1,13 @@ +import { Component, signal } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { ZardToastComponent } from '@shared/components/toast/toast.component'; + +@Component({ + selector: 'app-root', + imports: [RouterOutlet, ZardToastComponent], + templateUrl: './app.html', + styleUrl: './app.css', +}) +export class App { + protected readonly title = signal('pjp'); +} diff --git a/src/app/auth/auth-layout/auth-layout.css b/src/app/auth/auth-layout/auth-layout.css new file mode 100644 index 0000000..92d692c --- /dev/null +++ b/src/app/auth/auth-layout/auth-layout.css @@ -0,0 +1,3 @@ +:host { + display: contents; +} diff --git a/src/app/auth/auth-layout/auth-layout.html b/src/app/auth/auth-layout/auth-layout.html new file mode 100644 index 0000000..39ee889 --- /dev/null +++ b/src/app/auth/auth-layout/auth-layout.html @@ -0,0 +1,74 @@ +

    +
    +
    +
    + +
    +
    + +
    + + +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    + +
    +
    +
    +
    diff --git a/src/app/auth/auth-layout/auth-layout.spec.ts b/src/app/auth/auth-layout/auth-layout.spec.ts new file mode 100644 index 0000000..22b7c30 --- /dev/null +++ b/src/app/auth/auth-layout/auth-layout.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AuthLayout } from './auth-layout'; + +describe('AuthLayout', () => { + let component: AuthLayout; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AuthLayout] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AuthLayout); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/auth/auth-layout/auth-layout.ts b/src/app/auth/auth-layout/auth-layout.ts new file mode 100644 index 0000000..b69ec31 --- /dev/null +++ b/src/app/auth/auth-layout/auth-layout.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { Theme } from 'src/app/core/services/theme'; + +@Component({ + selector: 'app-auth-layout', + templateUrl: './auth-layout.html', + styleUrl: './auth-layout.css', + standalone: false, +}) +export class AuthLayout { + constructor(public theme: Theme) {} + + toggleTheme() { + this.theme.toggle(); + } +} diff --git a/src/app/auth/auth-module.ts b/src/app/auth/auth-module.ts new file mode 100644 index 0000000..109797c --- /dev/null +++ b/src/app/auth/auth-module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AuthRoutingModule } from './auth-routing-module'; +import { AuthLayout } from './auth-layout/auth-layout'; +import { Login } from './pages/login/login'; +import { ModeToggle } from '@shared/components/mode-toggle/mode-toggle'; +import { SharedModule } from '@shared/shared-module'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ZardSwitchComponent } from '@shared/components/switch/switch.component'; +import { PmuLogo } from '@shared/components/pmu-logo/pmu-logo'; + +@NgModule({ + declarations: [AuthLayout, Login], + imports: [ + CommonModule, + AuthRoutingModule, + SharedModule, + ModeToggle, + PmuLogo, + FormsModule, + ReactiveFormsModule, + ZardSwitchComponent, + ], +}) +export class AuthModule {} diff --git a/src/app/auth/auth-routing-module.ts b/src/app/auth/auth-routing-module.ts new file mode 100644 index 0000000..173e337 --- /dev/null +++ b/src/app/auth/auth-routing-module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AuthLayout } from './auth-layout/auth-layout'; +import { Login } from './pages/login/login'; + +const routes: Routes = [ + { + path: '', + component: AuthLayout, + children: [ + { + path: 'login', + component: Login, + }, + { path: '', pathMatch: 'full', redirectTo: 'login' }, + ], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class AuthRoutingModule {} diff --git a/src/app/auth/pages/login/login.css b/src/app/auth/pages/login/login.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/auth/pages/login/login.html b/src/app/auth/pages/login/login.html new file mode 100644 index 0000000..e3ca801 --- /dev/null +++ b/src/app/auth/pages/login/login.html @@ -0,0 +1,84 @@ +
    +
    + +

    Connexion

    +
    + +

    Accédez à votre espace PMU MALI

    + +
    + +
    + +
    + +
    + @if (form.controls['identifiant'].touched && form.controls['identifiant'].invalid) { +
    Identifiant requis
    + } +
    + + +
    + +
    + + +
    + @if (form.controls['password'].touched && form.controls['password'].invalid) { +
    8 caractères minimum
    + } +
    + + + +
    + +
    Plateforme de Jeux de la PMU.
    +
    diff --git a/src/app/auth/pages/login/login.spec.ts b/src/app/auth/pages/login/login.spec.ts new file mode 100644 index 0000000..dd8bbb3 --- /dev/null +++ b/src/app/auth/pages/login/login.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Login } from './login'; + +describe('Login', () => { + let component: Login; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Login] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Login); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/auth/pages/login/login.ts b/src/app/auth/pages/login/login.ts new file mode 100644 index 0000000..20f500d --- /dev/null +++ b/src/app/auth/pages/login/login.ts @@ -0,0 +1,47 @@ +import { Component, signal } from '@angular/core'; // Import OnInit +import { Validators, FormBuilder, FormGroup } from '@angular/forms'; // Import FormGroup +import { Router } from '@angular/router'; +import { toast } from 'ngx-sonner'; +import { Auth } from 'src/app/core/services/auth'; + +@Component({ + selector: 'app-login', + templateUrl: './login.html', + styleUrl: './login.css', + standalone: false, +}) +export class Login { + showPassword = false; + loading = signal(false); + errorMsg = signal(''); + form!: FormGroup; + + constructor(private fb: FormBuilder, private auth: Auth, private router: Router) { + this.form = this.fb.group({ + identifiant: ['', [Validators.required]], + password: ['', [Validators.required, Validators.minLength(8)]], + }); + } + + async submit() { + this.errorMsg.set(''); + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + this.loading.set(true); + try { + const { identifiant, password } = this.form.value; + await this.auth.login(identifiant!, password!); + await this.router.navigateByUrl('/'); + toast.success('Connexion réussie ! Bienvenue.'); + } catch (e: any) { + this.errorMsg.set( + e?.message || e?.error?.message || 'Échec de connexion. Veuillez réessayer.' + ); + toast.error(this.errorMsg(), { duration: 5000 }); + } finally { + this.loading.set(false); + } + } +} diff --git a/src/app/core/core-module.ts b/src/app/core/core-module.ts new file mode 100644 index 0000000..af5e615 --- /dev/null +++ b/src/app/core/core-module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { ApiPrefixInterceptor } from './interceptors/api-prefix-interceptor'; +import { AuthTokenInterceptor } from './interceptors/auth-token-interceptor'; +import { HttpErrorInterceptor } from './interceptors/http-error-interceptor'; + +@NgModule({ + declarations: [], + imports: [CommonModule], + providers: [ + { provide: HTTP_INTERCEPTORS, useClass: ApiPrefixInterceptor, multi: true }, + { provide: HTTP_INTERCEPTORS, useClass: AuthTokenInterceptor, multi: true }, + { provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true }, + ], +}) +export class CoreModule {} diff --git a/src/app/core/guards/auth-guard.spec.ts b/src/app/core/guards/auth-guard.spec.ts new file mode 100644 index 0000000..0ffeaf0 --- /dev/null +++ b/src/app/core/guards/auth-guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { CanActivateFn } from '@angular/router'; + +import { authGuard } from './auth-guard'; + +describe('authGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => authGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/src/app/core/guards/auth-guard.ts b/src/app/core/guards/auth-guard.ts new file mode 100644 index 0000000..c4de43e --- /dev/null +++ b/src/app/core/guards/auth-guard.ts @@ -0,0 +1,9 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; +import { Auth } from '../services/auth'; + +export const authGuard: CanActivateFn = (route, state) => { + const auth = inject(Auth); + const router = inject(Router); + return auth.isAuthenticated() ? true : router.parseUrl('/auth/login'); +}; diff --git a/src/app/core/guards/role-guard.ts b/src/app/core/guards/role-guard.ts new file mode 100644 index 0000000..b501e62 --- /dev/null +++ b/src/app/core/guards/role-guard.ts @@ -0,0 +1,33 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; +import { Auth } from '../services/auth'; + +/** + * Guard générique basé sur le roleId de l'utilisateur. + * Usage dans le routing: + * { + * path: 'users', + * canActivate: [roleGuard], + * data: { roles: ['1', '2'] } // ids de rôles autorisés + * } + */ +export const roleGuard: CanActivateFn = (route, state) => { + const auth = inject(Auth); + const router = inject(Router); + + const expectedRoles = (route.data?.['roles'] as string[] | undefined) ?? []; + + if (!auth.isAuthenticated()) { + return router.parseUrl('/auth/login'); + } + + if (expectedRoles.length === 0) { + // Si aucune contrainte, on laisse passer + return true; + } + + const ok = auth.hasAnyRoleId(expectedRoles); + return ok ? true : router.parseUrl('/dashboard'); // ou une page 403 dédiée +}; + + diff --git a/src/app/core/interceptors/api-prefix-interceptor.ts b/src/app/core/interceptors/api-prefix-interceptor.ts new file mode 100644 index 0000000..73bf7a5 --- /dev/null +++ b/src/app/core/interceptors/api-prefix-interceptor.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { environment } from 'src/environments/environment.development'; + +@Injectable() +export class ApiPrefixInterceptor implements HttpInterceptor { + constructor() {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + const isAbsolute = /^https?:\/\//i.test(request.url); + const url = isAbsolute ? request.url : `${environment.apiBaseUrl}${request.url}`; + + // Add ngrok bypass header to skip the warning page + const isNgrok = + url.includes('ngrok-free.app') || url.includes('ngrok.io') || url.includes('ngrok'); + + // Clone request with updated URL + let clonedRequest = request.clone({ url }); + + // Add ngrok bypass header if needed (only if not already present) + if (isNgrok && !clonedRequest.headers.has('ngrok-skip-browser-warning')) { + clonedRequest = clonedRequest.clone({ + setHeaders: { + 'ngrok-skip-browser-warning': 'true', + }, + }); + } + + return next.handle(clonedRequest); + } +} diff --git a/src/app/core/interceptors/auth-token-interceptor.spec.ts b/src/app/core/interceptors/auth-token-interceptor.spec.ts new file mode 100644 index 0000000..7ed4c16 --- /dev/null +++ b/src/app/core/interceptors/auth-token-interceptor.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpInterceptorFn } from '@angular/common/http'; + +import { authTokenInterceptor } from './auth-token-interceptor'; + +describe('authTokenInterceptor', () => { + const interceptor: HttpInterceptorFn = (req, next) => + TestBed.runInInjectionContext(() => authTokenInterceptor(req, next)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/core/interceptors/auth-token-interceptor.ts b/src/app/core/interceptors/auth-token-interceptor.ts new file mode 100644 index 0000000..124fb3d --- /dev/null +++ b/src/app/core/interceptors/auth-token-interceptor.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { Auth } from '../services/auth'; + +@Injectable() +export class AuthTokenInterceptor implements HttpInterceptor { + constructor(private auth: Auth) {} + intercept(req: HttpRequest, next: HttpHandler): Observable> { + const token = this.auth.getToken(); + if (!token) return next.handle(req); + return next.handle(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })); + } +} diff --git a/src/app/core/interceptors/http-error-interceptor.spec.ts b/src/app/core/interceptors/http-error-interceptor.spec.ts new file mode 100644 index 0000000..a5f46f0 --- /dev/null +++ b/src/app/core/interceptors/http-error-interceptor.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpInterceptorFn } from '@angular/common/http'; + +import { httpErrorInterceptor } from './http-error-interceptor'; + +describe('httpErrorInterceptor', () => { + const interceptor: HttpInterceptorFn = (req, next) => + TestBed.runInInjectionContext(() => httpErrorInterceptor(req, next)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/core/interceptors/http-error-interceptor.ts b/src/app/core/interceptors/http-error-interceptor.ts new file mode 100644 index 0000000..3ddcc8f --- /dev/null +++ b/src/app/core/interceptors/http-error-interceptor.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor, + HttpErrorResponse, +} from '@angular/common/http'; +import { catchError, Observable, throwError } from 'rxjs'; + +@Injectable() +export class HttpErrorInterceptor implements HttpInterceptor { + constructor() {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + catchError((err: HttpErrorResponse) => { + // TODO: remplacer par un toast global + console.error('HTTP error:', err.status, err.message); + return throwError(() => err); + }) + ); + } +} diff --git a/src/app/core/interfaces/agent-limit.ts b/src/app/core/interfaces/agent-limit.ts new file mode 100644 index 0000000..85fb26b --- /dev/null +++ b/src/app/core/interfaces/agent-limit.ts @@ -0,0 +1,23 @@ +export interface AgentLimit { + id: string; + code: string; // e.g., ALC001 + configCode: string; // e.g., ALC001 + nom: string; + isDefault: boolean; + actif: boolean; + + // Bet limits + betMin?: number; + betMax?: number; + maxBet?: number; + maxDisburseBet?: number; + + // Airtime + airtimeMin?: number; + airtimeMax?: number; + + createdAt?: string; + createdBy?: string; +} + + diff --git a/src/app/core/interfaces/agent.ts b/src/app/core/interfaces/agent.ts new file mode 100644 index 0000000..8cb2457 --- /dev/null +++ b/src/app/core/interfaces/agent.ts @@ -0,0 +1,65 @@ +import { TpeDevice } from './tpe'; + +export type AgentStatus = 'ACTIF' | 'INACTIF' | 'SUSPENDU'; + +export interface Agent { + id: string; + code: string; + profile: string; // ex. AGENT, SUPERVISEUR, CAISSIER + principalCode?: string; // Agent principal + caisseProfile?: string; + statut: AgentStatus; + zone?: string; + kiosk?: string; + fonction?: string; + dateEmbauche?: string; // ISO + + nom: string; + prenom: string; + autresNoms?: string; + dateNaissance?: string; + lieuNaissance?: string; + ville?: string; + adresse?: string; + autoriserAides?: boolean; + + phone: string; + pin?: string; // masked in UI + + limiteInferieure?: number; + limiteSuperieure?: number; + limiteParTransaction?: number; + limiteMinAirtime?: number; + limiteMaxAirtime?: number; + + maxPeripheriques?: number; + + limitId?: string; // reference to AgentLimit config + + // Légales + nationalite?: string; + cni?: string; + cniDelivreeLe?: string; + cniDelivreeA?: string; + residence?: string; + autreAdresse1?: string; + statutMarital?: string; + epoux?: string; + autreTelephone?: string; + + // TPE assignés (actifs seulement) + tpes?: TpeDevice[]; + + createdAt?: string; + updatedAt?: string; + createdBy?: string; +} + +export interface AgentFamilyMember { + id: string; + agentId: string; + nom: string; + statut?: string; // conjoint, enfant, etc. + dateNaissance?: string; + sexe?: 'M' | 'F'; +} diff --git a/src/app/core/interfaces/course.ts b/src/app/core/interfaces/course.ts new file mode 100644 index 0000000..33fbfd6 --- /dev/null +++ b/src/app/core/interfaces/course.ts @@ -0,0 +1,58 @@ +import { Reunion } from './reunion'; + +export enum CourseType { + TIERCE = 'TIERCE', + QUARTE = 'QUARTE + TIERCE', + QUINTE = 'QUINTE + TIERCE', +} + +export enum CourseStatut { + PROGRAMMEE = 'PROGRAMMEE', + CREATED = 'CREATED', + VALIDATED = 'VALIDATED', + RUNNING = 'RUNNING', + CLOSED = 'CLOSED', + CANCELED = 'CANCELED', +} + +export enum ResultatStatut { + NONE = 'NONE', + NON_GENERE = 'NON_GENERE', + CREATED = 'CREATED', + VALIDATED = 'VALIDATED', + CONFIRMED = 'CONFIRMED', +} + +export interface Course { + id: string; + type: CourseType | string; // API returns "Plat" as string + numero: number; + nom: string; + + dateDepartCourse: string; + dateDebutParis: string; + dateFinParis: string; + + reunion: Reunion; + reunionCourse: number; + + particularite?: string; + partants: number; + distance: number; + condition?: string; + + statut: CourseStatut | string; // API returns "PROGRAMMEE" as string + + nonPartants: string[]; + + // Additional API fields + estTerminee?: boolean; + estAnnulee?: boolean; + nombreChevauxInscrits?: number; + adeadHeat?: boolean; + + createdBy: string; + validatedBy?: string | null; + createdAt: string | null; + updatedAt: string | null; +} diff --git a/src/app/core/interfaces/hippodrome.ts b/src/app/core/interfaces/hippodrome.ts new file mode 100644 index 0000000..74dec39 --- /dev/null +++ b/src/app/core/interfaces/hippodrome.ts @@ -0,0 +1,13 @@ +export interface Hippodrome { + id: string; + nom: string; + ville: string; + pays: string; + actif: boolean; + capacite?: number; + description?: string; + reunionCount?: number; + courseCount?: number; + createdAt: string; + updatedAt: string; +} diff --git a/src/app/core/interfaces/menu-item.ts b/src/app/core/interfaces/menu-item.ts new file mode 100644 index 0000000..0e2dbef --- /dev/null +++ b/src/app/core/interfaces/menu-item.ts @@ -0,0 +1,7 @@ +export interface MenuItem { + icon: string; + label: string; + exact?: boolean; + link?: string; + submenu?: MenuItem[]; +} diff --git a/src/app/core/interfaces/report.ts b/src/app/core/interfaces/report.ts new file mode 100644 index 0000000..65bbaac --- /dev/null +++ b/src/app/core/interfaces/report.ts @@ -0,0 +1,26 @@ +import { Course } from './course'; + +export type ReportStatut = 'Validé' | 'Non Validé' | 'En attente'; +export type CourseCloseStatut = 'Clôturée' | 'Ouverte'; + +export interface CourseReportSummary { + id: string; // same as course id + course: Course; // full course reference; the course must be CLOSED + statut: ReportStatut; + confirmed?: boolean; // when true, report is locked (no further edits) +} + +export interface CourseReportDetailRow { + typeGain: string; // e.g., QUINTE ORDRE + typeJeu: string; // e.g., Quinte+ + montant: number; // amount per winning ticket + nombre: number; // number of winners + statut: 'Validée' | 'Non Validée'; + distributed?: boolean; + externe?: boolean; +} + +export interface CourseReportDetail { + summary: CourseReportSummary; + rows: CourseReportDetailRow[]; +} diff --git a/src/app/core/interfaces/resultat.ts b/src/app/core/interfaces/resultat.ts new file mode 100644 index 0000000..0baa814 --- /dev/null +++ b/src/app/core/interfaces/resultat.ts @@ -0,0 +1,58 @@ +import { Course } from './course'; + +export interface Resultat { + id: string; + course: Course; + /** + * Ordre d'arrivée des chevaux. + * The backend returns an array of strings/numbers (cheval numbers); + * in the UI we normalize them to plain numbers. + */ + ordreArrivee: number[]; + /** + * Chevaux en dead-heat (ex aequo), represented by their numbers. + */ + chevauxDeadHeat: number[]; + totalMises: number; + masseAPartager: number; + prelevementsLegaux: number; + montantRembourse: number; + montantCagnotte: number; + adeadHeat: boolean; + createdAt?: string; + updatedAt?: string; +} + +// API response structure (course may be just an ID in some cases) +export interface ResultatApiResponse { + id: string | number; + course: Course | string | number; + /** + * In the raw API this is an array of strings/numbers. + */ + ordreArrivee: (string | number)[]; + chevauxDeadHeat: (string | number)[]; + totalMises: number; + masseAPartager: number; + prelevementsLegaux: number; + montantRembourse: number; + montantCagnotte: number; + adeadHeat: boolean; + createdAt?: string; + updatedAt?: string; +} + +// POST payload structure +export interface CreateResultatPayload { + course: { + id: string | number; + }; + ordreArrivee: string[]; + chevauxDeadHeat?: (string | number)[]; + totalMises?: number; + masseAPartager?: number; + prelevementsLegaux?: number; + montantRembourse?: number; + montantCagnotte?: number; + adeadHeat?: boolean; +} diff --git a/src/app/core/interfaces/reunion.ts b/src/app/core/interfaces/reunion.ts new file mode 100644 index 0000000..7c7ec6d --- /dev/null +++ b/src/app/core/interfaces/reunion.ts @@ -0,0 +1,21 @@ +import { Hippodrome } from './hippodrome'; + +export enum ReunionStatut { + PLANIFIEE = 'PLANIFIEE', + EN_COURS = 'EN_COURS', + TERMINEE = 'TERMINEE', + ANNULEE = 'ANNULEE', +} + +export interface Reunion { + id: string; + code: string; + nom: string; + date: string; + numero: number; + statut: ReunionStatut; + hippodrome: Hippodrome; + totalCourses?: number; + createdAt: string; + updatedAt: string; +} diff --git a/src/app/core/interfaces/role.ts b/src/app/core/interfaces/role.ts new file mode 100644 index 0000000..f8464b6 --- /dev/null +++ b/src/app/core/interfaces/role.ts @@ -0,0 +1,14 @@ +export interface Permission { + id: string; + name: string; + description?: string; +} + +export interface Role { + id: string; + name: string; + description?: string; + permissions: Permission[]; + createdAt?: string; + updatedAt?: string; +} diff --git a/src/app/core/interfaces/tpe.ts b/src/app/core/interfaces/tpe.ts new file mode 100644 index 0000000..b2a5bec --- /dev/null +++ b/src/app/core/interfaces/tpe.ts @@ -0,0 +1,27 @@ +import { Agent } from './agent'; + +export type TpeStatus = + | 'VALIDE' + | 'INVALIDE' + | 'EN_PANNE' + | 'BLOQUE' + | 'DISPONIBLE' + | 'AFFECTE' + | 'EN_MAINTENANCE' + | 'HORS_SERVICE' + | 'VOLE'; +export type TpeType = 'POS' | 'OTHER'; + +export interface TpeDevice { + id: string; + imei: string; + serial: string; + type: TpeType; + marque: string; + modele: string; + statut: TpeStatus; + agent?: Agent; + assigne: boolean; + createdAt?: string; + updatedAt?: string; +} diff --git a/src/app/core/interfaces/user.ts b/src/app/core/interfaces/user.ts new file mode 100644 index 0000000..0506382 --- /dev/null +++ b/src/app/core/interfaces/user.ts @@ -0,0 +1,39 @@ +export type UserStatus = 'ACTIVE' | 'CANCELLED' | 'SUSPENDED' | string; +import type { Role } from './role'; + +/** + * Frontend User model. + * Aligns with backend payload while keeping a convenient `role` object when available. + */ +export interface User { + id: string; + /** Nom (last name) */ + nom: string; + /** Prénom (first name) */ + prenom: string; + /** Identifiant de connexion (username/login) */ + identifiant: string; + /** (Hashed) password – never filled from backend in UI, only for create/update. */ + password?: string; + /** Matricule Agent */ + matriculeAgent: string; + /** Foreign key vers le rôle */ + roleId: string; + /** Rôle complet (chargé séparément) */ + role?: Role; + /** Restriction de connexion (manual) */ + restrictionConnexion: boolean; + /** Restriction automatique */ + restrictionAutomatique: boolean; + /** Nombre d'IP autorisé (manual) */ + nombreIpAutorise: number; + /** Nombre d'IP auto autorisé (automatic) */ + nombreIpAutoAutorise: number; + /** Statut (from grid / backend) */ + statut: UserStatus; + /** Date de dernière connexion (ISO) */ + derniereConnexion?: string; + /** Timestamps */ + createdAt?: string; + updatedAt?: string; +} diff --git a/src/app/core/mocks/agent-limit.mocks.ts b/src/app/core/mocks/agent-limit.mocks.ts new file mode 100644 index 0000000..638bc7c --- /dev/null +++ b/src/app/core/mocks/agent-limit.mocks.ts @@ -0,0 +1,38 @@ +import { AgentLimit } from '../interfaces/agent-limit'; + +export const AGENT_LIMITS_MOCK: AgentLimit[] = [ + { + id: crypto.randomUUID(), + code: 'ALC001', + configCode: 'ALC001', + nom: 'REGION LIMITS', + isDefault: true, + actif: true, + betMin: 10_000, + betMax: 10_000_000, + maxBet: 10_000_000, + maxDisburseBet: -1, + airtimeMin: 0, + airtimeMax: 50_000, + createdAt: '2017-06-05T00:00:00.000Z', + createdBy: 'admin', + }, + { + id: crypto.randomUUID(), + code: 'ALC002', + configCode: 'ALC002', + nom: 'INDIV PAY KIOSK 200k', + isDefault: false, + actif: true, + betMin: 10_000, + betMax: 10_000_000, + maxBet: 10_000_000, + maxDisburseBet: 0, + airtimeMin: 100, + airtimeMax: 100_000, + createdAt: '2022-02-01T00:00:00.000Z', + createdBy: 'admin', + }, +]; + + diff --git a/src/app/core/mocks/agent.mocks.ts b/src/app/core/mocks/agent.mocks.ts new file mode 100644 index 0000000..f9eb7e5 --- /dev/null +++ b/src/app/core/mocks/agent.mocks.ts @@ -0,0 +1,65 @@ +// import { Agent } from '../interfaces/agent'; +// import { AGENT_LIMITS_MOCK } from './agent-limit.mocks'; +// import { TPE_MOCK } from '../mocks/tpe.mocks'; + +// export const AGENTS_MOCK: Agent[] = [ +// { +// id: crypto.randomUUID(), +// code: 'ALD001', +// profile: 'AGENT', +// principalCode: 'ALC001', +// caisseProfile: 'ALC001', +// statut: 'ACTIF', +// zone: 'Bamako', +// kiosk: 'K-0001', +// fonction: 'Vendeur', +// dateEmbauche: '2020-03-07T00:00:00.000Z', +// nom: 'Diop', +// prenom: 'Amadou', +// autresNoms: '', +// dateNaissance: '1990-01-01', +// lieuNaissance: 'Bamako', +// ville: 'Bamako', +// adresse: 'Quartier A', +// autoriserAides: false, +// phone: '+22370000001', +// limiteInferieure: 0, +// limiteSuperieure: 10_000_000, +// limiteParTransaction: 1_000_000, +// limiteMinAirtime: 0, +// limiteMaxAirtime: 100_000, +// maxPeripheriques: 5, +// limitId: AGENT_LIMITS_MOCK[0].id, +// nationalite: 'ML', +// cni: 'CNI123456', +// cniDelivreeLe: '2018-06-01', +// cniDelivreeA: 'Bamako', +// residence: 'Bamako', +// statutMarital: 'Marié', +// epoux: 'Aissatou', +// autreTelephone: '+22370000009', +// famille: [ +// { id: crypto.randomUUID(), nom: 'Aissatou', statut: 'Conjointe', dateNaissance: '1991-03-05', sexe: 'F' }, +// { id: crypto.randomUUID(), nom: 'Ibrahim', statut: 'Enfant', dateNaissance: '2015-09-10', sexe: 'M' }, +// ], +// assignedTpeIds: TPE_MOCK.filter((t) => t.statut === 'valide').slice(0, 1).map((t) => t.id), +// createdAt: '2020-03-07T00:00:00.000Z', +// createdBy: 'admin', +// }, +// ...Array.from({ length: 12 }).map((_, i) => ({ +// id: crypto.randomUUID(), +// code: `ALK${String(100 + i).padStart(3, '0')}`, +// profile: 'AGENT', +// statut: i % 5 === 0 ? 'INACTIF' : 'ACTIF', +// nom: `Agent${i + 1}`, +// prenom: 'Test', +// phone: `+2237${(1000000 + i).toString()}`, +// limiteInferieure: 0, +// limiteSuperieure: 10_000_000, +// limiteParTransaction: 500_000, +// limiteMinAirtime: 0, +// limiteMaxAirtime: 100_000, +// maxPeripheriques: 3, +// limitId: AGENT_LIMITS_MOCK[1].id, +// } as Agent)), +// ]; diff --git a/src/app/core/mocks/course.mocks.ts b/src/app/core/mocks/course.mocks.ts new file mode 100644 index 0000000..8bb403f --- /dev/null +++ b/src/app/core/mocks/course.mocks.ts @@ -0,0 +1,197 @@ +import { Course, CourseType, CourseStatut, ResultatStatut } from '../interfaces/course'; +import { REUNIONS_MOCK } from './reunion.mocks'; + +const now = new Date(); +const COURSES_PER_REUNION_BASE = 6; + +function requiredLength(t: CourseType): number { + switch (t) { + case CourseType.TIERCE: + return 3; + case CourseType.QUARTE: + return 4; + case CourseType.QUINTE: + return 5; + default: + return 0; + } +} + +function rngPick(arr: T[], seed: number): T { + const x = Math.abs(Math.sin(seed) * 10000); + const idx = Math.floor((x - Math.floor(x)) * arr.length) % arr.length; + return arr[idx]; +} + +function makeMockResultat( + type: CourseType, + partants: number, + nonPartantsNums: number[], + seed: number +): number[][] { + const req = requiredLength(type); + const np = new Set(nonPartantsNums); + const all = Array.from({ length: partants }, (_, i) => i + 1).filter((n) => !np.has(n)); + const used = new Set(); + const places: number[][] = []; + + const tiePlace = Math.abs(seed) % 10 === 0 ? ((seed % req) + req) % req : -1; + + for (let i = 0; i < req; i++) { + const remaining = all.filter((n) => !used.has(n)); + if (remaining.length === 0) { + places.push([]); + continue; + } + const first = rngPick(remaining, seed + i * 7); + used.add(first); + const slot = [first]; + + if (i === tiePlace) { + const remaining2 = all.filter((n) => !used.has(n)); + if (remaining2.length > 0) { + const second = rngPick(remaining2, seed + i * 13); + used.add(second); + slot.push(second); + slot.sort((a, b) => a - b); + } + } + + places.push(slot); + } + + return places; +} + +const COURSE_NAMES = [ + 'Prix du Delta', + 'Coupe du Fleuve Niger', + 'Trophée du Mandé', + 'Challenge du Nord', + 'Prix de Bamako', + 'Grand Prix de Tombouctou', + 'Prix du Sahara', + 'Trophée du Mali', + 'Prix de la Savane', + 'Course de la Paix', + 'Grand Prix du Sud', + 'Coupe de l’Avenir', + 'Prix du Coton', + 'Prix de la Liberté', + 'Prix du Marché Central', + 'Prix du Rail', + 'Challenge du Faso', + 'Prix du Soleil', + 'Prix du Soudan', + 'Grand Prix du Président', + 'Prix de la Jeunesse', + 'Coupe de la Nation', + 'Prix des Cavaliers', + 'Trophée de l’Unité', + 'Prix du Bénin', + 'Grand Prix de Sikasso', + 'Prix du Commerce', + 'Prix du Plateau', + 'Course des Champions', + 'Trophée de l’Espoir', + 'Prix du Développement', + 'Prix de l’Amitié', + 'Grand Prix International', + 'Prix du Peuple', + 'Prix de la Baie', + 'Trophée des Pionniers', + 'Prix du Littoral', +]; + +const COURSE_TYPES = [CourseType.TIERCE, CourseType.QUARTE, CourseType.QUINTE]; +const COURSE_STATUTS = [ + CourseStatut.CREATED, + CourseStatut.VALIDATED, + CourseStatut.RUNNING, + CourseStatut.CLOSED, + CourseStatut.CANCELED, +]; + +const coursesPerReunion = new Map(); + +const courses: Course[] = []; + +REUNIONS_MOCK.forEach((reunion, reunionIndex) => { + const courseCount = COURSES_PER_REUNION_BASE + (reunionIndex % 2); + const reunionDate = new Date(`${reunion.date}T00:00:00`); + + for (let i = 0; i < courseCount; i++) { + const globalIndex = courses.length; + const type = COURSE_TYPES[(globalIndex + i) % COURSE_TYPES.length]; + const statut = COURSE_STATUTS[(globalIndex + reunionIndex) % COURSE_STATUTS.length]; + + const numberWithinReunion = (coursesPerReunion.get(reunion.id) ?? 0) + 1; + coursesPerReunion.set(reunion.id, numberWithinReunion); + + const dateDebutParis = new Date(reunionDate); + dateDebutParis.setHours(8 + i, 0, 0, 0); + const dateFinParis = new Date(dateDebutParis); + dateFinParis.setHours(dateDebutParis.getHours() + 2); + const dateDepartCourse = new Date(reunionDate); + dateDepartCourse.setHours(12 + i, 30, 0, 0); + + const partants = 10 + ((reunionIndex + i) % 6) * 2; + + const nonPartants: string[] = numberWithinReunion % 4 === 0 ? [crypto.randomUUID()] : []; + + const nonPartantsNums = nonPartants.map((np) => Number(np)); + + let resultat: number[][] | undefined; + let resultatStatut: ResultatStatut = ResultatStatut.NONE; + + if (statut === CourseStatut.CLOSED) { + resultat = makeMockResultat(type, partants, nonPartantsNums, globalIndex * 31); + resultatStatut = ResultatStatut.CONFIRMED; + } else if (statut === CourseStatut.VALIDATED) { + resultat = makeMockResultat(type, partants, nonPartantsNums, globalIndex * 17); + resultatStatut = ResultatStatut.VALIDATED; + } else if (statut === CourseStatut.RUNNING && (globalIndex + reunionIndex) % 3 === 0) { + resultat = makeMockResultat(type, partants, nonPartantsNums, globalIndex * 7); + resultatStatut = ResultatStatut.CREATED; + } + + courses.push({ + id: crypto.randomUUID(), + type, + numero: globalIndex + 1, + nom: `${COURSE_NAMES[(globalIndex + reunionIndex) % COURSE_NAMES.length]} - ${ + reunion.hippodrome.ville + }`, + dateDebutParis: dateDebutParis.toISOString(), + dateFinParis: dateFinParis.toISOString(), + dateDepartCourse: dateDepartCourse.toISOString(), + reunion, + reunionCourse: numberWithinReunion, + particularite: + (globalIndex + reunionIndex) % 2 === 0 + ? 'Course de galop - conditions variées' + : 'Trot attelé - catégorie nationale', + partants, + distance: 2000 + ((reunionIndex + i) % 5) * 200, + condition: + (globalIndex + reunionIndex) % 3 === 0 + ? 'Réservée aux chevaux de 3 ans et plus' + : 'Course mixte - catégorie B', + statut, + nonPartants, + createdBy: `user-${((globalIndex + reunionIndex) % 5) + 1}`, + validatedBy: statut === CourseStatut.VALIDATED ? 'admin-1' : undefined, + createdAt: now.toISOString(), + updatedAt: now.toISOString(), + }); + } +}); + +coursesPerReunion.forEach((count, reunionId) => { + const reunion = REUNIONS_MOCK.find((r) => r.id === reunionId); + if (reunion) { + reunion.totalCourses = count; + } +}); + +export const COURSES_MOCK: Course[] = courses; diff --git a/src/app/core/mocks/hippodrome.mocks.ts b/src/app/core/mocks/hippodrome.mocks.ts new file mode 100644 index 0000000..7ff691e --- /dev/null +++ b/src/app/core/mocks/hippodrome.mocks.ts @@ -0,0 +1,421 @@ +import { Hippodrome } from '../interfaces/hippodrome'; + +export const HIPPODROMES_MOCK: Hippodrome[] = [ + // 🇫🇷 France + { + id: crypto.randomUUID(), + nom: 'Longchamp', + ville: 'Paris', + pays: 'France', + actif: true, + capacite: 50000, + description: 'Célèbre hippodrome parisien accueillant le Prix de l’Arc de Triomphe.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Vincennes', + ville: 'Paris', + pays: 'France', + actif: true, + capacite: 40000, + description: 'Spécialisé dans les courses de trot attelé.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Auteuil', + ville: 'Paris', + pays: 'France', + actif: true, + capacite: 30000, + description: 'Hippodrome de référence pour les courses d’obstacles.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Deauville-La Touques', + ville: 'Deauville', + pays: 'France', + actif: true, + capacite: 20000, + description: 'Station balnéaire accueillant les courses estivales.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Chantilly', + ville: 'Chantilly', + pays: 'France', + actif: true, + capacite: 25000, + description: 'Hippodrome emblématique adossé au château de Chantilly.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Saint-Cloud', + ville: 'Saint-Cloud', + pays: 'France', + actif: true, + capacite: 20000, + description: 'Courses de plat sur herbe, cadre verdoyant.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Cagnes-sur-Mer', + ville: 'Cagnes-sur-Mer', + pays: 'France', + actif: true, + capacite: 15000, + description: 'Hippodrome moderne du sud de la France.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Pau', + ville: 'Pau', + pays: 'France', + actif: true, + capacite: 10000, + description: 'Hippodrome historique du Béarn, courses d’obstacles.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Lyon-Parilly', + ville: 'Lyon', + pays: 'France', + actif: true, + capacite: 18000, + description: 'Hippodrome polyvalent de la région Rhône-Alpes.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Marseille-Borély', + ville: 'Marseille', + pays: 'France', + actif: true, + capacite: 20000, + description: 'Hippodrome emblématique du sud avec vue sur mer.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Nancy-Brabois', + ville: 'Nancy', + pays: 'France', + actif: false, + capacite: 8000, + description: 'Petit hippodrome régional pour courses locales.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇲🇦 Maroc + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Casablanca-Anfa', + ville: 'Casablanca', + pays: 'Maroc', + actif: true, + capacite: 30000, + description: 'Principal hippodrome du Maroc, moderne et actif.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Marrakech', + ville: 'Marrakech', + pays: 'Maroc', + actif: true, + capacite: 20000, + description: 'Installations modernes, climat idéal pour les courses.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome d’El Jadida', + ville: 'El Jadida', + pays: 'Maroc', + actif: true, + capacite: 15000, + description: 'Accueille des compétitions nationales et régionales.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Meknès', + ville: 'Meknès', + pays: 'Maroc', + actif: false, + capacite: 10000, + description: 'En rénovation, ancien centre hippique royal.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Rabat-Souissi', + ville: 'Rabat', + pays: 'Maroc', + actif: true, + capacite: 25000, + description: 'Hippodrome royal accueillant de grands événements.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇸🇳 Sénégal + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Niaga', + ville: 'Dakar', + pays: 'Sénégal', + actif: true, + capacite: 12000, + description: 'Centre principal des courses sénégalaises.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Thiès', + ville: 'Thiès', + pays: 'Sénégal', + actif: false, + capacite: 7000, + description: 'Structure régionale en cours de modernisation.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Saint-Louis', + ville: 'Saint-Louis', + pays: 'Sénégal', + actif: true, + capacite: 9000, + description: 'Traditionnel lieu de courses dans le nord du pays.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇲🇱 Mali + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Bamako', + ville: 'Bamako', + pays: 'Mali', + actif: true, + capacite: 15000, + description: 'Hippodrome national du Mali, centre principal des courses.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Kayes', + ville: 'Kayes', + pays: 'Mali', + actif: true, + capacite: 8000, + description: 'Centre hippique de la première région du Mali.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Sikasso', + ville: 'Sikasso', + pays: 'Mali', + actif: true, + capacite: 7000, + description: 'Hippodrome régional accueillant des compétitions locales.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Ségou', + ville: 'Ségou', + pays: 'Mali', + actif: false, + capacite: 5000, + description: 'Hippodrome en cours de réhabilitation.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Mopti', + ville: 'Mopti', + pays: 'Mali', + actif: true, + capacite: 6000, + description: 'Lieu emblématique des courses régionales du centre.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇨🇮 Côte d’Ivoire + { + id: crypto.randomUUID(), + nom: 'Hippodrome d’Abidjan', + ville: 'Abidjan', + pays: 'Côte d’Ivoire', + actif: true, + capacite: 18000, + description: 'Hippodrome national ivoirien.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Bouaké', + ville: 'Bouaké', + pays: 'Côte d’Ivoire', + actif: false, + capacite: 8000, + description: 'Petit hippodrome local en rénovation.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇧🇪 Belgique + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Wallonie', + ville: 'Mons', + pays: 'Belgique', + actif: true, + capacite: 12000, + description: 'Hippodrome principal du sud de la Belgique.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Kuurne', + ville: 'Kuurne', + pays: 'Belgique', + actif: true, + capacite: 9000, + description: 'Spécialisé dans les courses de trot.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇨🇭 Suisse + { + id: crypto.randomUUID(), + nom: 'Hippodrome d’Avenches', + ville: 'Avenches', + pays: 'Suisse', + actif: true, + capacite: 10000, + description: 'Hippodrome moderne et bien équipé au cœur de la Suisse.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇨🇦 Canada + { + id: crypto.randomUUID(), + nom: 'Hippodrome 3R', + ville: 'Trois-Rivières', + pays: 'Canada', + actif: true, + capacite: 15000, + description: 'Hippodrome historique du Québec.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Québec', + ville: 'Québec', + pays: 'Canada', + actif: false, + capacite: 10000, + description: 'Ancien hippodrome du centre-ville, fermé au public.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇹🇳 Tunisie + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Ksar Saïd', + ville: 'Tunis', + pays: 'Tunisie', + actif: true, + capacite: 20000, + description: 'Hippodrome national tunisien.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Sfax', + ville: 'Sfax', + pays: 'Tunisie', + actif: true, + capacite: 12000, + description: 'Centre hippique régional.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇩🇿 Algérie + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Caroubier', + ville: 'Alger', + pays: 'Algérie', + actif: true, + capacite: 25000, + description: 'Principal hippodrome d’Algérie.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + nom: 'Hippodrome d’Oran', + ville: 'Oran', + pays: 'Algérie', + actif: true, + capacite: 15000, + description: 'Hippodrome côtier moderne.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + + // 🇲🇷 Mauritanie + { + id: crypto.randomUUID(), + nom: 'Hippodrome de Nouakchott', + ville: 'Nouakchott', + pays: 'Mauritanie', + actif: true, + capacite: 10000, + description: 'Unique hippodrome national de Mauritanie.', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, +]; diff --git a/src/app/core/mocks/report.mocks.ts b/src/app/core/mocks/report.mocks.ts new file mode 100644 index 0000000..43b132c --- /dev/null +++ b/src/app/core/mocks/report.mocks.ts @@ -0,0 +1,110 @@ +import { Course } from '../interfaces/course'; +import { + CourseReportDetail, + CourseReportDetailRow, + CourseReportSummary, +} from '../interfaces/report'; +import { COURSES_MOCK } from './course.mocks'; + +function randomInt(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +export function payoutRowsForCourse(c: Course): CourseReportDetailRow[] { + const base: CourseReportDetailRow[] = [ + { + typeGain: 'QUINTE ORDRE', + typeJeu: 'Quinte+', + montant: 2840500, + nombre: randomInt(1, 30), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'QUINTE DESORDRE', + typeJeu: 'Quinte+', + montant: 40000, + nombre: randomInt(300, 5000), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'BONUS 4', + typeJeu: 'Quinte+', + montant: 2000, + nombre: randomInt(5000, 25000), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'REMBOURSEMENT', + typeJeu: 'Quinte+', + montant: 300, + nombre: randomInt(10, 500), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'TIERCE ORDRE', + typeJeu: 'Tierce', + montant: 37000, + nombre: randomInt(100, 2000), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'TIERCE DESORDRE', + typeJeu: 'Tierce', + montant: 6000, + nombre: randomInt(500, 6000), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'TRANSFORME COUPLE', + typeJeu: 'Tierce', + montant: 3000, + nombre: randomInt(200, 2000), + statut: 'Validée', + distributed: false, + externe: false, + }, + { + typeGain: 'TRANSFORME SIMPLE', + typeJeu: 'Tierce', + montant: 1500, + nombre: randomInt(10, 500), + statut: 'Validée', + distributed: false, + externe: false, + }, + ]; + return base; +} + +export const REPORT_SUMMARIES_MOCK: CourseReportSummary[] = COURSES_MOCK.filter( + (c) => c.statut === 'CLOSED' +) + .slice(0, 300) + .map( + (c) => ({ id: c.id, course: c, statut: 'En attente', confirmed: false } as CourseReportSummary) + ); + +export function buildDetailByCourseId(id: string): CourseReportDetail | undefined { + const summary = REPORT_SUMMARIES_MOCK.find((s) => s.id === id); + if (!summary) return undefined; + const rows = payoutRowsForCourse(summary.course as Course); + return { summary, rows } as CourseReportDetail; +} + +// Pre-built rows map for in-memory updates +export const REPORT_DETAILS_MOCK = new Map(); +for (const c of COURSES_MOCK.filter((c) => c.statut === 'CLOSED').slice(0, 300)) { + REPORT_DETAILS_MOCK.set(c.id, payoutRowsForCourse(c)); +} diff --git a/src/app/core/mocks/reunion.mocks.ts b/src/app/core/mocks/reunion.mocks.ts new file mode 100644 index 0000000..2ecb49b --- /dev/null +++ b/src/app/core/mocks/reunion.mocks.ts @@ -0,0 +1,61 @@ +import { Reunion, ReunionStatut } from '../interfaces/reunion'; +import { HIPPODROMES_MOCK } from './hippodrome.mocks'; + +const now = new Date(); + +const REUNIONS_PER_HIPPODROME = 3; +const BASE_DATE = new Date('2025-01-05T14:00:00Z'); +const STATUSES: ReunionStatut[] = [ + ReunionStatut.TERMINEE, + ReunionStatut.EN_COURS, + ReunionStatut.PLANIFIEE, +]; + +const REUNION_TITLES = [ + 'Grand Prix', + 'Challenge Régional', + 'Meeting de la Capitale', + 'Trophée des Champions', + 'Festival Hippique', + 'Prix du Président', + 'Coupe des Nations', + 'Gala des Courses', + 'Trophée de la Ville', + 'Coupe de l’Avenir', + 'Grand Meeting Nocturne', + 'Festival International', +]; + +function slugify(value: string): string { + return value + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-zA-Z0-9]/g, '') + .toUpperCase(); +} + +export const REUNIONS_MOCK: Reunion[] = HIPPODROMES_MOCK.flatMap((hippodrome, hipIndex) => { + const slug = slugify(hippodrome.nom || hippodrome.ville || `HIP${hipIndex + 1}`); + + return Array.from({ length: REUNIONS_PER_HIPPODROME }).map((_, reunionOffset) => { + const globalIndex = hipIndex * REUNIONS_PER_HIPPODROME + reunionOffset; + const date = new Date(BASE_DATE); + date.setDate(BASE_DATE.getDate() + globalIndex * 2); + + const title = REUNION_TITLES[globalIndex % REUNION_TITLES.length]; + const statut = STATUSES[globalIndex % STATUSES.length]; + + return { + id: crypto.randomUUID(), + code: `${slug}-${date.getFullYear()}-${(reunionOffset + 1).toString().padStart(2, '0')}`, + nom: `${title} de ${hippodrome.ville}`, + date: date.toISOString().slice(0, 10), + numero: reunionOffset + 1, + statut, + hippodrome, + totalCourses: 0, + createdAt: now.toISOString(), + updatedAt: now.toISOString(), + } satisfies Reunion; + }); +}); diff --git a/src/app/core/mocks/role.mocks.ts b/src/app/core/mocks/role.mocks.ts new file mode 100644 index 0000000..b649b62 --- /dev/null +++ b/src/app/core/mocks/role.mocks.ts @@ -0,0 +1,144 @@ +import { Permission, Role } from '../interfaces/role'; + +export const PERMISSIONS_MOCK: Permission[] = [ + // Users + { id: 'p1', name: 'USERS_READ', description: 'Voir utilisateurs' }, + { id: 'p2', name: 'USERS_CREATE', description: 'Créer utilisateurs' }, + { id: 'p3', name: 'USERS_UPDATE', description: 'Modifier utilisateurs' }, + { id: 'p4', name: 'USERS_DELETE', description: 'Supprimer utilisateurs' }, + { id: 'p5', name: 'USERS_RESET_PASSWORD', description: 'Réinitialiser mot de passe' }, + { id: 'p6', name: 'USERS_LOCK', description: 'Verrouiller utilisateurs' }, + { id: 'p7', name: 'USERS_UNLOCK', description: 'Déverrouiller utilisateurs' }, + { id: 'p8', name: 'USERS_RESET_2FA', description: 'Réinitialiser 2FA' }, + { id: 'p9', name: 'USERS_CHANGE_ROLE', description: 'Changer de rôle' }, + { id: 'p10', name: 'USERS_CHANGE_STATUS', description: 'Changer de statut' }, + + // Hippodromes + { id: 'p11', name: 'HIPPODROMES_READ', description: 'Voir hippodromes' }, + { id: 'p12', name: 'HIPPODROMES_CREATE', description: 'Créer hippodromes' }, + { id: 'p13', name: 'HIPPODROMES_UPDATE', description: 'Modifier hippodromes' }, + { id: 'p14', name: 'HIPPODROMES_DELETE', description: 'Supprimer hippodromes' }, + + // Reunions + { id: 'p11', name: 'REUNIONS_READ', description: 'Voir reunions' }, + { id: 'p12', name: 'REUNIONS_CREATE', description: 'Créer reunions' }, + { id: 'p13', name: 'REUNIONS_UPDATE', description: 'Modifier reunions' }, + { id: 'p14', name: 'REUNIONS_DELETE', description: 'Supprimer reunions' }, + { id: 'p15', name: 'REUNIONS_PLANIFIEE', description: 'Planifier reunions' }, + { id: 'p17', name: 'REUNIONS_TERMINEE', description: 'Terminer les reunions' }, + { id: 'p18', name: 'REUNIONS_CANCEL', description: 'Annuler les reunions' }, + + // Courses + { id: 'p19', name: 'COURSES_READ', description: 'Voir courses' }, + { id: 'p20', name: 'COURSES_CREATE', description: 'Créer courses' }, + { id: 'p21', name: 'COURSES_UPDATE', description: 'Modifier courses' }, + { id: 'p22', name: 'COURSES_DELETE', description: 'Supprimer courses' }, + { id: 'p23', name: 'COURSES_VALIDATE', description: 'Valider courses' }, + { id: 'p24', name: 'COURSES_CONFIRM', description: 'Confirmer courses' }, + { id: 'p25', name: 'COURSES_CLOSE', description: 'Clôturer courses' }, + { id: 'p26', name: 'COURSES_CANCEL', description: 'Annuler courses' }, + + // TPE + { id: 'p27', name: 'TPE_READ', description: 'Voir TPE' }, + { id: 'p28', name: 'TPE_CREATE', description: 'Créer TPE' }, + { id: 'p29', name: 'TPE_UPDATE', description: 'Modifier TPE' }, + { id: 'p30', name: 'TPE_DELETE', description: 'Supprimer TPE' }, + { id: 'p31', name: 'TPE_ASSIGN', description: 'Assigner TPE' }, + { id: 'p32', name: 'TPE_UNASSIGN', description: 'Déassigner TPE' }, + + // Agents + { id: 'p33', name: 'AGENTS_READ', description: 'Voir agents' }, + { id: 'p34', name: 'AGENTS_CREATE', description: 'Créer agents' }, + { id: 'p35', name: 'AGENTS_UPDATE', description: 'Modifier agents' }, + { id: 'p36', name: 'AGENTS_DELETE', description: 'Supprimer agents' }, + { id: 'p37', name: 'AGENTS_ASSIGN', description: 'Assigner agents' }, + { id: 'p38', name: 'AGENTS_UNASSIGN', description: 'Déassigner agents' }, + { id: 'p39', name: 'AGENTS_ASSIGN_TPE', description: 'Assigner TPE à agents' }, + { id: 'p40', name: 'AGENTS_UNASSIGN_TPE', description: 'Déassigner TPE à agents' }, + + // Familles Agents + { id: 'p41', name: 'AGENT_FAMILIES_READ', description: 'Voir familles agents' }, + { id: 'p42', name: 'AGENT_FAMILIES_CREATE', description: 'Créer familles agents' }, + { id: 'p43', name: 'AGENT_FAMILIES_UPDATE', description: 'Modifier familles agents' }, + { id: 'p44', name: 'AGENT_FAMILIES_DELETE', description: 'Supprimer familles agents' }, + + // Limites Agents + { id: 'p41', name: 'AGENT_LIMITS_READ', description: 'Voir limites agents' }, + { id: 'p42', name: 'AGENT_LIMITS_CREATE', description: 'Créer limites agents' }, + { id: 'p43', name: 'AGENT_LIMITS_UPDATE', description: 'Modifier limites agents' }, + { id: 'p44', name: 'AGENT_LIMITS_DELETE', description: 'Supprimer limites agents' }, + { id: 'p45', name: 'AGENT_LIMITS_DEFAULTED', description: 'Définir limites agents par défaut' }, + + // Permissions + { id: 'p31', name: 'PERMISSIONS_READ', description: 'Voir permissions' }, + { id: 'p32', name: 'PERMISSIONS_CREATE', description: 'Créer permissions' }, + { id: 'p33', name: 'PERMISSIONS_UPDATE', description: 'Modifier permissions' }, + { id: 'p34', name: 'PERMISSIONS_DELETE', description: 'Supprimer permissions' }, + { id: 'p35', name: 'PERMISSIONS_ASSIGN', description: 'Assigner permissions' }, + { id: 'p36', name: 'PERMISSIONS_UNASSIGN', description: 'Déassigner permissions' }, + + // Roles + { id: 'p37', name: 'ROLES_READ', description: 'Voir rôles' }, + { id: 'p38', name: 'ROLES_CREATE', description: 'Créer rôles' }, + { id: 'p39', name: 'ROLES_UPDATE', description: 'Modifier rôles' }, + { id: 'p40', name: 'ROLES_DELETE', description: 'Supprimer rôles' }, + { id: 'p41', name: 'ROLES_ASSIGN', description: 'Assigner rôles' }, + { id: 'p42', name: 'ROLES_UNASSIGN', description: 'Déassigner rôles' }, + { id: 'p43', name: 'ROLES_ASSIGN_PERMISSIONS', description: 'Assigner permissions à rôles' }, + { id: 'p44', name: 'ROLES_UNASSIGN_PERMISSIONS', description: 'Déassigner permissions à rôles' }, + + // Users + { id: 'p45', name: 'USERS_READ', description: 'Voir utilisateurs' }, + { id: 'p46', name: 'USERS_CREATE', description: 'Créer utilisateurs' }, + { id: 'p47', name: 'USERS_UPDATE', description: 'Modifier utilisateurs' }, + { id: 'p48', name: 'USERS_DELETE', description: 'Supprimer utilisateurs' }, + { id: 'p49', name: 'USERS_RESET_PASSWORD', description: 'Réinitialiser mot de passe' }, + { id: 'p50', name: 'USERS_LOCK', description: 'Verrouiller utilisateurs' }, + { id: 'p51', name: 'USERS_UNLOCK', description: 'Déverrouiller utilisateurs' }, +]; + +export const ROLES_MOCK: Role[] = [ + { + id: crypto.randomUUID(), + name: 'Superadmin', + description: 'Accès total à toute la plateforme', + permissions: [...PERMISSIONS_MOCK], + createdAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + name: 'Administrateur Hippique', + description: 'Gestion des courses et résultats', + permissions: PERMISSIONS_MOCK.filter((p) => + [ + 'COURSES_READ', + 'COURSES_MANAGE', + 'RESULTATS_VALIDATE', + 'RESULTATS_CONFIRM', + 'USERS_READ', + ].includes(p.name) + ), + createdAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + name: 'Agent Commercial', + description: 'Gestion commerciale', + permissions: PERMISSIONS_MOCK.filter((p) => ['USERS_READ', 'COURSES_READ'].includes(p.name)), + createdAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + name: 'Gestionnaires Réseau', + description: 'Gestion du réseau et consultation', + permissions: PERMISSIONS_MOCK.filter((p) => ['USERS_READ', 'COURSES_READ'].includes(p.name)), + createdAt: new Date().toISOString(), + }, + { + id: crypto.randomUUID(), + name: 'Support PJP', + description: 'Support et consultation', + permissions: PERMISSIONS_MOCK.filter((p) => ['USERS_READ', 'COURSES_READ'].includes(p.name)), + createdAt: new Date().toISOString(), + }, +]; diff --git a/src/app/core/mocks/tpe.mocks.ts b/src/app/core/mocks/tpe.mocks.ts new file mode 100644 index 0000000..d44f2be --- /dev/null +++ b/src/app/core/mocks/tpe.mocks.ts @@ -0,0 +1,36 @@ +// import { TpeDevice } from '../interfaces/tpe'; + +// const brands = ['MobioT', 'Pax', 'Ingenico', 'Sunmi']; +// const models = ['MP4+', 'A920', 'Move5000', 'P2']; + +// function randomImei(i: number): string { +// return `${String(i).padStart(3, '0')}${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`; +// } + +// export const TPE_MOCK: TpeDevice[] = [ +// { +// id: crypto.randomUUID(), +// imei: '0000ac43ad03c7fd', +// serial: 'S-10001', +// type: 'POS', +// marque: 'MobioT', +// modele: 'MP4+', +// statut: 'valide', +// assigne: true, +// createdAt: new Date().toISOString(), +// }, +// ...Array.from({ length: 24 }).map( +// (_, i) => +// ({ +// id: crypto.randomUUID(), +// imei: randomImei(i + 1), +// serial: `S-${10002 + i}`, +// type: 'POS', +// marque: brands[i % brands.length], +// modele: models[i % models.length], +// statut: 'valide', +// assigne: i % 7 === 0, +// createdAt: new Date().toISOString(), +// } as TpeDevice) +// ), +// ]; diff --git a/src/app/core/mocks/user.mocks.ts b/src/app/core/mocks/user.mocks.ts new file mode 100644 index 0000000..da1366f --- /dev/null +++ b/src/app/core/mocks/user.mocks.ts @@ -0,0 +1,69 @@ +import { User } from '../interfaces/user'; +import { ROLES_MOCK } from '../mocks/role.mocks'; + +export const USERS_MOCK: User[] = [ + { + id: crypto.randomUUID(), + nom: 'Maiga', + prenom: 'Abdoulaye', + identifiant: 'maiga', + matriculeAgent: '91111', + roleId: ROLES_MOCK[1].id, + role: ROLES_MOCK[1], + restrictionConnexion: false, + restrictionAutomatique: false, + nombreIpAutorise: 0, + nombreIpAutoAutorise: 0, + statut: 'Annulé', + derniereConnexion: '2021-05-10T09:00:00.000Z', + createdAt: '2020-01-01T00:00:00.000Z', + }, + { + id: crypto.randomUUID(), + nom: 'Toulema', + prenom: 'Moussa', + identifiant: 'toulema', + matriculeAgent: '91111', + roleId: ROLES_MOCK[1].id, + role: ROLES_MOCK[1], + restrictionConnexion: false, + restrictionAutomatique: false, + nombreIpAutorise: 0, + nombreIpAutoAutorise: 0, + statut: 'Annulé', + derniereConnexion: '2023-09-01T10:10:00.000Z', + }, + { + id: crypto.randomUUID(), + nom: 'Toure', + prenom: 'Ibrahim', + identifiant: 'toure', + matriculeAgent: '91111', + roleId: ROLES_MOCK[1].id, + role: ROLES_MOCK[1], + restrictionConnexion: false, + restrictionAutomatique: false, + nombreIpAutorise: 0, + nombreIpAutoAutorise: 0, + statut: 'Annulé', + derniereConnexion: '2022-05-05T08:00:00.000Z', + }, + ...Array.from({ length: 20 }).map( + (_, i) => + ({ + id: crypto.randomUUID(), + nom: `Utilisateur${i + 1}`, + prenom: 'Demo', + identifiant: `user${i + 1}`, + matriculeAgent: String(90000 + i), + roleId: (i % 3 === 0 ? ROLES_MOCK[3] : ROLES_MOCK[4]).id, + role: (i % 3 === 0 ? ROLES_MOCK[3] : ROLES_MOCK[4]), + restrictionConnexion: false, + restrictionAutomatique: false, + nombreIpAutorise: i % 2 === 0 ? 10 : 8, + nombreIpAutoAutorise: i % 2 === 0 ? 10 : 8, + statut: i % 5 === 0 ? 'Suspendu' : 'Actif', + derniereConnexion: new Date(2024, i % 12, (i % 28) + 1).toISOString(), + } as User) + ), +]; diff --git a/src/app/core/services/agent-family-member.ts b/src/app/core/services/agent-family-member.ts new file mode 100644 index 0000000..819c448 --- /dev/null +++ b/src/app/core/services/agent-family-member.ts @@ -0,0 +1,236 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { AgentFamilyMember } from '../interfaces/agent'; +import { environment } from 'src/environments/environment.development'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/agent-family-members'; + +// Interface to match the API response structure +interface AgentFamilyMemberApiResponse { + id: number; + agentId: number; + nom: string; + statut?: string; + dateNaissance?: string; + sexe?: string; +} + +@Injectable({ providedIn: 'root' }) +export class AgentFamilyMemberService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor(private http: HttpClient) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // Transform API response to AgentFamilyMember + private transformMember(apiMember: AgentFamilyMemberApiResponse): AgentFamilyMember { + return { + id: String(apiMember.id), + agentId: String(apiMember.agentId), + nom: apiMember.nom, + statut: apiMember.statut, + dateNaissance: apiMember.dateNaissance, + sexe: apiMember.sexe as 'M' | 'F' | undefined, + }; + } + + // Helper method to convert date string to LocalDateTime format (YYYY-MM-DDTHH:mm:ss) + private formatDateForApi(dateStr: string | undefined): string | undefined { + if (!dateStr) return undefined; + // If already in ISO format with time, return as is + if (dateStr.includes('T') || dateStr.includes(' ')) { + return dateStr; + } + // If only date (YYYY-MM-DD), add time component + if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) { + return `${dateStr}T00:00:00`; + } + return dateStr; + } + + // Transform AgentFamilyMember to API payload + private transformToApiPayload(member: Partial): any { + const payload: any = {}; + if (member.agentId !== undefined) payload.agentId = Number(member.agentId); + if (member.nom !== undefined) payload.nom = member.nom; + if (member.statut !== undefined) payload.statut = member.statut; + if (member.dateNaissance !== undefined) payload.dateNaissance = this.formatDateForApi(member.dateNaissance); + if (member.sexe !== undefined) payload.sexe = member.sexe; + return payload; + } + + // GET /api/v1/agent-family-members/{id} - Get by ID + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiMember) => this.transformMember(apiMember)), + catchError((err) => { + console.error(`Error fetching agent family member ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // GET /api/v1/agent-family-members - List all + list(): Observable { + if (USE_SERVER) { + return this.http + .get(this.apiUrl, { headers: this.getNgrokHeaders() }) + .pipe( + map((list) => list.map((apiMember) => this.transformMember(apiMember))), + catchError((err) => { + console.error('Error fetching agent family members:', err); + return of([]); + }) + ); + } + return of([]); + } + + // POST /api/v1/agent-family-members - Create + create(payload: Omit): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .post(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiMember) => this.transformMember(apiMember)), + catchError((err) => { + console.error('Error creating agent family member:', err); + throw err; + }) + ); + } + throw new Error('Server mode is required'); + } + + // PUT /api/v1/agent-family-members/{id} - Update + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .put(`${this.apiUrl}/${id}`, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiMember) => this.transformMember(apiMember)), + catchError((err) => { + console.error(`Error updating agent family member ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // DELETE /api/v1/agent-family-members/{id} - Delete + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting agent family member ${id}:`, err); + return of(false); + }) + ); + } + return of(false); + } + + // GET /api/v1/agent-family-members/statut/{statut} - List by statut + getByStatut(statut: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/statut/${statut}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((list) => list.map((apiMember) => this.transformMember(apiMember))), + catchError((err) => { + console.error(`Error fetching agent family members by statut ${statut}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agent-family-members/sexe/{sexe} - List by sexe + getBySexe(sexe: 'M' | 'F'): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/sexe/${sexe}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((list) => list.map((apiMember) => this.transformMember(apiMember))), + catchError((err) => { + console.error(`Error fetching agent family members by sexe ${sexe}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agent-family-members/search - Search by keyword + search(query: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/search`, { + params: { q: query.trim() }, + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiMember) => this.transformMember(apiMember))), + catchError((err) => { + console.error(`Error searching agent family members with query ${query}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agent-family-members/nom/{nom} - List by nom + getByNom(nom: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/nom/${nom}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((list) => list.map((apiMember) => this.transformMember(apiMember))), + catchError((err) => { + console.error(`Error fetching agent family members by nom ${nom}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // Get family members by agentId (filter from list) + getByAgentId(agentId: string): Observable { + if (USE_SERVER) { + return this.list().pipe( + map((list) => list.filter((member) => member.agentId === agentId)), + catchError((err) => { + console.error(`Error fetching agent family members by agentId ${agentId}:`, err); + return of([]); + }) + ); + } + return of([]); + } +} + diff --git a/src/app/core/services/agent-limit.ts b/src/app/core/services/agent-limit.ts new file mode 100644 index 0000000..15d02c1 --- /dev/null +++ b/src/app/core/services/agent-limit.ts @@ -0,0 +1,335 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, of, forkJoin } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { AgentLimit } from '../interfaces/agent-limit'; +import { environment } from 'src/environments/environment.development'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { ListParams, PagedResult } from '@shared/paging/paging'; +import { AgentService } from './agent'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/agent-limits'; + +// Interface to match the API response structure +interface AgentLimitApiResponse { + id: number; + code: string; + configCode: string; + nom: string; + isDefault: boolean; + actif: boolean; + betMin?: number; + betMax?: number; + maxBet?: number; + maxDisburseBet?: number; + airtimeMin?: number; + airtimeMax?: number; + createdAt?: string; + createdBy?: string; + default?: boolean; +} + +@Injectable({ providedIn: 'root' }) +export class AgentLimitService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor(private http: HttpClient, private agentService: AgentService) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // Transform API response to AgentLimit + private transformLimit(apiLimit: AgentLimitApiResponse): AgentLimit { + return { + id: String(apiLimit.id), + code: apiLimit.code, + configCode: apiLimit.configCode, + nom: apiLimit.nom, + isDefault: apiLimit.isDefault ?? apiLimit.default ?? false, + actif: apiLimit.actif, + betMin: apiLimit.betMin, + betMax: apiLimit.betMax, + maxBet: apiLimit.maxBet, + maxDisburseBet: apiLimit.maxDisburseBet, + airtimeMin: apiLimit.airtimeMin, + airtimeMax: apiLimit.airtimeMax, + createdAt: apiLimit.createdAt, + createdBy: apiLimit.createdBy, + }; + } + + // Transform AgentLimit to API payload + private transformToApiPayload(limit: Partial): any { + const payload: any = {}; + if (limit.code !== undefined) payload.code = limit.code; + if (limit.configCode !== undefined) payload.configCode = limit.configCode; + if (limit.nom !== undefined) payload.nom = limit.nom; + if (limit.isDefault !== undefined) { + payload.isDefault = limit.isDefault; + payload.default = limit.isDefault; + } + if (limit.actif !== undefined) payload.actif = limit.actif; + if (limit.betMin !== undefined) payload.betMin = limit.betMin; + if (limit.betMax !== undefined) payload.betMax = limit.betMax; + if (limit.maxBet !== undefined) payload.maxBet = limit.maxBet; + if (limit.maxDisburseBet !== undefined) payload.maxDisburseBet = limit.maxDisburseBet; + if (limit.airtimeMin !== undefined) payload.airtimeMin = limit.airtimeMin; + if (limit.airtimeMax !== undefined) payload.airtimeMax = limit.airtimeMax; + if (limit.createdBy !== undefined) payload.createdBy = limit.createdBy; + return payload; + } + + // GET /api/v1/agent-limits/{id} - Get by ID + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiLimit) => this.transformLimit(apiLimit)), + catchError((err) => { + console.error(`Error fetching agent limit ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // GET /api/v1/agent-limits - List all + list(params?: ListParams): Observable> { + if (USE_SERVER) { + let httpParams = new HttpParams(); + if (params) { + if (params.page) httpParams = httpParams.set('page', params.page.toString()); + if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString()); + if (params.search) httpParams = httpParams.set('search', params.search); + if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey); + if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir); + } + + return this.http + .get(this.apiUrl, { + params: httpParams, + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => { + const limits = list.map((apiLimit) => this.transformLimit(apiLimit)); + // If pagination params provided, return paginated result + if (params) { + return normalizePage( + { data: limits, meta: { total: limits.length } }, + params.page || 1, + params.perPage || 10 + ); + } + // Otherwise return all as single page + return normalizePage( + { data: limits, meta: { total: limits.length } }, + 1, + limits.length + ); + }), + catchError((err) => { + console.error('Error fetching agent limits:', err); + return of(normalizePage({ data: [], meta: { total: 0 } }, 1, 10)); + }) + ); + } + return of(normalizePage({ data: [], meta: { total: 0 } }, 1, 10)); + } + + // POST /api/v1/agent-limits - Create + create(payload: Omit): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .post(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiLimit) => { + const limit = this.transformLimit(apiLimit); + // If this limit is set as default, handle default assignment + if (limit.isDefault) { + return this.handleDefaultLimitChange(limit.id).pipe(map(() => limit)); + } + return of(limit); + }), + catchError((err) => { + console.error('Error creating agent limit:', err); + throw err; + }) + ); + } + throw new Error('Server mode is required'); + } + + // PUT /api/v1/agent-limits/{id} - Update + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + // Check if isDefault is being changed to true + const isSettingDefault = payload.isDefault === true; + const wasDefault = payload.isDefault !== undefined; + + return this.http + .put(`${this.apiUrl}/${id}`, this.transformToApiPayload(payload), { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((apiLimit) => { + const limit = this.transformLimit(apiLimit); + // If this limit is being set as default, handle default assignment + if (isSettingDefault) { + return this.handleDefaultLimitChange(limit.id).pipe(map(() => limit)); + } + return of(limit); + }), + catchError((err) => { + console.error(`Error updating agent limit ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // Helper method to handle default limit changes + // When a limit is set as default: + // 1. Find the previous default limit and unset it (preserving all other fields) + // 2. Assign the new default limit to all agents + private handleDefaultLimitChange(newDefaultLimitId: string): Observable { + // First, find the previous default limit + return this.list({ + page: 1, + perPage: 1000, + search: '', + sortKey: 'code', + sortDir: 'asc', + } as any).pipe( + switchMap((result) => { + const limits = result.data; + const previousDefault = limits.find((l) => l.isDefault && l.id !== newDefaultLimitId); + + const operations: Observable[] = []; + + // If there's a previous default, unset it while preserving all other fields + if (previousDefault) { + // Create a payload with all fields from previousDefault, but with isDefault set to false + // This ensures we preserve all existing data + const updatePayload: Partial = { + code: previousDefault.code, + configCode: previousDefault.configCode, + nom: previousDefault.nom, + isDefault: false, + actif: previousDefault.actif, + betMin: previousDefault.betMin, + betMax: previousDefault.betMax, + maxBet: previousDefault.maxBet, + maxDisburseBet: previousDefault.maxDisburseBet, + airtimeMin: previousDefault.airtimeMin, + airtimeMax: previousDefault.airtimeMax, + }; + + // Use the update method with the full payload + operations.push( + this.update(previousDefault.id, updatePayload).pipe( + map(() => true), + catchError((err) => { + console.error(`Error unsetting previous default limit ${previousDefault.id}:`, err); + return of(null); + }) + ) + ); + } + + // Assign the new default limit to all agents + operations.push(this.agentService.updateAllAgentsLimitId(newDefaultLimitId)); + + return forkJoin(operations).pipe( + map(() => true), + catchError((err) => { + console.error('Error handling default limit change:', err); + return of(false); + }) + ); + }), + catchError((err) => { + console.error('Error fetching limits for default change:', err); + return of(false); + }) + ); + } + + // DELETE /api/v1/agent-limits/{id} - Delete + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting agent limit ${id}:`, err); + return of(false); + }) + ); + } + return of(false); + } + + // GET /api/v1/agent-limits/search/{nom} - Search by nom + search(query: string): Observable { + if (USE_SERVER) { + const searchTerm = encodeURIComponent(query.trim()); + return this.http + .get(`${this.apiUrl}/search/${searchTerm}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiLimit) => this.transformLimit(apiLimit))), + catchError((err) => { + console.error(`Error searching agent limits with query ${query}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agent-limits/actif/{actif} - List by actif status + getByActif(actif: boolean): Observable { + if (USE_SERVER) { + if (actif) { + return this.http + .get(`${this.apiUrl}/actif`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiLimit) => this.transformLimit(apiLimit))), + catchError((err) => { + console.error(`Error fetching agent limits by actif ${actif}:`, err); + return of([]); + }) + ); + } else { + return this.http + .get(`${this.apiUrl}/inactif`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiLimit) => this.transformLimit(apiLimit))), + catchError((err) => { + console.error(`Error fetching agent limits by actif ${actif}:`, err); + return of([]); + }) + ); + } + } + return of([]); + } +} diff --git a/src/app/core/services/agent.ts b/src/app/core/services/agent.ts new file mode 100644 index 0000000..78dbac5 --- /dev/null +++ b/src/app/core/services/agent.ts @@ -0,0 +1,484 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, of, forkJoin } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { Agent, AgentStatus } from '../interfaces/agent'; +import { TpeDevice, TpeStatus, TpeType } from '../interfaces/tpe'; +import { environment } from 'src/environments/environment.development'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { ListParams, PagedResult } from '@shared/paging/paging'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/agents'; + +// Interface to match the API response structure for TPE (nested in Agent) +// Note: When TPE is nested in Agent's tpes array, the agent field might be omitted or be a reference +interface TpeApiResponse { + id: number; + imei: string; + serial: string; + type: string; + marque: string; + modele: string; + statut: string; + agent?: any; // Can be Agent object or string reference, we'll handle it in transformTpe + assigne: boolean; + createdAt?: string; + updatedAt?: string; +} + +// Interface to match the API response structure +interface AgentApiResponse { + id: number; + code: string; + profile: string; + principalCode?: string; + caisseProfile?: string; + statut: string; + zone?: string; + kiosk?: string; + fonction?: string; + dateEmbauche?: string; + nom: string; + prenom: string; + autresNoms?: string; + dateNaissance?: string; + lieuNaissance?: string; + ville?: string; + adresse?: string; + autoriserAides?: boolean; + phone: string; + pin?: string; + limiteInferieure?: number; + limiteSuperieure?: number; + limiteParTransaction?: number; + limiteMinAirtime?: number; + limiteMaxAirtime?: number; + maxPeripheriques?: number; + limitId?: number; + nationalite?: string; + cni?: string; + cniDelivreeLe?: string; + cniDelivreeA?: string; + residence?: string; + autreAdresse1?: string; + statutMarital?: string; + epoux?: string; + autreTelephone?: string; + tpes?: TpeApiResponse[]; + createdAt?: string; + updatedAt?: string; + createdBy?: string; +} + +@Injectable({ providedIn: 'root' }) +export class AgentService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor(private http: HttpClient) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // Transform API TPE response to TpeDevice + private transformTpe(apiTpe: TpeApiResponse): TpeDevice { + const transformStatut = (apiStatut: string): TpeStatus => { + const upperStatut = apiStatut.toUpperCase() as TpeStatus; + const validStatuses: TpeStatus[] = [ + 'VALIDE', + 'INVALIDE', + 'EN_PANNE', + 'BLOQUE', + 'DISPONIBLE', + 'AFFECTE', + 'EN_MAINTENANCE', + 'HORS_SERVICE', + 'VOLE', + ]; + return validStatuses.includes(upperStatut) ? upperStatut : 'INVALIDE'; + }; + + // Transform agent if it's an object (not just a string reference) + let transformedAgent: Agent | undefined = undefined; + if (apiTpe.agent && typeof apiTpe.agent === 'object' && apiTpe.agent.id) { + // If agent is a full object, transform it + transformedAgent = this.transformAgent(apiTpe.agent as any); + } + + return { + id: String(apiTpe.id), + imei: apiTpe.imei, + serial: apiTpe.serial, + type: apiTpe.type as TpeType, + marque: apiTpe.marque, + modele: apiTpe.modele, + statut: transformStatut(apiTpe.statut), + agent: transformedAgent, + assigne: apiTpe.assigne, + createdAt: apiTpe.createdAt, + updatedAt: apiTpe.updatedAt, + }; + } + + // Transform API response to Agent + private transformAgent(apiAgent: AgentApiResponse): Agent { + return { + id: String(apiAgent.id), + code: apiAgent.code, + profile: apiAgent.profile, + principalCode: apiAgent.principalCode, + caisseProfile: apiAgent.caisseProfile, + statut: apiAgent.statut as AgentStatus, + zone: apiAgent.zone, + kiosk: apiAgent.kiosk, + fonction: apiAgent.fonction, + dateEmbauche: apiAgent.dateEmbauche, + nom: apiAgent.nom, + prenom: apiAgent.prenom, + autresNoms: apiAgent.autresNoms, + dateNaissance: apiAgent.dateNaissance, + lieuNaissance: apiAgent.lieuNaissance, + ville: apiAgent.ville, + adresse: apiAgent.adresse, + autoriserAides: apiAgent.autoriserAides, + phone: apiAgent.phone, + pin: apiAgent.pin, + limiteInferieure: apiAgent.limiteInferieure, + limiteSuperieure: apiAgent.limiteSuperieure, + limiteParTransaction: apiAgent.limiteParTransaction, + limiteMinAirtime: apiAgent.limiteMinAirtime, + limiteMaxAirtime: apiAgent.limiteMaxAirtime, + maxPeripheriques: apiAgent.maxPeripheriques, + limitId: apiAgent.limitId ? String(apiAgent.limitId) : undefined, + nationalite: apiAgent.nationalite, + cni: apiAgent.cni, + cniDelivreeLe: apiAgent.cniDelivreeLe, + cniDelivreeA: apiAgent.cniDelivreeA, + residence: apiAgent.residence, + autreAdresse1: apiAgent.autreAdresse1, + statutMarital: apiAgent.statutMarital, + epoux: apiAgent.epoux, + autreTelephone: apiAgent.autreTelephone, + tpes: apiAgent.tpes?.map((tpe) => { + const transformed = this.transformTpe(tpe); + return transformed; + }), + createdAt: apiAgent.createdAt, + updatedAt: apiAgent.updatedAt, + createdBy: apiAgent.createdBy, + }; + } + + // Helper method to convert date string to LocalDateTime format (YYYY-MM-DDTHH:mm:ss) + private formatDateForApi(dateStr: string | undefined): string | undefined { + if (!dateStr) return undefined; + // If already in ISO format with time, return as is + if (dateStr.includes('T') || dateStr.includes(' ')) { + return dateStr; + } + // If only date (YYYY-MM-DD), add time component + if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) { + return `${dateStr}T00:00:00`; + } + return dateStr; + } + + // Transform Agent to API payload + private transformToApiPayload(agent: Partial): any { + const payload: any = {}; + if (agent.code !== undefined) payload.code = agent.code; + if (agent.profile !== undefined) payload.profile = agent.profile; + if (agent.principalCode !== undefined) payload.principalCode = agent.principalCode; + if (agent.caisseProfile !== undefined) payload.caisseProfile = agent.caisseProfile; + if (agent.statut !== undefined) payload.statut = agent.statut; + if (agent.zone !== undefined) payload.zone = agent.zone; + if (agent.kiosk !== undefined) payload.kiosk = agent.kiosk; + if (agent.fonction !== undefined) payload.fonction = agent.fonction; + if (agent.dateEmbauche !== undefined) + payload.dateEmbauche = this.formatDateForApi(agent.dateEmbauche); + if (agent.nom !== undefined) payload.nom = agent.nom; + if (agent.prenom !== undefined) payload.prenom = agent.prenom; + if (agent.autresNoms !== undefined) payload.autresNoms = agent.autresNoms; + if (agent.dateNaissance !== undefined) + payload.dateNaissance = this.formatDateForApi(agent.dateNaissance); + if (agent.lieuNaissance !== undefined) payload.lieuNaissance = agent.lieuNaissance; + if (agent.ville !== undefined) payload.ville = agent.ville; + if (agent.adresse !== undefined) payload.adresse = agent.adresse; + if (agent.autoriserAides !== undefined) payload.autoriserAides = agent.autoriserAides; + if (agent.phone !== undefined) payload.phone = agent.phone; + if (agent.pin !== undefined) payload.pin = agent.pin; + if (agent.limiteInferieure !== undefined) payload.limiteInferieure = agent.limiteInferieure; + if (agent.limiteSuperieure !== undefined) payload.limiteSuperieure = agent.limiteSuperieure; + if (agent.limiteParTransaction !== undefined) + payload.limiteParTransaction = agent.limiteParTransaction; + if (agent.limiteMinAirtime !== undefined) payload.limiteMinAirtime = agent.limiteMinAirtime; + if (agent.limiteMaxAirtime !== undefined) payload.limiteMaxAirtime = agent.limiteMaxAirtime; + if (agent.maxPeripheriques !== undefined) payload.maxPeripheriques = agent.maxPeripheriques; + if (agent.limitId !== undefined) + payload.limitId = agent.limitId ? Number(agent.limitId) : undefined; + if (agent.nationalite !== undefined) payload.nationalite = agent.nationalite; + if (agent.cni !== undefined) payload.cni = agent.cni; + if (agent.cniDelivreeLe !== undefined) + payload.cniDelivreeLe = this.formatDateForApi(agent.cniDelivreeLe); + if (agent.cniDelivreeA !== undefined) payload.cniDelivreeA = agent.cniDelivreeA; + if (agent.residence !== undefined) payload.residence = agent.residence; + if (agent.autreAdresse1 !== undefined) payload.autreAdresse1 = agent.autreAdresse1; + if (agent.statutMarital !== undefined) payload.statutMarital = agent.statutMarital; + if (agent.epoux !== undefined) payload.epoux = agent.epoux; + if (agent.autreTelephone !== undefined) payload.autreTelephone = agent.autreTelephone; + if (agent.createdBy !== undefined) payload.createdBy = agent.createdBy; + // Include tpes if provided - transform to API format + if (agent.tpes !== undefined) { + payload.tpes = agent.tpes.map((tpe) => ({ + id: tpe.id ? Number(tpe.id) : undefined, + imei: tpe.imei, + serial: tpe.serial, + type: tpe.type, + marque: tpe.marque, + modele: tpe.modele, + statut: tpe.statut, + agent: undefined, // Will be set by backend + assigne: tpe.assigne, + createdAt: tpe.createdAt, + updatedAt: tpe.updatedAt, + })); + } + return payload; + } + + // GET /api/v1/agents/{id} - Get by ID + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiAgent) => this.transformAgent(apiAgent)), + catchError((err) => { + console.error(`Error fetching agent ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // GET /api/v1/agents - List all + list(params?: ListParams): Observable> { + if (USE_SERVER) { + let httpParams = new HttpParams(); + if (params) { + if (params.page) httpParams = httpParams.set('page', params.page.toString()); + if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString()); + if (params.search) httpParams = httpParams.set('search', params.search); + if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey); + if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir); + } + + return this.http + .get(this.apiUrl, { + params: httpParams, + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => { + const agents = list.map((apiAgent) => { + const transformed = this.transformAgent(apiAgent); + return transformed; + }); + // If pagination params provided, return paginated result + if (params) { + return normalizePage( + { data: agents, meta: { total: agents.length } }, + params.page || 1, + params.perPage || 10 + ); + } + // Otherwise return all as single page + return normalizePage( + { data: agents, meta: { total: agents.length } }, + 1, + agents.length + ); + }), + catchError((err) => { + console.error('Error fetching agents:', err); + return of(normalizePage({ data: [], meta: { total: 0 } }, 1, 10)); + }) + ); + } + return of(normalizePage({ data: [], meta: { total: 0 } }, 1, 10)); + } + + // POST /api/v1/agents - Create + create(payload: Omit): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .post(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiAgent) => this.transformAgent(apiAgent)), + catchError((err) => { + console.error('Error creating agent:', err); + throw err; + }) + ); + } + throw new Error('Server mode is required'); + } + + // PUT /api/v1/agents/{id} - Update + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .put(`${this.apiUrl}/${id}`, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((apiAgent) => this.transformAgent(apiAgent)), + catchError((err) => { + console.error(`Error updating agent ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // DELETE /api/v1/agents/{id} - Delete + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting agent ${id}:`, err); + return of(false); + }) + ); + } + return of(false); + } + + // GET /api/v1/agents/ville/{ville} - List by ville + getByVille(ville: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/ville/${ville}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiAgent) => this.transformAgent(apiAgent))), + catchError((err) => { + console.error(`Error fetching agents by ville ${ville}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agents/statut/{statut} - List by statut + getByStatut(statut: AgentStatus): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/statut/${statut}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiAgent) => this.transformAgent(apiAgent))), + catchError((err) => { + console.error(`Error fetching agents by statut ${statut}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agents/search - Search by nom or prenom + search(query: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/search`, { + params: { q: query.trim() }, + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiAgent) => this.transformAgent(apiAgent))), + catchError((err) => { + console.error(`Error searching agents with query ${query}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/agents/code/{code} - Get by code + getByCode(code: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/code/${code}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiAgent) => this.transformAgent(apiAgent)), + catchError((err) => { + console.error(`Error fetching agent by code ${code}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // Helper method to update all agents' limitId to a new default limit + // This is used when a limit is set as default + updateAllAgentsLimitId(limitId: string): Observable { + if (USE_SERVER) { + // Get all agents first + return this.list({ + page: 1, + perPage: 10000, + search: '', + sortKey: 'code', + sortDir: 'asc', + } as any).pipe( + switchMap((result) => { + const agents = result.data; + if (agents.length === 0) { + return of(true); + } + // Update each agent's limitId in parallel + const updateObservables = agents.map((agent) => + this.update(agent.id, { limitId }).pipe( + catchError((err) => { + console.error(`Error updating agent ${agent.id} limitId:`, err); + return of(undefined); + }) + ) + ); + // Wait for all updates to complete + return forkJoin(updateObservables).pipe( + map(() => true), + catchError((err) => { + console.error('Error updating all agents limitId:', err); + return of(false); + }) + ); + }), + catchError((err) => { + console.error('Error fetching agents for limitId update:', err); + return of(false); + }) + ); + } + return of(false); + } +} diff --git a/src/app/core/services/auth.spec.ts b/src/app/core/services/auth.spec.ts new file mode 100644 index 0000000..3a04d76 --- /dev/null +++ b/src/app/core/services/auth.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Auth } from './auth'; + +describe('Auth', () => { + let service: Auth; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Auth); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/auth.ts b/src/app/core/services/auth.ts new file mode 100644 index 0000000..ced41db --- /dev/null +++ b/src/app/core/services/auth.ts @@ -0,0 +1,103 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment.development'; +import { User } from '../interfaces/user'; +import { firstValueFrom } from 'rxjs'; + +interface LoginRequest { + identifiant: string; + password: string; +} + +// Backend returns full user; no token specified in current spec. +type LoginResponse = any; + +@Injectable({ + providedIn: 'root', +}) +export class Auth { + private tokenKey = 'pmu_token'; + private userKey = 'pmu_user'; + + constructor(private http: HttpClient) {} + + isAuthenticated(): boolean { + return !!localStorage.getItem(this.tokenKey); + } + + getUser(): User | null { + const raw = localStorage.getItem(this.userKey); + return raw ? (JSON.parse(raw) as User) : null; + } + + /** + * Vérifie si l'utilisateur connecté possède un roleId donné. + */ + hasRoleId(roleId: string): boolean { + const user = this.getUser(); + if (!user?.roleId) return false; + return String(user.roleId) === String(roleId); + } + + /** + * Vérifie si l'utilisateur possède l'un des rôles attendus (par id). + */ + hasAnyRoleId(roleIds: string[]): boolean { + const user = this.getUser(); + if (!user?.roleId) return false; + return roleIds.map(String).includes(String(user.roleId)); + } + + getToken(): string | null { + return localStorage.getItem(this.tokenKey); + } + + setToken(token: string) { + localStorage.setItem(this.tokenKey, token); + } + + logout() { + localStorage.removeItem(this.tokenKey); + localStorage.removeItem(this.userKey); + } + + private setSession(token: string, user: User) { + localStorage.setItem(this.tokenKey, token); + localStorage.setItem(this.userKey, JSON.stringify(user)); + } + + async login(identifiant: string, password: string) { + const url = `${environment.apiBaseUrl}/api/v1/auth/login`; + const body: LoginRequest = { identifiant, password }; + + const res = (await firstValueFrom(this.http.post(url, body))) as any; + + if (!res) { + throw new Error('Réponse de connexion invalide'); + } + + // Map backend user to frontend User model + const user: User = { + id: String(res.id), + nom: res.nom, + prenom: res.prenom, + identifiant: res.identifiant, + matriculeAgent: res.matriculeAgent, + roleId: String(res.roleId), + restrictionConnexion: !!res.restrictionConnexion, + restrictionAutomatique: !!res.restrictionAutomatique, + nombreIpAutorise: res.nombreIpAutorise ?? 0, + nombreIpAutoAutorise: res.nombreIpAutoAutorise ?? 0, + statut: res.statut ?? 'ACTIVE', + derniereConnexion: res.derniereConnexion, + createdAt: res.createdAt, + updatedAt: res.updatedAt, + }; + + // Backend spec does not expose a token yet; we set a dummy non-empty token + // so that authGuard & interceptors keep working. + const token = (res && (res.token || res.accessToken)) || 'session'; + this.setSession(token, user); + return true; + } +} diff --git a/src/app/core/services/course-sample.spec.ts b/src/app/core/services/course-sample.spec.ts new file mode 100644 index 0000000..a7963e8 --- /dev/null +++ b/src/app/core/services/course-sample.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { CourseSample } from './course-sample'; + +describe('CourseSample', () => { + let service: CourseSample; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(CourseSample); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/course-sample.ts b/src/app/core/services/course-sample.ts new file mode 100644 index 0000000..20f487d --- /dev/null +++ b/src/app/core/services/course-sample.ts @@ -0,0 +1,38 @@ +// src/app/features/courses/course.service.ts +import { inject, Injectable } from '@angular/core'; +import { PaginatedHttpService } from '@shared/paging/paginated-http.service'; +import { BackendConfig, ListParams, PagedResult } from '@shared/paging/paging'; +import { Observable } from 'rxjs'; + +export interface Course { + id: string; + numero: number; + nom: string; + type_course: string; + depart_at: string | null; + statut: string; +} + +@Injectable({ providedIn: 'root' }) +export class CourseService { + private http = inject(PaginatedHttpService); + private base = '/api/courses'; + + list(params: ListParams): Observable> { + const cfg: BackendConfig = { + zeroBasedPageIndex: true, + buildSort: (key, dir) => (key && dir ? ['sort', `${key},${dir}`] : null), + mapClientSortKey: (k) => { + const map: Record = { + depart_at: 'departAt', + type_course: 'type', + numero: 'numero', + nom: 'nom', + statut: 'statut', + }; + return k ? map[k] ?? k : undefined; + }, + }; + return this.http.fetch(this.base, params, cfg); + } +} diff --git a/src/app/core/services/course.spec.ts b/src/app/core/services/course.spec.ts new file mode 100644 index 0000000..23e24d2 --- /dev/null +++ b/src/app/core/services/course.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Course } from './course'; + +describe('Course', () => { + let service: Course; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Course); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/course.ts b/src/app/core/services/course.ts new file mode 100644 index 0000000..bb069e6 --- /dev/null +++ b/src/app/core/services/course.ts @@ -0,0 +1,874 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of, forkJoin } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { Course, CourseType, CourseStatut } from '../interfaces/course'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { PaginatedHttpService } from '@shared/paging/paginated-http.service'; +import { ListParams, PagedResult } from '@shared/paging/paging'; +import { environment } from 'src/environments/environment.development'; +import { Reunion } from '../interfaces/reunion'; +import { ReunionService } from './reunion'; +import { NonPartantService } from './non-partant'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/courses'; +// Interface to match the API response structure for Course +interface CourseApiResponse { + id: number; + type: string; + numero: number; + nom: string; + dateDepartCourse: string; + dateDebutParis: string; + dateFinParis: string; + reunionId: number; // API returns reunionId + reunionCourse: number; + particularite?: string; + partants: number; + distance: number; + condition?: string; + estTerminee: boolean; + estAnnulee: boolean; + statut: CourseStatut; + nombreChevauxInscrits: number; + createdBy: string; + validatedBy: string | null; + createdAt: string | null; + updatedAt: string | null; + nonPartants: string[]; + adeadHeat: boolean; +} + +@Injectable({ providedIn: 'root' }) +export class CourseService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor( + private http: HttpClient, + private paginatedHttp: PaginatedHttpService, + private reunionService: ReunionService, // Inject ReunionService + private nonPartantService: NonPartantService // Inject NonPartantService + ) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + list( + params: ListParams, + usePaginationEndpoint: boolean = false + ): Observable> { + if (USE_SERVER) { + // If there's a search query, use the search endpoint + if (params.search && params.search.trim()) { + return this.search(params.search.trim()).pipe( + map((courses) => { + // Apply client-side sorting and pagination + let filtered = [...courses]; + + // Sort + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params; + filtered.sort((a: any, b: any) => { + const getValue = (obj: any, path: string): any => + path.split('.').reduce((o, key) => o?.[key], obj); + + const va = getValue(a, sortKey); + const vb = getValue(b, sortKey); + const sa = va == null ? '' : String(va); + const sb = vb == null ? '' : String(vb); + const cmp = sa.localeCompare(sb, 'fr', { numeric: true }); + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + const total = filtered.length; + const start = (params.page - 1) * params.perPage; + const pageData = filtered.slice(start, start + params.perPage); + + const totalByType = filtered.reduce>((acc, c) => { + const type = String(c.type); + acc[type] = (acc[type] ?? 0) + 1; + return acc; + }, {}); + const totalRunning = filtered.filter( + (c) => c.statut === CourseStatut.RUNNING || c.statut === 'RUNNING' + ).length; + const totalClosed = filtered.filter( + (c) => c.statut === CourseStatut.CLOSED || c.statut === 'CLOSED' + ).length; + + return normalizePage( + { + data: pageData, + meta: { + total, + totalRunning, + totalClosed, + totalByType, + }, + }, + params.page, + params.perPage + ); + }), + catchError((err) => { + console.error('Error searching courses:', err); + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + }) + ); + } + + if (usePaginationEndpoint) { + return this.paginatedHttp + .fetch(this.apiUrl, params, { + zeroBasedPageIndex: false, + }) + .pipe( + switchMap((pagedResult) => { + // Handle empty data case + if (!pagedResult.data || pagedResult.data.length === 0) { + return of( + normalizePage( + { + data: [], + meta: { + total: pagedResult.meta?.total ?? 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + } + + // Extract unique reunionIds + const uniqueReunionIds = [ + ...new Set(pagedResult.data.map((c) => String(c.reunionId))), + ]; + + // If no reunion IDs, we can't build valid Reunion objects – return empty page + if (uniqueReunionIds.length === 0) { + return of( + normalizePage( + { + data: [], + meta: { + total: pagedResult.meta?.total ?? 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + } + + // Fetch all reunions in parallel + const reunionRequests = uniqueReunionIds.map((id) => + this.reunionService + .getById(id) + .pipe(catchError(() => of(undefined))) + ); + + return forkJoin(reunionRequests).pipe( + map((reunions) => { + // Create a map of reunionId -> Reunion + const reunionMap = new Map(); + uniqueReunionIds.forEach((id, index) => { + const reunion = reunions[index]; + if (reunion) { + reunionMap.set(id, reunion); + } + }); + + // Transform API data to Course objects + const transformedData: Course[] = pagedResult.data + .map((apiCourse) => { + const reunion = reunionMap.get(String(apiCourse.reunionId)); + if (!reunion) { + // If we couldn't resolve the reunion, drop this course to keep typing sound + return null; + } + return { + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + } as Course; + }) + .filter((c): c is Course => c !== null); + + // Calculate meta stats + const totalByType = transformedData.reduce>((acc, c) => { + const type = String(c.type); + acc[type] = (acc[type] ?? 0) + 1; + return acc; + }, {}); + const totalRunning = transformedData.filter( + (c) => c.statut === CourseStatut.RUNNING || c.statut === 'RUNNING' + ).length; + const totalClosed = transformedData.filter( + (c) => c.statut === CourseStatut.CLOSED || c.statut === 'CLOSED' + ).length; + + return normalizePage( + { + data: transformedData, + meta: { + total: pagedResult.meta?.total ?? transformedData.length, + totalRunning, + totalClosed, + totalByType, + }, + }, + params.page, + params.perPage + ); + }) + ); + }), + catchError((err) => { + console.error('Error fetching courses:', err); + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + }) + ); + } else { + // Fetch all data and apply client-side pagination + return this.http + .get(this.apiUrl, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiData) => { + // Handle empty data case + if (!apiData || apiData.length === 0) { + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + } + + // Extract unique reunionIds + const uniqueReunionIds = [...new Set(apiData.map((c) => String(c.reunionId)))]; + + // Handle case where there are no unique IDs (shouldn't happen, but be safe) + if (uniqueReunionIds.length === 0) { + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + } + + // Fetch all reunions in parallel + const reunionRequests = uniqueReunionIds.map((id) => + this.reunionService + .getById(id) + .pipe(catchError(() => of(undefined))) + ); + + return forkJoin(reunionRequests).pipe( + map((reunions) => { + // Create a map of reunionId -> Reunion + const reunionMap = new Map(); + uniqueReunionIds.forEach((id, index) => { + const reunion = reunions[index]; + if (reunion) { + reunionMap.set(id, reunion); + } + }); + + // Transform API data to Course objects + const transformedData: Course[] = apiData + .map((apiCourse) => { + const reunion = reunionMap.get(String(apiCourse.reunionId)); + if (!reunion) { + return null; + } + return { + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + } as Course; + }) + .filter((c): c is Course => c !== null); + + // Apply client-side filtering, sorting, and pagination + let filtered = this.applyClientFilters(transformedData, params); + const total = filtered.length; + const start = (params.page - 1) * params.perPage; + const pageData = filtered.slice(start, start + params.perPage); + + const totalByType = filtered.reduce>((acc, c) => { + const type = String(c.type); + acc[type] = (acc[type] ?? 0) + 1; + return acc; + }, {}); + const totalRunning = filtered.filter( + (c) => c.statut === CourseStatut.RUNNING || c.statut === 'RUNNING' + ).length; + const totalClosed = filtered.filter( + (c) => c.statut === CourseStatut.CLOSED || c.statut === 'CLOSED' + ).length; + + return normalizePage( + { + data: pageData, + meta: { + total, + totalRunning, + totalClosed, + totalByType, + }, + }, + params.page, + params.perPage + ); + }) + ); + }), + catchError((err) => { + console.error('Error fetching courses:', err); + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + }) + ); + } + } + + // If USE_SERVER is false, return empty result + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + totalRunning: 0, + totalClosed: 0, + totalByType: {}, + }, + }, + params.page, + params.perPage + ) + ); + } + + private applyClientFilters(data: Course[], params: ListParams): Course[] { + let filtered = [...data]; + + // Search filter + const q = (params.search ?? '').toLowerCase(); + if (q) { + filtered = filtered.filter((c) => { + const reunionName = c.reunion?.nom?.toLowerCase?.() ?? ''; + const hippodromeName = c.reunion?.hippodrome?.nom?.toLowerCase?.() ?? ''; + return ( + c.nom.toLowerCase().includes(q) || + c.type.toLowerCase().includes(q) || + reunionName.includes(q) || + hippodromeName.includes(q) + ); + }); + } + + // Sort + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params; + filtered.sort((a: any, b: any) => { + const getValue = (obj: any, path: string): any => + path.split('.').reduce((o, key) => o?.[key], obj); + + const va = getValue(a, sortKey); + const vb = getValue(b, sortKey); + const sa = va == null ? '' : String(va); + const sb = vb == null ? '' : String(vb); + const cmp = sa.localeCompare(sb, 'fr', { numeric: true }); + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + return filtered; + } + + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiCourse) => { + // Fetch the reunion (non-partants are already included in the API response) + return this.reunionService.getById(String(apiCourse.reunionId)).pipe( + map((reunion) => { + if (!reunion) { + return undefined; + } + return { + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + }; + }) + ); + }), + catchError((err) => { + console.error(`Error fetching course ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + getByReunionId(reunionId: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/reunion/${reunionId}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((apiData) => { + // Fetch the reunion once + return this.reunionService.getById(reunionId).pipe( + map((reunion) => { + if (!reunion) { + return []; + } + // Transform all courses with the same reunion + return apiData.map((apiCourse) => ({ + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + })); + }) + ); + }), + catchError((err) => { + console.error(`Error fetching courses for reunion ${reunionId}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + search(query: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/search`, { + params: { q: query.trim() }, + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((apiData) => { + // Extract unique reunionIds + const uniqueReunionIds = [...new Set(apiData.map((c) => String(c.reunionId)))]; + + // Fetch all reunions in parallel + const reunionRequests = uniqueReunionIds.map((id) => + this.reunionService + .getById(id) + .pipe(catchError(() => of(undefined))) + ); + + return forkJoin(reunionRequests).pipe( + map((reunions) => { + // Create a map of reunionId -> Reunion + const reunionMap = new Map(); + uniqueReunionIds.forEach((id, index) => { + const reunion = reunions[index]; + if (reunion) { + reunionMap.set(id, reunion); + } + }); + + // Transform API data to Course objects + return apiData + .map((apiCourse) => { + const reunion = reunionMap.get(String(apiCourse.reunionId)); + if (!reunion) { + return null; + } + return { + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + } as Course; + }) + .filter((c): c is Course => c !== null); + }) + ); + }), + catchError((err) => { + console.error(`Error searching courses with query ${query}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + create(payload: Omit): Observable { + if (USE_SERVER) { + // Transform payload to API format (send reunionId instead of reunion object) + const apiPayload: any = { + type: payload.type, + numero: payload.numero, + nom: payload.nom, + dateDepartCourse: payload.dateDepartCourse, + dateDebutParis: payload.dateDebutParis, + dateFinParis: payload.dateFinParis, + reunionId: typeof payload.reunion === 'object' ? payload.reunion.id : payload.reunion, + reunionCourse: payload.reunionCourse, + particularite: payload.particularite, + partants: payload.partants, + distance: payload.distance, + condition: payload.condition, + statut: payload.statut, + createdBy: payload.createdBy, + validatedBy: payload.validatedBy, + }; + + return this.http + .post(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiCourse) => { + // Fetch the reunion to build the full Course object + return this.reunionService.getById(String(apiCourse.reunionId)).pipe( + map((reunion) => { + if (!reunion) { + throw new Error('Reunion not found'); + } + const item: Course = { + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + }; + return item; + }) + ); + }), + catchError((err) => { + console.error('Error creating course:', err); + throw err; + }) + ); + } + throw new Error('Server mode is required'); + } + + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + // Transform payload to API format (send reunionId instead of reunion object) + const apiPayload: any = { ...payload }; + if (payload.reunion) { + apiPayload.reunionId = + typeof payload.reunion === 'object' ? payload.reunion.id : payload.reunion; + delete apiPayload.reunion; + } + + return this.http + .put(`${this.apiUrl}/${id}`, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((apiCourse) => { + // Fetch the reunion to build the full Course object + return this.reunionService.getById(String(apiCourse.reunionId)).pipe( + map((reunion) => { + if (!reunion) { + throw new Error('Reunion not found'); + } + return { + id: String(apiCourse.id), + type: apiCourse.type, + numero: apiCourse.numero, + nom: apiCourse.nom, + dateDepartCourse: apiCourse.dateDepartCourse, + dateDebutParis: apiCourse.dateDebutParis, + dateFinParis: apiCourse.dateFinParis, + reunion, + reunionCourse: apiCourse.reunionCourse, + particularite: apiCourse.particularite, + partants: apiCourse.partants, + distance: apiCourse.distance, + condition: apiCourse.condition, + statut: apiCourse.statut as CourseStatut, + nonPartants: apiCourse.nonPartants || [], + estTerminee: apiCourse.estTerminee, + estAnnulee: apiCourse.estAnnulee, + nombreChevauxInscrits: apiCourse.nombreChevauxInscrits, + adeadHeat: apiCourse.adeadHeat, + createdBy: apiCourse.createdBy, + validatedBy: apiCourse.validatedBy, + createdAt: apiCourse.createdAt || new Date().toISOString(), + updatedAt: apiCourse.updatedAt || new Date().toISOString(), + }; + }) + ); + }), + catchError((err) => { + console.error(`Error updating course ${id}:`, err); + return of(undefined); + }) + ); + } + throw new Error('Server mode is required'); + } + + updateStatut(id: string, statut: CourseStatut): Observable { + if (USE_SERVER) { + return this.http + .patch( + `${this.apiUrl}/${id}/statut`, + { statut }, + { headers: this.getNgrokHeaders() } + ) + .pipe( + catchError((err) => { + console.error(`Error updating course statut ${id}:`, err); + return this.update(id, { statut }); + }) + ); + } + return this.update(id, { statut }); + } + + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting course ${id}:`, err); + return of(false); + }) + ); + } + throw new Error('Server mode is required'); + } + + addNonPartant(courseId: string, npList: string[]) { + console.warn('addNonPartant is deprecated. Use setNonPartants instead.'); + return this.setNonPartants(courseId, npList); + } + + setNonPartants(courseId: string, npList: string[]): Observable { + if (USE_SERVER) { + // Use PUT endpoint to replace the entire list + return this.nonPartantService.replaceNonPartants(courseId, npList).pipe( + switchMap((updatedNonPartants) => { + // Fetch the updated course to return it + return this.getById(courseId).pipe( + map((course) => { + if (course) { + return { + ...course, + nonPartants: updatedNonPartants, + }; + } + return undefined; + }) + ); + }), + catchError((err) => { + console.error(`Error setting nonPartants for course ${courseId}:`, err); + return of(undefined); + }) + ); + } + throw new Error('Server mode is required'); + } +} diff --git a/src/app/core/services/hippodrome.spec.ts b/src/app/core/services/hippodrome.spec.ts new file mode 100644 index 0000000..8aff47e --- /dev/null +++ b/src/app/core/services/hippodrome.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Hippodrome } from './hippodrome'; + +describe('Hippodrome', () => { + let service: Hippodrome; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Hippodrome); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/hippodrome.ts b/src/app/core/services/hippodrome.ts new file mode 100644 index 0000000..ffd3308 --- /dev/null +++ b/src/app/core/services/hippodrome.ts @@ -0,0 +1,547 @@ +import { Injectable, signal } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of, forkJoin } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { Hippodrome } from '../interfaces/hippodrome'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { PaginatedHttpService } from '@shared/paging/paginated-http.service'; +import { ListParams, PagedResult } from '@shared/paging/paging'; +import { environment } from 'src/environments/environment.development'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/hippodromes'; + +@Injectable({ providedIn: 'root' }) +export class HippodromeService { + private apiUrl = environment.apiBaseUrl + API_BASE; + private store = signal([]); + + constructor(private http: HttpClient, private paginatedHttp: PaginatedHttpService) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + this.apiUrl.includes('ngrok-free.app') || + this.apiUrl.includes('ngrok.io') || + this.apiUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // LISTE — supporte client & serveur + list( + params: ListParams, + usePaginationEndpoint: boolean = false + ): Observable> { + if (USE_SERVER) { + // If there's a search query, use the search endpoint + if (params.search && params.search.trim()) { + return this.search(params.search.trim()).pipe( + switchMap((hippodromes) => { + // Fetch all reunions and courses to calculate counts + return forkJoin({ + reunions: this.http + .get(`${environment.apiBaseUrl}/api/v1/reunions`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError(() => of([])), + map((data) => ({ data: data || [], meta: { total: (data || []).length } })) + ), + courses: this.http + .get(`${environment.apiBaseUrl}/api/v1/courses`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError(() => of([])), + map((data) => ({ data: data || [], meta: { total: (data || []).length } })) + ), + }).pipe( + map(({ reunions, courses }) => { + // Count reunions per hippodrome + const reunionCountMap = new Map(); + reunions.data.forEach((reunion: any) => { + const hippodromeId = String(reunion.hippodromeId || reunion.hippodrome?.id); + if (hippodromeId && hippodromeId !== 'undefined' && hippodromeId !== 'null') { + reunionCountMap.set(hippodromeId, (reunionCountMap.get(hippodromeId) || 0) + 1); + } + }); + + // Create a map of reunionId -> hippodromeId from reunions + const reunionToHippodromeMap = new Map(); + reunions.data.forEach((reunion: any) => { + const reunionId = String(reunion.id); + const hippodromeId = String(reunion.hippodromeId || reunion.hippodrome?.id); + if ( + reunionId && + reunionId !== 'undefined' && + reunionId !== 'null' && + hippodromeId && + hippodromeId !== 'undefined' && + hippodromeId !== 'null' + ) { + reunionToHippodromeMap.set(reunionId, hippodromeId); + } + }); + + // Count courses per hippodrome using the reunion -> hippodrome mapping + const courseCountMap = new Map(); + courses.data.forEach((course: any) => { + const reunionId = String(course.reunionId || course.reunion?.id); + if (reunionId && reunionId !== 'undefined' && reunionId !== 'null') { + const hippodromeId = reunionToHippodromeMap.get(reunionId); + if (hippodromeId) { + courseCountMap.set(hippodromeId, (courseCountMap.get(hippodromeId) || 0) + 1); + } + } + }); + + // Add counts to hippodromes + const hippodromesWithCounts = hippodromes.map((h) => ({ + ...h, + reunionCount: reunionCountMap.get(String(h.id)) ?? 0, + courseCount: courseCountMap.get(String(h.id)) ?? 0, + })); + + // Apply client-side sorting and pagination + let filtered = this.applyClientFilters(hippodromesWithCounts, { + ...params, + search: '', // Already filtered by search endpoint + }); + const total = filtered.length; + const start = (params.page - 1) * params.perPage; + const pageData = filtered.slice(start, start + params.perPage); + + const uniqueCountries = new Set(filtered.map((h) => h.pays)).size; + const uniqueCities = new Set(filtered.map((h) => h.ville)).size; + const averageByCountry = filtered.length + ? Math.round(filtered.length / uniqueCountries) + : 0; + const totalReunions = filtered.reduce((acc, h) => acc + (h.reunionCount ?? 0), 0); + const totalCourses = filtered.reduce((acc, h) => acc + (h.courseCount ?? 0), 0); + + return normalizePage( + { + data: pageData, + meta: { + total, + uniqueCountries, + uniqueCities, + averageByCountry, + totalReunions, + totalCourses, + }, + }, + params.page, + params.perPage + ); + }) + ); + }), + catchError((err) => { + console.error('Error searching hippodromes:', err); + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + uniqueCountries: 0, + uniqueCities: 0, + averageByCountry: 0, + totalReunions: 0, + totalCourses: 0, + }, + }, + params.page, + params.perPage + ) + ); + }) + ); + } + + if (usePaginationEndpoint) { + return this.paginatedHttp + .fetch(this.apiUrl, params, { + zeroBasedPageIndex: false, + buildSort: (key, dir) => (key && dir ? ['sort', `${key},${dir}`] : null), + mapClientSortKey: (k) => { + const alias: Record = { + name: 'nom', + city: 'ville', + country: 'pays', + }; + return k ? alias[k] ?? k : undefined; + }, + }) + .pipe( + catchError((err) => { + console.error('Error fetching hippodromes:', err); + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + uniqueCountries: 0, + uniqueCities: 0, + averageByCountry: 0, + totalReunions: 0, + totalCourses: 0, + }, + }, + params.page, + params.perPage + ) + ); + }) + ); + } else { + // Fetch all data and apply client-side pagination + return this.http + .get(`${this.apiUrl}/actifs`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((allData) => { + // Fetch all reunions and courses directly from API to calculate counts + // We fetch directly to avoid circular dependency with ReunionService and CourseService + return forkJoin({ + reunions: this.http + .get(`${environment.apiBaseUrl}/api/v1/reunions`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError(() => of([])), + map((data) => ({ data, meta: { total: data.length } })) + ), + courses: this.http + .get(`${environment.apiBaseUrl}/api/v1/courses`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError(() => of([])), + map((data) => ({ data, meta: { total: data.length } })) + ), + }).pipe( + map(({ reunions, courses }) => { + // Count reunions per hippodrome + const reunionCountMap = new Map(); + reunions.data.forEach((reunion: any) => { + const hippodromeId = String(reunion.hippodromeId || reunion.hippodrome?.id); + if (hippodromeId && hippodromeId !== 'undefined' && hippodromeId !== 'null') { + reunionCountMap.set( + hippodromeId, + (reunionCountMap.get(hippodromeId) || 0) + 1 + ); + } + }); + + // Create a map of reunionId -> hippodromeId from reunions + const reunionToHippodromeMap = new Map(); + reunions.data.forEach((reunion: any) => { + const reunionId = String(reunion.id); + const hippodromeId = String(reunion.hippodromeId || reunion.hippodrome?.id); + if ( + reunionId && + reunionId !== 'undefined' && + reunionId !== 'null' && + hippodromeId && + hippodromeId !== 'undefined' && + hippodromeId !== 'null' + ) { + reunionToHippodromeMap.set(reunionId, hippodromeId); + } + }); + + // Count courses per hippodrome using the reunion -> hippodrome mapping + const courseCountMap = new Map(); + courses.data.forEach((course: any) => { + const reunionId = String(course.reunionId || course.reunion?.id); + if (reunionId && reunionId !== 'undefined' && reunionId !== 'null') { + const hippodromeId = reunionToHippodromeMap.get(reunionId); + if (hippodromeId) { + courseCountMap.set( + hippodromeId, + (courseCountMap.get(hippodromeId) || 0) + 1 + ); + } + } + }); + + // Add counts to hippodromes + const hippodromesWithCounts = allData.map((h) => ({ + ...h, + reunionCount: reunionCountMap.get(String(h.id)) ?? 0, + courseCount: courseCountMap.get(String(h.id)) ?? 0, + })); + + // Apply client-side filtering, sorting, and pagination + let filtered = this.applyClientFilters(hippodromesWithCounts, params); + const total = filtered.length; + const start = (params.page - 1) * params.perPage; + const pageData = filtered.slice(start, start + params.perPage); + + const uniqueCountries = new Set(filtered.map((h) => h.pays)).size; + const uniqueCities = new Set(filtered.map((h) => h.ville)).size; + const averageByCountry = filtered.length + ? Math.round(filtered.length / uniqueCountries) + : 0; + const totalReunions = filtered.reduce((acc, h) => acc + (h.reunionCount ?? 0), 0); + const totalCourses = filtered.reduce((acc, h) => acc + (h.courseCount ?? 0), 0); + + return normalizePage( + { + data: pageData, + meta: { + total, + uniqueCountries, + uniqueCities, + averageByCountry, + totalReunions, + totalCourses, + }, + }, + params.page, + params.perPage + ); + }) + ); + }), + catchError((err) => { + console.error('Error fetching hippodromes:', err); + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + uniqueCountries: 0, + uniqueCities: 0, + averageByCountry: 0, + totalReunions: 0, + totalCourses: 0, + }, + }, + params.page, + params.perPage + ) + ); + }) + ); + } + } + + // Mock mode disabled - return empty result + return of( + normalizePage( + { + data: [], + meta: { + total: 0, + uniqueCountries: 0, + uniqueCities: 0, + averageByCountry: 0, + totalReunions: 0, + totalCourses: 0, + }, + }, + params.page, + params.perPage + ) + ); + } + + private applyClientFilters(data: Hippodrome[], params: ListParams): Hippodrome[] { + let filtered = [...data]; + + // Search filter + const q = (params.search ?? '').toLowerCase(); + if (q) { + filtered = filtered.filter( + (h) => + h.nom.toLowerCase().includes(q) || + h.ville.toLowerCase().includes(q) || + h.pays.toLowerCase().includes(q) + ); + } + + // Sort + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params; + filtered.sort((a: any, b: any) => { + const va = a[sortKey!], + vb = b[sortKey!]; + let cmp: number; + + if (typeof va === 'number' && typeof vb === 'number') { + cmp = va - vb; + } else { + const sa = va == null ? '' : String(va); + const sb = vb == null ? '' : String(vb); + cmp = sa.localeCompare(sb, 'fr', { numeric: true, sensitivity: 'base' }); + } + + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + return filtered; + } + + // READ + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + catchError((err) => { + console.error(`Error fetching hippodrome ${id}:`, err); + return of(this.store().find((h) => h.id === id)); + }) + ); + } + const found = this.store().find((h) => h.id === id); + return of(found); + } + + // CREATE + create(payload: Omit): Observable { + if (USE_SERVER) { + return this.http + .post(this.apiUrl, payload, { headers: this.getNgrokHeaders() }) + .pipe( + catchError((err) => { + console.error('Error creating hippodrome:', err); + const item: Hippodrome = { id: crypto.randomUUID(), ...payload }; + this.store.set([item, ...this.store()]); + return of(item); + }) + ); + } + const item: Hippodrome = { id: crypto.randomUUID(), ...payload }; + this.store.set([item, ...this.store()]); + return of(item); + } + + // UPDATE + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + return this.http + .put(`${this.apiUrl}/${id}`, payload, { headers: this.getNgrokHeaders() }) + .pipe( + catchError((err) => { + console.error(`Error updating hippodrome ${id}:`, err); + let updated: Hippodrome | undefined; + this.store.set( + this.store().map((h) => { + if (h.id === id) { + updated = { ...h, ...payload }; + return updated; + } + return h; + }) + ); + return of(updated); + }) + ); + } + let updated: Hippodrome | undefined; + this.store.set( + this.store().map((h) => { + if (h.id === id) { + updated = { ...h, ...payload }; + return updated; + } + return h; + }) + ); + return of(updated); + } + + // DELETE + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting hippodrome ${id}:`, err); + const before = this.store().length; + this.store.set(this.store().filter((h) => h.id !== id)); + return of(this.store().length < before); + }) + ); + } + const before = this.store().length; + this.store.set(this.store().filter((h) => h.id !== id)); + return of(this.store().length < before); + } + + // GET by ville + getByVille(ville: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/ville/${encodeURIComponent(ville)}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError((err) => { + console.error(`Error fetching hippodromes by ville ${ville}:`, err); + return of(this.store().filter((h) => h.ville === ville)); + }) + ); + } + return of(this.store().filter((h) => h.ville === ville)); + } + + // SEARCH by query (q parameter) + search(query: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/search`, { + params: { nom: query.trim() }, + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError((err) => { + console.error(`Error searching hippodromes with query ${query}:`, err); + const q = query.toLowerCase(); + return of( + this.store().filter( + (h) => + h.nom.toLowerCase().includes(q) || + h.ville.toLowerCase().includes(q) || + h.pays.toLowerCase().includes(q) + ) + ); + }) + ); + } + const q = query.toLowerCase(); + return of( + this.store().filter( + (h) => + h.nom.toLowerCase().includes(q) || + h.ville.toLowerCase().includes(q) || + h.pays.toLowerCase().includes(q) + ) + ); + } + + // GET actifs + getActifs(): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/actifs`, { headers: this.getNgrokHeaders() }) + .pipe( + catchError((err) => { + console.error('Error fetching active hippodromes:', err); + return of(this.store().filter((h) => h.actif)); + }) + ); + } + return of(this.store().filter((h) => h.actif)); + } +} diff --git a/src/app/core/services/non-partant.ts b/src/app/core/services/non-partant.ts new file mode 100644 index 0000000..d2773c1 --- /dev/null +++ b/src/app/core/services/non-partant.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { environment } from 'src/environments/environment.development'; + +const USE_SERVER = true; + +@Injectable({ providedIn: 'root' }) +export class NonPartantService { + constructor(private http: HttpClient) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // PUT /api/v1/courses/{courseId}/non-partants - Replace the list of non-partants for a course + replaceNonPartants(courseId: string, nonPartants: string[]): Observable { + if (USE_SERVER) { + const courseApiUrl = environment.apiBaseUrl + '/api/v1/courses'; + return this.http + .put(`${courseApiUrl}/${courseId}/non-partants`, nonPartants, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((np) => String(np))), + catchError((err) => { + console.error(`Error replacing non-partants for course ${courseId}:`, err); + return of([]); + }) + ); + } + return of([]); + } +} diff --git a/src/app/core/services/report.ts b/src/app/core/services/report.ts new file mode 100644 index 0000000..5f4fbd5 --- /dev/null +++ b/src/app/core/services/report.ts @@ -0,0 +1,76 @@ +import { Injectable, signal } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { CourseReportDetail, CourseReportDetailRow, CourseReportSummary } from '../interfaces/report'; +import { REPORT_SUMMARIES_MOCK, REPORT_DETAILS_MOCK } from '../mocks/report.mocks'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { ListParams, PagedResult, SortDir } from '@shared/paging/paging'; + +@Injectable({ providedIn: 'root' }) +export class ReportService { + private summaries = signal([...REPORT_SUMMARIES_MOCK]); + + list(params: ListParams): Observable> { + let data = [...this.summaries()]; + const q = (params.search ?? '').toLowerCase(); + if (q) { + data = data.filter((r) => + [ + r.course.nom, + r.course.type, + r.course.reunion?.hippodrome?.nom, + String(r.course.numero), + ] + .filter(Boolean) + .map((s) => String(s).toLowerCase()) + .some((s) => s.includes(q)) + ); + } + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params as { sortKey: string; sortDir: SortDir }; + const get = (o: any, k: string) => k.split('.').reduce((a, b) => a?.[b], o); + data = [...data].sort((a, b) => String(get(a, sortKey) ?? '').localeCompare(String(get(b, sortKey) ?? ''), 'fr', { numeric: true }) * (sortDir === 'asc' ? 1 : -1)); + } + const start = (params.page - 1) * params.perPage; + const pageData = data.slice(start, start + params.perPage); + return of(normalizePage({ data: pageData, meta: { total: data.length } }, params.page, params.perPage)); + } + + getDetail(courseId: string): Observable { + const summary = this.summaries().find((s) => s.id === courseId); + if (!summary) return of(undefined); + const rows = REPORT_DETAILS_MOCK.get(courseId) ?? []; + return of({ summary, rows }); + } + + // === Actions === + validate(courseId: string): Observable { + let updated: CourseReportSummary | undefined; + this.summaries.set( + this.summaries().map((s) => (s.id === courseId ? ((updated = { ...s, statut: 'Validé', confirmed: false }), updated) : s)) + ); + return of(updated); + } + + confirm(courseId: string): Observable { + let updated: CourseReportSummary | undefined; + this.summaries.set( + this.summaries().map((s) => (s.id === courseId ? ((updated = { ...s, statut: 'Validé', confirmed: true }), updated) : s)) + ); + return of(updated); + } + + resetStatus(courseId: string): Observable { + let updated: CourseReportSummary | undefined; + this.summaries.set( + this.summaries().map((s) => (s.id === courseId ? ((updated = { ...s, statut: 'Non Validé', confirmed: false }), updated) : s)) + ); + return of(updated); + } + + modifyRows(courseId: string, rows: CourseReportDetailRow[]): Observable { + REPORT_DETAILS_MOCK.set(courseId, rows); + return of(true); + } +} + + diff --git a/src/app/core/services/resultat.ts b/src/app/core/services/resultat.ts new file mode 100644 index 0000000..848c2ef --- /dev/null +++ b/src/app/core/services/resultat.ts @@ -0,0 +1,282 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of, forkJoin } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { Resultat, ResultatApiResponse, CreateResultatPayload } from '../interfaces/resultat'; +import { Course } from '../interfaces/course'; +import { CourseService } from './course'; +import { environment } from 'src/environments/environment.development'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/resultat'; + +@Injectable({ providedIn: 'root' }) +export class ResultatService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor(private http: HttpClient, private courseService: CourseService) {} + + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // GET /api/v1/resultat/{id} + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiResultat) => { + // Fetch the full course object if course is just an ID + const courseId = + typeof apiResultat.course === 'object' && 'id' in apiResultat.course + ? String(apiResultat.course.id) + : String(apiResultat.course); + + return this.courseService.getById(courseId).pipe( + map((course) => { + if (!course) { + return undefined; + } + return this.transformApiResponse(apiResultat, course); + }) + ); + }), + catchError((err) => { + console.error(`Error fetching resultat ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // GET /api/v1/resultat + list(): Observable { + if (USE_SERVER) { + return this.http + .get(this.apiUrl, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiResultats) => { + // Fetch all unique course IDs + const courseIds = [ + ...new Set( + apiResultats.map((r) => + typeof r.course === 'object' && 'id' in r.course + ? String(r.course.id) + : String(r.course) + ) + ), + ]; + // Fetch all courses in parallel + const courseRequests = courseIds.map((id) => + this.courseService + .getById(id) + .pipe(catchError(() => of(undefined))) + ); + + return forkJoin(courseRequests).pipe( + map((courses) => { + const courseMap = new Map(); + courseIds.forEach((id, index) => { + const course = courses[index]; + if (course) { + courseMap.set(id, course); + } + }); + + return apiResultats + .map((apiResultat) => { + const courseId = + typeof apiResultat.course === 'object' && 'id' in apiResultat.course + ? String(apiResultat.course.id) + : String(apiResultat.course); + const course = courseMap.get(courseId); + if (!course) { + return null; + } + return this.transformApiResponse(apiResultat, course); + }) + .filter((r): r is Resultat => r !== null); + }), + catchError((err) => { + console.error('Error fetching resultats:', err); + return of([]); + }) + ); + }), + catchError((err) => { + console.error('Error fetching resultats:', err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/resultat/course/{courseId} + getByCourseId(courseId: string): Observable { + if (!USE_SERVER) { + return of(undefined); + } + + return this.http + .get(`${this.apiUrl}/course/${courseId}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((raw) => { + // Some courses don't have a resultat yet. + // In that case the API returns 200 with a body like: + // { "message": "Aucun résultat disponible pour cette course" } + // We interpret this as "no resultat" and return undefined. + if ( + raw && + typeof raw === 'object' && + 'message' in raw && + !('id' in raw) && + !('ordreArrivee' in raw) + ) { + return of(undefined); + } + + const apiResultat = raw as ResultatApiResponse; + + return this.courseService.getById(courseId).pipe( + map((course) => { + if (!course) { + return undefined; + } + return this.transformApiResponse(apiResultat, course); + }) + ); + }), + catchError((err) => { + // If the backend ever responds with 404 here, also treat as "no resultat". + if (err?.status === 404) { + return of(undefined); + } + + console.error(`Error fetching resultat for course ${courseId}:`, err); + return of(undefined); + }) + ); + } + + // POST /api/v1/resultat + create(payload: CreateResultatPayload): Observable { + if (USE_SERVER) { + return this.http + .post(this.apiUrl, payload, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiResultat) => { + const courseId = String(payload.course.id); + return this.courseService.getById(courseId).pipe( + map((course) => { + if (!course) { + throw new Error('Course not found'); + } + return this.transformApiResponse(apiResultat, course); + }) + ); + }), + catchError((err) => { + console.error('Error creating resultat:', err); + throw err; + }) + ); + } + throw new Error('Server mode is required'); + } + + // PUT /api/v1/resultat/{id} + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + return this.http + .put(`${this.apiUrl}/${id}`, payload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((apiResultat) => { + const courseId = + typeof apiResultat.course === 'object' && 'id' in apiResultat.course + ? String(apiResultat.course.id) + : String(apiResultat.course); + + return this.courseService.getById(courseId).pipe( + map((course) => { + if (!course) { + return undefined; + } + return this.transformApiResponse(apiResultat, course); + }) + ); + }), + catchError((err) => { + console.error(`Error updating resultat ${id}:`, err); + return of(undefined); + }) + ); + } + throw new Error('Server mode is required'); + } + + // DELETE /api/v1/resultat/{id} + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting resultat ${id}:`, err); + return of(false); + }) + ); + } + throw new Error('Server mode is required'); + } + + // DELETE /api/v1/resultat/course/{courseId} + deleteByCourseId(courseId: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/course/${courseId}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting resultat for course ${courseId}:`, err); + return of(false); + }) + ); + } + throw new Error('Server mode is required'); + } + + private transformApiResponse(apiResultat: ResultatApiResponse, course: Course): Resultat { + return { + id: String(apiResultat.id), + course, + // Normalize ordreArrivee to an array of cheval numbers + ordreArrivee: (apiResultat.ordreArrivee || []) + .map((v) => (typeof v === 'string' ? Number(v) : v)) + .filter((v): v is number => typeof v === 'number' && !Number.isNaN(v)), + // Normalize dead-heat horses to numbers as well + chevauxDeadHeat: (apiResultat.chevauxDeadHeat || []) + .map((v) => (typeof v === 'string' ? Number(v) : v)) + .filter((v): v is number => typeof v === 'number' && !Number.isNaN(v)), + totalMises: apiResultat.totalMises, + masseAPartager: apiResultat.masseAPartager, + prelevementsLegaux: apiResultat.prelevementsLegaux, + montantRembourse: apiResultat.montantRembourse, + montantCagnotte: apiResultat.montantCagnotte, + adeadHeat: apiResultat.adeadHeat, + createdAt: apiResultat.createdAt, + updatedAt: apiResultat.updatedAt, + }; + } +} diff --git a/src/app/core/services/reunion.spec.ts b/src/app/core/services/reunion.spec.ts new file mode 100644 index 0000000..a7aa150 --- /dev/null +++ b/src/app/core/services/reunion.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Reunion } from './reunion'; + +describe('Reunion', () => { + let service: Reunion; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Reunion); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/reunion.ts b/src/app/core/services/reunion.ts new file mode 100644 index 0000000..17e551e --- /dev/null +++ b/src/app/core/services/reunion.ts @@ -0,0 +1,537 @@ +import { Injectable, signal } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of, forkJoin } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { Reunion } from '../interfaces/reunion'; +import { Hippodrome } from '../interfaces/hippodrome'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { PaginatedHttpService } from '@shared/paging/paginated-http.service'; +import { ListParams, PagedResult } from '@shared/paging/paging'; +import { environment } from 'src/environments/environment.development'; +import { HippodromeService } from './hippodrome'; + +// API response interface (has hippodromeId instead of hippodrome) +interface ReunionApiResponse { + id: string; + code: string; + nom: string; + date: string; + numero: number; + statut: string; + hippodromeId: string; + totalCourses?: number; + createdAt: string; + updatedAt: string; +} + +const USE_SERVER = true; +const API_BASE = '/api/v1/reunions'; + +@Injectable({ providedIn: 'root' }) +export class ReunionService { + private apiUrl = environment.apiBaseUrl + API_BASE; + private store = signal([]); + + constructor( + private http: HttpClient, + private paginatedHttp: PaginatedHttpService, + private hippodromeService: HippodromeService + ) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + list( + params: ListParams, + usePaginationEndpoint: boolean = false + ): Observable> { + if (USE_SERVER) { + if (usePaginationEndpoint) { + return this.paginatedHttp + .fetch(this.apiUrl, params, { + zeroBasedPageIndex: false, + buildSort: (key, dir) => (key && dir ? ['sort', `${key},${dir}`] : null), + }) + .pipe( + switchMap((pagedResult) => { + // Handle empty data case + if (!pagedResult.data || pagedResult.data.length === 0) { + return of({ + ...pagedResult, + data: [], + meta: { + ...pagedResult.meta, + uniqueHippodromes: 0, + }, + }); + } + + // Extract unique hippodrome IDs from the paginated data + const uniqueHippodromeIds = [ + ...new Set(pagedResult.data.map((r) => String(r.hippodromeId))), + ]; + + // Handle case where there are no unique IDs + if (uniqueHippodromeIds.length === 0) { + return of({ + ...pagedResult, + data: [], + meta: { + ...pagedResult.meta, + uniqueHippodromes: 0, + }, + }); + } + + // Fetch all unique hippodromes in parallel + const hippodromeRequests = uniqueHippodromeIds.map((id) => + this.hippodromeService + .getById(id) + .pipe(catchError(() => of(undefined))) + ); + + // Fetch courses to calculate counts per reunion + const coursesRequest = this.http + .get(`${environment.apiBaseUrl}/api/v1/courses`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError(() => of([])), + map((data) => data || []) + ); + + return forkJoin({ + hippodromes: forkJoin(hippodromeRequests), + courses: coursesRequest, + }).pipe( + map(({ hippodromes, courses }) => { + // Create a map of hippodrome ID to hippodrome object + const hippodromeMap = new Map(); + uniqueHippodromeIds.forEach((id, index) => { + const hippodrome = hippodromes[index]; + if (hippodrome) { + hippodromeMap.set(id, hippodrome); + } + }); + + // Count courses per reunion + const courseCountMap = new Map(); + courses.forEach((course: any) => { + const reunionId = String(course.reunionId || course.reunion?.id); + if (reunionId && reunionId !== 'undefined' && reunionId !== 'null') { + courseCountMap.set( + reunionId, + (courseCountMap.get(reunionId) || 0) + 1 + ); + } + }); + + // Transform API responses to Reunion objects + const transformedData: Reunion[] = pagedResult.data + .map((apiReunion) => { + const hippodrome = hippodromeMap.get(String(apiReunion.hippodromeId)); + if (!hippodrome) { + return null; + } + const reunionId = String(apiReunion.id); + const courseCount = courseCountMap.get(reunionId) ?? apiReunion.totalCourses ?? 0; + return { + id: reunionId, + code: apiReunion.code, + nom: apiReunion.nom, + date: apiReunion.date, + numero: apiReunion.numero, + statut: apiReunion.statut as any, + hippodrome, + totalCourses: courseCount, + createdAt: apiReunion.createdAt, + updatedAt: apiReunion.updatedAt, + } as Reunion; + }) + .filter((r): r is Reunion => r !== null && r !== undefined); + + // Calculate unique hippodromes count + const uniqueHippodromes = new Set(transformedData.map((r) => r.hippodrome.id)) + .size; + + return { + ...pagedResult, + data: transformedData, + meta: { + ...pagedResult.meta, + uniqueHippodromes, + }, + }; + }) + ); + }), + catchError((err) => { + console.error('Error fetching reunions:', err); + return this.getMockList(params); + }) + ); + } else { + // Fetch all data and apply client-side pagination + return this.http + .get(this.apiUrl, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiData) => { + // Handle empty data case + if (!apiData || apiData.length === 0) { + return of( + normalizePage( + { + data: [], + meta: { total: 0, uniqueHippodromes: 0, upcomingReunions: 0, pastReunions: 0 }, + }, + params.page, + params.perPage + ) + ); + } + + // Extract unique hippodrome IDs + const uniqueHippodromeIds = [...new Set(apiData.map((r) => String(r.hippodromeId)))]; + + // Handle case where there are no unique IDs (shouldn't happen, but be safe) + if (uniqueHippodromeIds.length === 0) { + return of( + normalizePage( + { + data: [], + meta: { total: 0, uniqueHippodromes: 0, upcomingReunions: 0, pastReunions: 0 }, + }, + params.page, + params.perPage + ) + ); + } + + // Fetch all unique hippodromes and all courses in parallel + const hippodromeRequests = uniqueHippodromeIds.map((id) => + this.hippodromeService + .getById(id) + .pipe(catchError(() => of(undefined))) + ); + + // Fetch courses to calculate counts per reunion + const coursesRequest = this.http + .get(`${environment.apiBaseUrl}/api/v1/courses`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError(() => of([])), + map((data) => data || []) + ); + + return forkJoin({ + hippodromes: forkJoin(hippodromeRequests), + courses: coursesRequest, + }).pipe( + map(({ hippodromes, courses }) => { + // Create a map of hippodrome ID to hippodrome object + const hippodromeMap = new Map(); + uniqueHippodromeIds.forEach((id, index) => { + const hippodrome = hippodromes[index]; + if (hippodrome) { + hippodromeMap.set(id, hippodrome); + } + }); + + // Count courses per reunion + const courseCountMap = new Map(); + courses.forEach((course: any) => { + const reunionId = String(course.reunionId || course.reunion?.id); + if (reunionId && reunionId !== 'undefined' && reunionId !== 'null') { + courseCountMap.set( + reunionId, + (courseCountMap.get(reunionId) || 0) + 1 + ); + } + }); + + // Transform API responses to Reunion objects + const transformedData: Reunion[] = apiData + .map((apiReunion) => { + const hippodrome = hippodromeMap.get(String(apiReunion.hippodromeId)); + if (!hippodrome) { + // Skip if hippodrome not found + return null; + } + const reunionId = String(apiReunion.id); + const courseCount = courseCountMap.get(reunionId) ?? apiReunion.totalCourses ?? 0; + return { + id: reunionId, + code: apiReunion.code, + nom: apiReunion.nom, + date: apiReunion.date, + numero: apiReunion.numero, + statut: apiReunion.statut as any, + hippodrome, + totalCourses: courseCount, + createdAt: apiReunion.createdAt, + updatedAt: apiReunion.updatedAt, + } as Reunion; + }) + .filter((r): r is Reunion => r !== null && r !== undefined); + + // Apply client-side filtering, sorting, and pagination + let filtered = this.applyClientFilters(transformedData, params); + const total = filtered.length; + const start = (params.page - 1) * params.perPage; + const pageData = filtered.slice(start, start + params.perPage); + + const upcomingReunions = filtered.filter( + (r) => new Date(r.date) >= new Date() + ).length; + const pastReunions = filtered.filter((r) => new Date(r.date) < new Date()).length; + const uniqueHippodromes = new Set(filtered.map((r) => r.hippodrome.id)).size; + + return normalizePage( + { + data: pageData, + meta: { total, uniqueHippodromes, upcomingReunions, pastReunions }, + }, + params.page, + params.perPage + ); + }) + ); + }), + catchError((err) => { + console.error('Error fetching reunions:', err); + return this.getMockList(params); + }) + ); + } + } + + return this.getMockList(params); + } + + private applyClientFilters(data: Reunion[], params: ListParams): Reunion[] { + let filtered = [...data]; + + // Search filter + const q = (params.search ?? '').toLowerCase(); + if (q) { + filtered = filtered.filter( + (r) => + r.nom.toLowerCase().includes(q) || + r.hippodrome.nom.toLowerCase().includes(q) || + r.hippodrome.ville.toLowerCase().includes(q) + ); + } + + // Sort + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params; + filtered.sort((a: any, b: any) => { + const va = a[sortKey!], + vb = b[sortKey!]; + const sa = va == null ? '' : String(va); + const sb = vb == null ? '' : String(vb); + const cmp = sa.localeCompare(sb, 'fr', { numeric: true, sensitivity: 'base' }); + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + return filtered; + } + + private getMockList(params: ListParams): Observable> { + const q = (params.search ?? '').toLowerCase(); + let data = this.store(); + + if (q) { + data = data.filter( + (r) => + r.nom.toLowerCase().includes(q) || + r.hippodrome.nom.toLowerCase().includes(q) || + r.hippodrome.ville.toLowerCase().includes(q) + ); + } + + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params; + data = [...data].sort((a: any, b: any) => { + const va = a[sortKey!], + vb = b[sortKey!]; + const sa = va == null ? '' : String(va); + const sb = vb == null ? '' : String(vb); + const cmp = sa.localeCompare(sb, 'fr', { numeric: true, sensitivity: 'base' }); + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + const start = (params.page - 1) * params.perPage; + const pageData = data.slice(start, start + params.perPage); + + const upcomingReunions = data.filter((r) => new Date(r.date) >= new Date()).length; + const pastReunions = data.filter((r) => new Date(r.date) < new Date()).length; + const uniqueHippodromes = new Set(data.map((r) => r.hippodrome.nom)).size; + + return of( + normalizePage( + { + data: pageData, + meta: { total: data.length, uniqueHippodromes, upcomingReunions, pastReunions }, + }, + params.page, + params.perPage + ) + ); + } + + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + switchMap((apiReunion) => { + // Fetch the hippodrome data + return this.hippodromeService.getById(String(apiReunion.hippodromeId)).pipe( + map((hippodrome) => { + if (!hippodrome) { + return undefined; + } + return { + id: String(apiReunion.id), + code: apiReunion.code, + nom: apiReunion.nom, + date: apiReunion.date, + numero: apiReunion.numero, + statut: apiReunion.statut as any, + hippodrome, + totalCourses: apiReunion.totalCourses, + createdAt: apiReunion.createdAt, + updatedAt: apiReunion.updatedAt, + } as Reunion; + }) + ); + }), + catchError((err) => { + console.error(`Error fetching reunion ${id}:`, err); + return of(this.store().find((r) => r.id === id)); + }) + ); + } + const found = this.store().find((r) => r.id === id); + return of(found); + } + + getByCode(code: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/code/${encodeURIComponent(code)}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + switchMap((apiReunion) => { + // Fetch the hippodrome data + return this.hippodromeService.getById(String(apiReunion.hippodromeId)).pipe( + map((hippodrome) => { + if (!hippodrome) { + return undefined; + } + return { + id: String(apiReunion.id), + code: apiReunion.code, + nom: apiReunion.nom, + date: apiReunion.date, + numero: apiReunion.numero, + statut: apiReunion.statut as any, + hippodrome, + totalCourses: apiReunion.totalCourses, + createdAt: apiReunion.createdAt, + updatedAt: apiReunion.updatedAt, + } as Reunion; + }) + ); + }), + catchError((err) => { + console.error(`Error fetching reunion by code ${code}:`, err); + return of(this.store().find((r) => r.code === code)); + }) + ); + } + return of(this.store().find((r) => r.code === code)); + } + + create(payload: Omit): Observable { + if (USE_SERVER) { + return this.http + .post(this.apiUrl, payload, { headers: this.getNgrokHeaders() }) + .pipe( + catchError((err) => { + console.error('Error creating reunion:', err); + const item: Reunion = { id: crypto.randomUUID(), ...payload }; + this.store.set([item, ...this.store()]); + return of(item); + }) + ); + } + const item: Reunion = { id: crypto.randomUUID(), ...payload }; + this.store.set([item, ...this.store()]); + return of(item); + } + + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + return this.http + .put(`${this.apiUrl}/${id}`, payload, { headers: this.getNgrokHeaders() }) + .pipe( + catchError((err) => { + console.error(`Error updating reunion ${id}:`, err); + let updated: Reunion | undefined; + this.store.set( + this.store().map((r) => { + if (r.id === id) { + updated = { ...r, ...payload }; + return updated; + } + return r; + }) + ); + return of(updated); + }) + ); + } + let updated: Reunion | undefined; + this.store.set( + this.store().map((r) => { + if (r.id === id) { + updated = { ...r, ...payload }; + return updated; + } + return r; + }) + ); + return of(updated); + } + + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting reunion ${id}:`, err); + const before = this.store().length; + this.store.set(this.store().filter((r) => r.id !== id)); + return of(this.store().length < before); + }) + ); + } + const before = this.store().length; + this.store.set(this.store().filter((r) => r.id !== id)); + return of(this.store().length < before); + } +} diff --git a/src/app/core/services/role.ts b/src/app/core/services/role.ts new file mode 100644 index 0000000..c25e41f --- /dev/null +++ b/src/app/core/services/role.ts @@ -0,0 +1,283 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { Permission, Role } from '../interfaces/role'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { ListParams, PagedResult } from '@shared/paging/paging'; +import { environment } from 'src/environments/environment.development'; + +const USE_SERVER = true; +const ROLES_API_BASE = '/api/v1/roles'; +const PERMISSIONS_API_BASE = '/api/v1/permissions'; + +// API Response interfaces +interface PermissionApiResponse { + id: number; + name: string; + description?: string; +} + +interface RoleApiResponse { + id: number; + name: string; + description?: string; + permissions?: PermissionApiResponse[]; + createdAt?: string; + updatedAt?: string; +} + +@Injectable({ providedIn: 'root' }) +export class RoleService { + private rolesUrl = environment.apiBaseUrl + ROLES_API_BASE; + private permissionsUrl = environment.apiBaseUrl + PERMISSIONS_API_BASE; + + constructor(private http: HttpClient) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // Transform API response to Permission + private transformPermission(api: PermissionApiResponse): Permission { + return { + id: String(api.id), + name: api.name, + description: api.description, + }; + } + + // Transform API response to Role + private transformRole(api: RoleApiResponse): Role { + return { + id: String(api.id), + name: api.name, + description: api.description, + permissions: (api.permissions || []).map((p) => this.transformPermission(p)), + createdAt: api.createdAt, + updatedAt: api.updatedAt, + }; + } + + // Transform Role to API payload + private transformRoleToApi(role: Partial): any { + return { + id: role.id ? Number(role.id) : undefined, + name: role.name ?? '', + description: role.description, + permissions: (role.permissions || []).map((p) => ({ + id: p.id ? Number(p.id) : undefined, + name: p.name, + description: p.description, + })), + }; + } + + // Transform Permission to API payload + private transformPermissionToApi(perm: Partial): any { + return { + id: perm.id ? Number(perm.id) : undefined, + name: perm.name ?? '', + description: perm.description, + }; + } + + // Helpers + private buildParams(params: ListParams): HttpParams { + let httpParams = new HttpParams() + .set('page', String(params.page - 1)) + .set('size', String(params.perPage)); + if (params.search) { + httpParams = httpParams.set('search', params.search); + } + if (params.sortKey && params.sortDir) { + httpParams = httpParams.set('sort', `${params.sortKey},${params.sortDir}`); + } + return httpParams; + } + + /** + * LIST roles – supports both backend pagination and fallback to simple GET all + */ + list(params: ListParams): Observable> { + if (USE_SERVER) { + return this.http + .get(this.rolesUrl, { + headers: this.getNgrokHeaders(), + params: this.buildParams(params), + }) + .pipe( + map((data) => { + const roles = (data || []).map((r) => this.transformRole(r)); + return normalizePage( + { data: roles, meta: { total: roles.length } }, + params.page, + params.perPage + ); + }), + catchError((err) => { + console.error('Error fetching roles:', err); + return of( + normalizePage({ data: [], meta: { total: 0 } }, params.page, params.perPage) + ); + }) + ); + } + + // Fallback (should not be used anymore) + return of( + normalizePage( + { + data: [], + meta: { total: 0 }, + }, + params.page, + params.perPage + ) + ); + } + + /** + * LIST all permissions + */ + allPermissions(): Observable { + if (USE_SERVER) { + return this.http + .get(this.permissionsUrl, { headers: this.getNgrokHeaders() }) + .pipe( + map((res) => (res || []).map((p) => this.transformPermission(p))), + catchError((err) => { + console.error('Error fetching permissions:', err); + return of([]); + }) + ); + } + return of([]); + } + + /** + * CREATE role + */ + create(payload: Omit): Observable { + const apiPayload = this.transformRoleToApi(payload); + return this.http + .post(this.rolesUrl, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + map((r) => this.transformRole(r)), + catchError((err) => { + console.error('Error creating role:', err); + throw err; + }) + ); + } + + /** + * UPDATE role + */ + update(id: string, payload: Partial): Observable { + const apiPayload = this.transformRoleToApi(payload); + return this.http + .put(`${this.rolesUrl}/${id}`, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((r) => this.transformRole(r)), + catchError((err) => { + console.error(`Error updating role ${id}:`, err); + return of(undefined); + }) + ); + } + + /** + * DELETE role + */ + delete(id: string): Observable<{ success: boolean; error?: string }> { + return this.http + .delete(`${this.rolesUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => ({ success: true })), + catchError((err) => { + console.error(`Error deleting role ${id}:`, err); + // Check if error is due to role being used by users + const errorMessage = + err?.error?.message || + err?.message || + (err?.status === 409 || err?.status === 400 + ? 'Ce rôle est utilisé par des utilisateurs et ne peut pas être supprimé' + : 'Erreur lors de la suppression du rôle'); + return of({ success: false, error: errorMessage }); + }) + ); + } + + // --------------- PERMISSIONS CRUD ---------------- + + getPermission(id: string): Observable { + return this.http + .get(`${this.permissionsUrl}/${id}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((p) => this.transformPermission(p)), + catchError((err) => { + console.error(`Error fetching permission ${id}:`, err); + return of(null); + }) + ); + } + + createPermission(payload: Omit): Observable { + const apiPayload = this.transformPermissionToApi(payload); + return this.http + .post(this.permissionsUrl, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((p) => this.transformPermission(p)), + catchError((err) => { + console.error('Error creating permission:', err); + throw err; + }) + ); + } + + updatePermission(id: string, payload: Partial): Observable { + const apiPayload = this.transformPermissionToApi(payload); + return this.http + .put(`${this.permissionsUrl}/${id}`, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((p) => this.transformPermission(p)), + catchError((err) => { + console.error(`Error updating permission ${id}:`, err); + return of(undefined); + }) + ); + } + + deletePermission(id: string): Observable<{ success: boolean; error?: string }> { + return this.http + .delete(`${this.permissionsUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => ({ success: true })), + catchError((err) => { + console.error(`Error deleting permission ${id}:`, err); + // Check if error is due to permission being used by roles + const errorMessage = + err?.error?.message || + err?.message || + (err?.status === 409 || err?.status === 400 + ? 'Cette permission est utilisée par des rôles et ne peut pas être supprimée' + : 'Erreur lors de la suppression de la permission'); + return of({ success: false, error: errorMessage }); + }) + ); + } +} diff --git a/src/app/core/services/theme.spec.ts b/src/app/core/services/theme.spec.ts new file mode 100644 index 0000000..e78dc3d --- /dev/null +++ b/src/app/core/services/theme.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { Theme } from './theme'; + +describe('Theme', () => { + let service: Theme; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(Theme); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/core/services/theme.ts b/src/app/core/services/theme.ts new file mode 100644 index 0000000..0aa6934 --- /dev/null +++ b/src/app/core/services/theme.ts @@ -0,0 +1,90 @@ +import { Injectable, OnDestroy, signal } from '@angular/core'; + +const STORAGE_KEY = 'pmu_theme'; // 'light' | 'dark' | 'system' + +type Mode = 'light' | 'dark' | 'system'; + +@Injectable({ providedIn: 'root' }) +export class Theme implements OnDestroy { + mode = signal('light'); + + private mql?: MediaQueryList; + private onMqlChange = (e: MediaQueryListEvent) => { + // only react if user selected "system" + if (this.mode() === 'system') this.apply('system', /*fromMql*/ true); + }; + + constructor() { + const saved = (localStorage.getItem(STORAGE_KEY) as Mode | null) ?? 'system'; + this.setupMql(); + this.apply(saved); + } + + ngOnDestroy(): void { + this.teardownMql(); + } + + toggle() { + // If you're on "system", decide based on current resolved value + const resolved = this.resolve(this.mode()); + const next: Mode = resolved === 'dark' ? 'light' : 'dark'; + this.apply(next); + } + + /** + * Optionally expose a 3-state cycle: + * light -> dark -> system -> light ... + */ + cycle() { + const order: Mode[] = ['light', 'dark', 'system']; + const i = order.indexOf(this.mode()); + this.apply(order[(i + 1) % order.length]); + } + + apply(next: Mode, fromMql = false) { + this.mode.set(next); + const root = document.documentElement; + const resolved = this.resolve(next); + + // toggle class + root.classList.toggle('dark', resolved === 'dark'); + // attribute for any 3rd-party styling + root.setAttribute('data-theme', resolved); + + // store only when user explicitly changed (avoid thrashing on mql change) + if (!fromMql) localStorage.setItem(STORAGE_KEY, next); + } + + private resolve(mode: Mode): 'light' | 'dark' { + // SSR guard + if (typeof window === 'undefined') return mode === 'dark' ? 'dark' : 'light'; + + if (mode !== 'system') return mode; + const prefersDark = this.mql?.matches ?? false; + return prefersDark ? 'dark' : 'light'; + } + + private setupMql() { + if (typeof window === 'undefined' || !window.matchMedia) return; + this.mql = window.matchMedia('(prefers-color-scheme: dark)'); + + // modern browsers + if ('addEventListener' in this.mql) { + this.mql.addEventListener('change', this.onMqlChange); + } else { + // Safari < 14 fallback + // @ts-expect-error legacy + this.mql.addListener(this.onMqlChange); + } + } + + private teardownMql() { + if (!this.mql) return; + if ('removeEventListener' in this.mql) { + this.mql.removeEventListener('change', this.onMqlChange); + } else { + // @ts-expect-error legacy + this.mql.removeListener(this.onMqlChange); + } + } +} diff --git a/src/app/core/services/tpe.ts b/src/app/core/services/tpe.ts new file mode 100644 index 0000000..ad4345f --- /dev/null +++ b/src/app/core/services/tpe.ts @@ -0,0 +1,467 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { map, catchError, switchMap } from 'rxjs/operators'; +import { TpeDevice, TpeStatus, TpeType } from '../interfaces/tpe'; +import { Agent, AgentStatus } from '../interfaces/agent'; +import { environment } from 'src/environments/environment.development'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { ListParams, PagedResult } from '@shared/paging/paging'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/tpes'; + +// Interface to match the API response structure for Agent (nested in TPE) +interface AgentApiResponse { + id: number; + code: string; + profile: string; + principalCode?: string; + caisseProfile?: string; + statut: string; + zone?: string; + kiosk?: string; + fonction?: string; + dateEmbauche?: string; + nom: string; + prenom: string; + autresNoms?: string; + dateNaissance?: string; + lieuNaissance?: string; + ville?: string; + adresse?: string; + autoriserAides?: boolean; + phone: string; + pin?: string; + limiteInferieure?: number; + limiteSuperieure?: number; + limiteParTransaction?: number; + limiteMinAirtime?: number; + limiteMaxAirtime?: number; + maxPeripheriques?: number; + limitId?: number; + nationalite?: string; + cni?: string; + cniDelivreeLe?: string; + cniDelivreeA?: string; + residence?: string; + autreAdresse1?: string; + statutMarital?: string; + epoux?: string; + autreTelephone?: string; + createdAt?: string; + updatedAt?: string; + createdBy?: string; +} + +// Interface to match the API response structure +interface TpeApiResponse { + id: number; + imei: string; + serial: string; + type: string; + marque: string; + modele: string; + statut: string; // API uses uppercase: VALIDE, INVALIDE, EN_PANNE, BLOQUE + agent?: AgentApiResponse; + assigne: boolean; + createdAt?: string; + updatedAt?: string; +} + +// Stats interfaces +interface CountByStatutResponse { + [key: string]: number; +} + +// Assignment stats is just a number (count of assigned TPEs) +type AssignesStatsResponse = number; + +@Injectable({ providedIn: 'root' }) +export class TpeService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor(private http: HttpClient) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + // Transform API statut to interface statut (both use uppercase now) + private transformStatut(apiStatut: string): TpeStatus { + const upperStatut = apiStatut.toUpperCase() as TpeStatus; + const validStatuses: TpeStatus[] = [ + 'VALIDE', + 'INVALIDE', + 'EN_PANNE', + 'BLOQUE', + 'DISPONIBLE', + 'AFFECTE', + 'EN_MAINTENANCE', + 'HORS_SERVICE', + 'VOLE', + ]; + return validStatuses.includes(upperStatut) ? upperStatut : 'INVALIDE'; + } + + // Transform interface statut to API statut (both use uppercase now, so direct return) + private transformStatutToApi(statut: TpeStatus): string { + return statut; // Already uppercase, no transformation needed + } + + // Transform API Agent response to Agent + private transformAgent(apiAgent: AgentApiResponse): Agent { + return { + id: String(apiAgent.id), + code: apiAgent.code, + profile: apiAgent.profile, + principalCode: apiAgent.principalCode, + caisseProfile: apiAgent.caisseProfile, + statut: apiAgent.statut as AgentStatus, + zone: apiAgent.zone, + kiosk: apiAgent.kiosk, + fonction: apiAgent.fonction, + dateEmbauche: apiAgent.dateEmbauche, + nom: apiAgent.nom, + prenom: apiAgent.prenom, + autresNoms: apiAgent.autresNoms, + dateNaissance: apiAgent.dateNaissance, + lieuNaissance: apiAgent.lieuNaissance, + ville: apiAgent.ville, + adresse: apiAgent.adresse, + autoriserAides: apiAgent.autoriserAides, + phone: apiAgent.phone, + pin: apiAgent.pin, + limiteInferieure: apiAgent.limiteInferieure, + limiteSuperieure: apiAgent.limiteSuperieure, + limiteParTransaction: apiAgent.limiteParTransaction, + limiteMinAirtime: apiAgent.limiteMinAirtime, + limiteMaxAirtime: apiAgent.limiteMaxAirtime, + maxPeripheriques: apiAgent.maxPeripheriques, + limitId: apiAgent.limitId ? String(apiAgent.limitId) : undefined, + nationalite: apiAgent.nationalite, + cni: apiAgent.cni, + cniDelivreeLe: apiAgent.cniDelivreeLe, + cniDelivreeA: apiAgent.cniDelivreeA, + residence: apiAgent.residence, + autreAdresse1: apiAgent.autreAdresse1, + statutMarital: apiAgent.statutMarital, + epoux: apiAgent.epoux, + autreTelephone: apiAgent.autreTelephone, + createdAt: apiAgent.createdAt, + updatedAt: apiAgent.updatedAt, + createdBy: apiAgent.createdBy, + }; + } + + // Transform API response to TpeDevice + private transformTpe(apiTpe: TpeApiResponse): TpeDevice { + return { + id: String(apiTpe.id), + imei: apiTpe.imei, + serial: apiTpe.serial, + type: apiTpe.type as TpeType, + marque: apiTpe.marque, + modele: apiTpe.modele, + statut: this.transformStatut(apiTpe.statut), + agent: apiTpe.agent ? this.transformAgent(apiTpe.agent) : undefined, + assigne: apiTpe.assigne, + createdAt: apiTpe.createdAt, + updatedAt: apiTpe.updatedAt, + }; + } + + // Transform TpeDevice to API payload + private transformToApiPayload(tpe: Partial): any { + const payload: any = {}; + if (tpe.imei !== undefined) payload.imei = tpe.imei; + if (tpe.serial !== undefined) payload.serial = tpe.serial; + if (tpe.type !== undefined) payload.type = tpe.type; + if (tpe.marque !== undefined) payload.marque = tpe.marque; + if (tpe.modele !== undefined) payload.modele = tpe.modele; + if (tpe.statut !== undefined) payload.statut = this.transformStatutToApi(tpe.statut); + if (tpe.assigne !== undefined) payload.assigne = tpe.assigne; + return payload; + } + + // GET /api/v1/tpes/{id} - Get by ID + getById(id: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiTpe) => this.transformTpe(apiTpe)), + catchError((err) => { + console.error(`Error fetching TPE ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // GET /api/v1/tpes - List all + list(params?: ListParams): Observable> { + if (USE_SERVER) { + let httpParams = new HttpParams(); + if (params) { + if (params.page) httpParams = httpParams.set('page', params.page.toString()); + if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString()); + if (params.search) httpParams = httpParams.set('search', params.search); + if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey); + if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir); + } + + return this.http + .get(this.apiUrl, { + params: httpParams, + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => { + const tpes = list.map((apiTpe) => this.transformTpe(apiTpe)); + // If pagination params provided, return paginated result + if (params) { + return normalizePage( + { data: tpes, meta: { total: tpes.length } }, + params.page || 1, + params.perPage || 10 + ); + } + // Otherwise return all as single page + return normalizePage( + { data: tpes, meta: { total: tpes.length } }, + 1, + tpes.length + ); + }), + catchError((err) => { + console.error('Error fetching TPEs:', err); + return of(normalizePage({ data: [], meta: { total: 0 } }, 1, 10)); + }) + ); + } + return of(normalizePage({ data: [], meta: { total: 0 } }, 1, 10)); + } + + // POST /api/v1/tpes - Create + create(payload: Omit): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .post(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() }) + .pipe( + map((apiTpe) => this.transformTpe(apiTpe)), + catchError((err) => { + console.error('Error creating TPE:', err); + throw err; + }) + ); + } + throw new Error('Server mode is required'); + } + + // PUT /api/v1/tpes/{id} - Update + update(id: string, payload: Partial): Observable { + if (USE_SERVER) { + const apiPayload = this.transformToApiPayload(payload); + return this.http + .put(`${this.apiUrl}/${id}`, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((apiTpe) => this.transformTpe(apiTpe)), + catchError((err) => { + console.error(`Error updating TPE ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // DELETE /api/v1/tpes/{id} - Delete + delete(id: string): Observable { + if (USE_SERVER) { + return this.http + .delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }) + .pipe( + map(() => true), + catchError((err) => { + console.error(`Error deleting TPE ${id}:`, err); + return of(false); + }) + ); + } + return of(false); + } + + // PATCH /api/v1/tpes/{id}/statut - Update statut + updateStatut(id: string, statut: TpeStatus): Observable { + if (USE_SERVER) { + return this.http + .patch( + `${this.apiUrl}/${id}/statut`, + { statut: this.transformStatutToApi(statut) }, + { headers: this.getNgrokHeaders() } + ) + .pipe( + map((apiTpe) => this.transformTpe(apiTpe)), + catchError((err) => { + console.error(`Error updating TPE statut ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // PATCH /api/v1/tpes/{id}/liberer - Liberate TPE (updates whole TPE, sets assigne to false and statut to DISPONIBLE) + liberer(id: string): Observable { + if (USE_SERVER) { + // First get the current TPE data + return this.getById(id).pipe( + switchMap((tpe) => { + if (!tpe) { + return of(undefined); + } + // Update the whole TPE with assigne set to false and statut to DISPONIBLE + const updatedTpe = { ...tpe, assigne: false, statut: 'DISPONIBLE' as TpeStatus }; + const apiPayload = this.transformToApiPayload(updatedTpe); + return this.http + .patch(`${this.apiUrl}/liberer/${id}`, apiPayload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((apiTpe) => this.transformTpe(apiTpe)), + catchError((err) => { + console.error(`Error liberating TPE ${id}:`, err); + return of(undefined); + }) + ); + }), + catchError((err) => { + console.error(`Error fetching TPE ${id} for liberation:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // PATCH /api/v1/tpes/assigner - Assign TPE + // Payload: { tpeId: number, agentId: number } + assigner(id: string, agentId: string): Observable { + if (USE_SERVER) { + const payload = { + tpeId: Number(id), + agentId: Number(agentId), + }; + return this.http + .patch(`${this.apiUrl}/assigner`, payload, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((apiTpe) => this.transformTpe(apiTpe)), + catchError((err) => { + console.error(`Error assigning TPE ${id}:`, err); + return of(undefined); + }) + ); + } + return of(undefined); + } + + // GET /api/v1/tpes/statut/{statut} - List by statut + getByStatut(statut: TpeStatus): Observable { + if (USE_SERVER) { + const apiStatut = this.transformStatutToApi(statut); + return this.http + .get(`${this.apiUrl}/statut/${apiStatut}`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiTpe) => this.transformTpe(apiTpe))), + catchError((err) => { + console.error(`Error fetching TPEs by statut ${statut}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/tpes/stats/count-by-statut - Get count by statut + getCountByStatut(): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/stats/count-by-statut`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError((err) => { + console.error('Error fetching TPE count by statut:', err); + return of({}); + }) + ); + } + return of({}); + } + + // GET /api/v1/tpes/stats/assignes - Get assignment stats (returns a number) + getAssignesStats(): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/stats/assignes`, { + headers: this.getNgrokHeaders(), + }) + .pipe( + catchError((err) => { + console.error('Error fetching TPE assignment stats:', err); + return of(0); + }) + ); + } + return of(0); + } + + // GET /api/v1/tpes/search - Search + search(query: string): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/search`, { + params: { q: query.trim() }, + headers: this.getNgrokHeaders(), + }) + .pipe( + map((list) => list.map((apiTpe) => this.transformTpe(apiTpe))), + catchError((err) => { + console.error(`Error searching TPEs with query ${query}:`, err); + return of([]); + }) + ); + } + return of([]); + } + + // GET /api/v1/tpes/disponibles - List available TPEs + getDisponibles(): Observable { + if (USE_SERVER) { + return this.http + .get(`${this.apiUrl}/disponibles`, { headers: this.getNgrokHeaders() }) + .pipe( + map((list) => list.map((apiTpe) => this.transformTpe(apiTpe))), + catchError((err) => { + console.error('Error fetching available TPEs:', err); + return of([]); + }) + ); + } + return of([]); + } +} diff --git a/src/app/core/services/user.ts b/src/app/core/services/user.ts new file mode 100644 index 0000000..30cd679 --- /dev/null +++ b/src/app/core/services/user.ts @@ -0,0 +1,162 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { User } from '../interfaces/user'; +import { PaginatedHttpService } from '@shared/paging/paginated-http.service'; +import { ListParams, PagedResult, SortDir } from '@shared/paging/paging'; +import { normalizePage } from '@shared/paging/normalize-page'; +import { environment } from 'src/environments/environment.development'; + +const USE_SERVER = true; +const API_BASE = '/api/v1/users'; + +// Backend payload +interface UserApiResponse { + id: number; + nom: string; + prenom: string; + identifiant: string; + password?: string; + matriculeAgent: string; + roleId: number; + restrictionConnexion: boolean; + restrictionAutomatique: boolean; + nombreIpAutorise: number; + nombreIpAutoAutorise: number; + statut: string; + derniereConnexion?: string; + createdAt?: string; + updatedAt?: string; +} + +@Injectable({ providedIn: 'root' }) +export class UserService { + private apiUrl = environment.apiBaseUrl + API_BASE; + + constructor(private http: HttpClient, private paginatedHttp: PaginatedHttpService) {} + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = + environment.apiBaseUrl.includes('ngrok-free.app') || + environment.apiBaseUrl.includes('ngrok.io') || + environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + private transform(api: UserApiResponse): User { + return { + id: String(api.id), + nom: api.nom, + prenom: api.prenom, + identifiant: api.identifiant, + // We never expose password back to UI + matriculeAgent: api.matriculeAgent, + roleId: String(api.roleId), + restrictionConnexion: api.restrictionConnexion, + restrictionAutomatique: api.restrictionAutomatique, + nombreIpAutorise: api.nombreIpAutorise, + nombreIpAutoAutorise: api.nombreIpAutoAutorise, + statut: api.statut, + derniereConnexion: api.derniereConnexion, + createdAt: api.createdAt, + updatedAt: api.updatedAt, + }; + } + + private transformToApiPayload(user: Partial): Partial { + return { + id: user.id ? Number(user.id) : undefined, + nom: user.nom ?? '', + prenom: user.prenom ?? '', + identifiant: user.identifiant ?? '', + password: user.password, + matriculeAgent: user.matriculeAgent ?? '', + roleId: user.roleId ? Number(user.roleId) : 0, + restrictionConnexion: user.restrictionConnexion ?? false, + restrictionAutomatique: user.restrictionAutomatique ?? false, + nombreIpAutorise: user.nombreIpAutorise ?? 0, + nombreIpAutoAutorise: user.nombreIpAutoAutorise ?? 0, + statut: user.statut ?? 'Actif', + derniereConnexion: user.derniereConnexion, + createdAt: user.createdAt, + updatedAt: user.updatedAt, + }; + } + + list(params: ListParams): Observable> { + if (USE_SERVER) { + // Backend returns full list; paginate client-side + return this.http + .get(this.apiUrl, { headers: this.getNgrokHeaders() }) + .pipe( + map((items) => (items || []).map((u) => this.transform(u))), + map((users) => { + const q = (params.search ?? '').toLowerCase(); + let data = users; + + if (q) { + data = data.filter((u) => + [u.nom, u.prenom, u.identifiant, u.matriculeAgent, u.statut] + .filter(Boolean) + .map((x) => String(x).toLowerCase()) + .some((s) => s.includes(q)) + ); + } + + if (params.sortKey && params.sortDir) { + const { sortKey, sortDir } = params as { sortKey: string; sortDir: SortDir }; + const getValue = (obj: any, path: string) => + path.split('.').reduce((o, k) => o?.[k], obj); + data = [...data].sort((a: any, b: any) => { + const sa = String(getValue(a, sortKey) ?? ''); + const sb = String(getValue(b, sortKey) ?? ''); + const cmp = sa.localeCompare(sb, 'fr', { numeric: true }); + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + const start = (params.page - 1) * params.perPage; + const pageData = data.slice(start, start + params.perPage); + + return normalizePage( + { data: pageData, meta: { total: data.length } }, + params.page, + params.perPage + ); + }), + catchError(() => + of(normalizePage({ data: [], meta: { total: 0 } }, params.page, params.perPage)) + ) + ); + } + + // Fallback should not be used anymore + return of(normalizePage({ data: [], meta: { total: 0 } }, params.page, params.perPage)); + } + + create(payload: Omit): Observable { + const body = this.transformToApiPayload(payload); + return this.http + .post(this.apiUrl, body, { headers: this.getNgrokHeaders() }) + .pipe(map((res) => this.transform(res))); + } + + update(id: string, payload: Partial): Observable { + const body = this.transformToApiPayload({ ...payload, id }); + return this.http + .put(`${this.apiUrl}/${id}`, body, { headers: this.getNgrokHeaders() }) + .pipe( + map((res) => this.transform(res)), + catchError(() => of(undefined)) + ); + } + + delete(id: string): Observable { + return this.http.delete(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() }).pipe( + map(() => true), + catchError(() => of(false)) + ); + } +} diff --git a/src/app/dashboard/dashboard-module.ts b/src/app/dashboard/dashboard-module.ts new file mode 100644 index 0000000..4ecbca6 --- /dev/null +++ b/src/app/dashboard/dashboard-module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { DashboardRoutingModule } from './dashboard-routing-module'; +import { + Ban, + FolderPen, + Lock, + LucideAngularModule, + Printer, + RefreshCw, + SlidersHorizontal, + Trash2, + Trophy, + Unlink2, +} from 'lucide-angular'; + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + DashboardRoutingModule, + LucideAngularModule.pick({ + FolderPen, + Trash2, + Ban, + Trophy, + Lock, + Printer, + RefreshCw, + SlidersHorizontal, + Unlink2, + }), + ], +}) +export class DashboardModule {} diff --git a/src/app/dashboard/dashboard-routing-module.ts b/src/app/dashboard/dashboard-routing-module.ts new file mode 100644 index 0000000..da7b5f2 --- /dev/null +++ b/src/app/dashboard/dashboard-routing-module.ts @@ -0,0 +1,67 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { Layout } from './layout/layout'; +import { authGuard } from '../core/guards/auth-guard'; + +const routes: Routes = [ + { + path: '', + component: Layout, + canActivate: [authGuard], + children: [ + { path: '', loadComponent: () => import('./pages/main/main').then((m) => m.Main) }, + { + path: 'courses', + loadComponent: () => import('./pages/courses/courses').then((m) => m.Course), + }, + { + path: 'hippodromes', + loadComponent: () => import('./pages/hippodrome/hippodrome').then((m) => m.Hippodrome), + }, + { + path: 'reunions', + loadComponent: () => import('./pages/reunion/reunion').then((m) => m.ReunionList), + }, + { + path: 'users', + loadComponent: () => import('./pages/users/users').then((m) => m.UsersPage), + }, + { + path: 'profile', + loadComponent: () => import('./pages/profile/profile').then((m) => m.ProfilePage), + }, + { + path: 'roles', + loadComponent: () => import('./pages/roles/roles').then((m) => m.RolesPage), + }, + { + path: 'tpes', + loadComponent: () => import('./pages/tpe/tpe').then((m) => m.TpePage), + }, + { + path: 'agents', + loadComponent: () => import('./pages/agents/agents').then((m) => m.AgentsPage), + }, + { + path: 'limits', + loadComponent: () => import('./pages/limits/limits').then((m) => m.LimitsPage), + }, + { + path: 'rapport-courses', + loadComponent: () => + import('./pages/report-courses/report-list').then((m) => m.ReportCoursesListPage), + }, + { + path: 'rapport-courses/:id', + loadComponent: () => + import('./pages/report-courses/report-detail').then((m) => m.ReportCoursesDetailPage), + }, + ], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class DashboardRoutingModule {} diff --git a/src/app/dashboard/layout/layout.css b/src/app/dashboard/layout/layout.css new file mode 100644 index 0000000..92d692c --- /dev/null +++ b/src/app/dashboard/layout/layout.css @@ -0,0 +1,3 @@ +:host { + display: contents; +} diff --git a/src/app/dashboard/layout/layout.html b/src/app/dashboard/layout/layout.html new file mode 100644 index 0000000..2da27af --- /dev/null +++ b/src/app/dashboard/layout/layout.html @@ -0,0 +1,182 @@ + + + + + + + +
    + + + + + + + + Home + + + + Components + + + + +
    + +
    +
    + +
    + +
    +
    +
    diff --git a/src/app/dashboard/layout/layout.spec.ts b/src/app/dashboard/layout/layout.spec.ts new file mode 100644 index 0000000..6ff67d8 --- /dev/null +++ b/src/app/dashboard/layout/layout.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Layout } from './layout'; + +describe('Layout', () => { + let component: Layout; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Layout] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Layout); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/layout/layout.ts b/src/app/dashboard/layout/layout.ts new file mode 100644 index 0000000..d358ec9 --- /dev/null +++ b/src/app/dashboard/layout/layout.ts @@ -0,0 +1,119 @@ +import { CommonModule } from '@angular/common'; +import { Component, signal } from '@angular/core'; +import { Router, RouterModule } from '@angular/router'; +import { ZardAvatarComponent } from '@shared/components/avatar/avatar.component'; +import { ZardBreadcrumbModule } from '@shared/components/breadcrumb/breadcrumb.module'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardDividerComponent } from '@shared/components/divider/divider.component'; +import { LayoutModule } from '@shared/components/layout/layout.module'; +import { ZardMenuModule } from '@shared/components/menu/menu.module'; +import { ZardTooltipModule } from '@shared/components/tooltip/tooltip'; +import { MenuItem } from 'src/app/core/interfaces/menu-item'; +import { Theme } from 'src/app/core/services/theme'; +import { ModeToggle } from '@shared/components/mode-toggle/mode-toggle'; +import { PmuLogo } from '@shared/components/pmu-logo/pmu-logo'; +import { User } from 'src/app/core/interfaces/user'; +import { Auth } from 'src/app/core/services/auth'; + +@Component({ + selector: 'app-layout', + imports: [ + CommonModule, + RouterModule, + LayoutModule, + ZardButtonComponent, + ZardBreadcrumbModule, + ZardMenuModule, + ZardTooltipModule, + ZardDividerComponent, + ZardAvatarComponent, + ModeToggle, + PmuLogo, + ], + templateUrl: './layout.html', + styleUrl: './layout.css', +}) +export class Layout { + sidebarCollapsed = signal(false); + user = signal(null); + + constructor(public theme: Theme, public auth: Auth, public router: Router) { + this.user.set(auth.getUser()); + } + + mainMenuItems: MenuItem[] = [ + { icon: '🏠', label: 'Tableau de bord', link: '/', exact: true }, + { icon: '🏟️', label: 'Hippodromes', link: '/hippodromes' }, + { icon: '📅', label: 'Reunions', link: '/reunions' }, + { icon: '🏇', label: 'Courses', link: '/courses' }, + { icon: 'icon-chart-bar', label: 'Rapport des courses', link: '/rapport-courses' }, + ]; + + workspaceMenuItems: MenuItem[] = [ + { + icon: 'icon-folder', + label: 'Gestion Agents', + submenu: [ + { icon: 'icon-user-plus', label: 'Gestion Agents', link: '/agents' }, + { icon: 'icon-sliders', label: 'Gestion limites Agents', link: '/limits' }, + ], + }, + { icon: 'icon-monitor', label: 'Gestion des TPE', link: '/tpes' }, + { + icon: 'icon-users', + label: 'Utilisateurs', + submenu: [ + { icon: 'icon-users', label: 'Liste des utilisateurs', link: '/users' }, + { icon: 'icon-shield', label: 'Rôles & Permissions', link: '/roles' }, + ], + }, + ]; + + avatar = { + fallback: 'ZA', + url: '/assets/images/avatar.svg', + alt: 'ZadUI', + }; + + toggleSidebar() { + this.sidebarCollapsed.update((collapsed) => !collapsed); + } + + onCollapsedChange(collapsed: boolean) { + this.sidebarCollapsed.set(collapsed); + } + + toggleTheme() { + this.theme.toggle(); + } + + async logout() { + this.auth.logout(); + await this.router.navigateByUrl('/auth/login'); + } + + navigate(link: string | undefined) { + if (link) { + this.router.navigateByUrl(link); + } + } + + isActive(link: string, exact = false): boolean { + const current = this.router.url; + return exact ? current === link : current.startsWith(link); + } + + isAnyActive(item: MenuItem): boolean { + if (item.link && this.isActive(item.link, !!item.exact)) return true; + if (item.submenu && item.submenu.length) { + return item.submenu.some((s) => !!s.link && this.isActive(s.link!, !!s.exact)); + } + return false; + } + + isEmoji(icon: string): boolean { + // simple: emojis are not alphanumeric or typical class names + // Detect if it contains non-ASCII characters + return /[^\u0000-\u00ff]/.test(icon); + } +} diff --git a/src/app/dashboard/pages/agents/agents.html b/src/app/dashboard/pages/agents/agents.html new file mode 100644 index 0000000..9baee08 --- /dev/null +++ b/src/app/dashboard/pages/agents/agents.html @@ -0,0 +1,405 @@ +
    +
    +

    Gestion des Agents

    + Nouvel agent +
    + + + + + +
    + + + + +
    +
    +
    + + +
    + + + +
    + Annuler + Enregistrer +
    +
    + + +@if (detailItem()) { + + @if (detailItem(); as agent) { +
    + + +
    Informations Emploi
    +
    +
    +
    Code
    +
    {{ agent.code }}
    +
    +
    +
    Profil
    +
    {{ agent.profile }}
    +
    +
    +
    Statut
    +
    + @if (agent.statut === 'ACTIF') { + + Actif + + } @else if (agent.statut === 'INACTIF') { + + Inactif + + } @else { + + Suspendu + + } +
    +
    + @if (agent.principalCode) { +
    +
    Agent Principal
    +
    {{ agent.principalCode }}
    +
    + } + @if (agent.zone) { +
    +
    Zone
    +
    {{ agent.zone }}
    +
    + } + @if (agent.kiosk) { +
    +
    Kiosque
    +
    {{ agent.kiosk }}
    +
    + } + @if (agent.fonction) { +
    +
    Fonction
    +
    {{ agent.fonction }}
    +
    + } + @if (agent.dateEmbauche) { +
    +
    Date Embauche
    +
    {{ agent.dateEmbauche | date: 'dd/MM/yyyy' }}
    +
    + } +
    +
    + + + +
    Informations Personnelles
    +
    +
    +
    Nom
    +
    {{ agent.nom }}
    +
    +
    +
    Prénom
    +
    {{ agent.prenom }}
    +
    + @if (agent.autresNoms) { +
    +
    Autre(s) Nom(s)
    +
    {{ agent.autresNoms }}
    +
    + } + @if (agent.dateNaissance) { +
    +
    Date de naissance
    +
    {{ agent.dateNaissance | date: 'dd/MM/yyyy' }}
    +
    + } + @if (agent.lieuNaissance) { +
    +
    Lieu de naissance
    +
    {{ agent.lieuNaissance }}
    +
    + } + @if (agent.ville) { +
    +
    Ville
    +
    {{ agent.ville }}
    +
    + } + @if (agent.adresse) { +
    +
    Adresse
    +
    {{ agent.adresse }}
    +
    + } + @if (agent.phone) { +
    +
    Téléphone
    +
    {{ agent.phone }}
    +
    + } + @if (agent.autoriserAides !== undefined) { +
    +
    Autoriser Aides
    +
    + @if (agent.autoriserAides) { + Oui + } @else { + Non + } +
    +
    + } +
    +
    + + + +
    Limites et Configuration
    +
    + @if (agent.limiteInferieure !== undefined) { +
    +
    Limite inférieure
    +
    {{ agent.limiteInferieure | number: '1.2-2' }}
    +
    + } + @if (agent.limiteSuperieure !== undefined) { +
    +
    Limite supérieure
    +
    {{ agent.limiteSuperieure | number: '1.2-2' }}
    +
    + } + @if (agent.limiteParTransaction !== undefined) { +
    +
    Limite / transaction
    +
    {{ agent.limiteParTransaction | number: '1.2-2' }}
    +
    + } + @if (agent.limiteMinAirtime !== undefined) { +
    +
    Limite min airtime
    +
    {{ agent.limiteMinAirtime | number: '1.2-2' }}
    +
    + } + @if (agent.limiteMaxAirtime !== undefined) { +
    +
    Limite max airtime
    +
    {{ agent.limiteMaxAirtime | number: '1.2-2' }}
    +
    + } + @if (agent.maxPeripheriques !== undefined) { +
    +
    Nbre max. périphériques
    +
    {{ agent.maxPeripheriques }}
    +
    + } +
    +
    + + + @if (agent.nationalite || agent.cni || agent.cniDelivreeLe || agent.residence || agent.statutMarital) { + +
    Informations Légales
    +
    + @if (agent.nationalite) { +
    +
    Nationalité
    +
    {{ agent.nationalite }}
    +
    + } + @if (agent.cni) { +
    +
    N° CNI
    +
    {{ agent.cni }}
    +
    + } + @if (agent.cniDelivreeLe) { +
    +
    CNI Délivrée le
    +
    {{ agent.cniDelivreeLe | date: 'dd/MM/yyyy' }}
    +
    + } + @if (agent.cniDelivreeA) { +
    +
    CNI Délivrée à
    +
    {{ agent.cniDelivreeA }}
    +
    + } + @if (agent.residence) { +
    +
    Résidence
    +
    {{ agent.residence }}
    +
    + } + @if (agent.statutMarital) { +
    +
    Statut marital
    +
    {{ agent.statutMarital }}
    +
    + } + @if (agent.epoux) { +
    +
    Époux/Épouse
    +
    {{ agent.epoux }}
    +
    + } +
    +
    + } + + + @if (detailFamilyMembers().length > 0) { + +
    Membres de famille
    +
    + @for (member of detailFamilyMembers(); track member.id || $index) { +
    +
    +
    +
    +
    Nom
    +
    {{ member.nom }}
    +
    + @if (member.statut) { +
    +
    Statut
    +
    {{ member.statut }}
    +
    + } + @if (member.dateNaissance) { +
    +
    Date de naissance
    +
    {{ member.dateNaissance | date: 'dd/MM/yyyy' }}
    +
    + } + @if (member.sexe) { +
    +
    Sexe
    +
    {{ member.sexe === 'M' ? 'Masculin' : 'Féminin' }}
    +
    + } +
    +
    +
    + } +
    +
    + } + + + @if (getAgentTpes(agent.id).length > 0) { + +
    TPE Assignés ({{ getAgentTpes(agent.id).length }})
    +
    + @for (tpe of getAgentTpes(agent.id); track tpe.id) { +
    +
    +
    {{ tpe.imei }}
    + @if (tpe.statut) { + + {{ formatTpeStatut(tpe.statut) }} + + } +
    +
    + @if (tpe.marque || tpe.modele) { +
    + Modèle: {{ tpe.marque }} {{ tpe.modele }} +
    + } + @if (tpe.serial) { +
    + Série: {{ tpe.serial }} +
    + } + @if (tpe.type) { +
    + Type: {{ tpe.type }} +
    + } +
    +
    + } +
    +
    + } +
    + } +
    + Fermer + @if (detailItem()) { + + Modifier + + } +
    +
    +} + + +@if (assigningAgent()) { + +
    + @if (tpesLoading()) { +
    Chargement des TPE disponibles...
    + } @else if (availableTpes().length === 0) { +
    Aucun TPE disponible
    + } @else { + + +
    + + @for (tpe of availableTpes(); track tpe.id) { + + {{ tpe.imei }} - {{ tpe.marque }} {{ tpe.modele }} + @if (tpe.statut === 'VALIDE') { + (Valide) + } + + } + +
    +
    + } +
    +
    + Annuler + +
    +
    +} diff --git a/src/app/dashboard/pages/agents/agents.ts b/src/app/dashboard/pages/agents/agents.ts new file mode 100644 index 0000000..5c8c049 --- /dev/null +++ b/src/app/dashboard/pages/agents/agents.ts @@ -0,0 +1,483 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ViewChild, + effect, + signal, + untracked, +} from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { SortDir } from '@shared/paging/paging'; +import { Agent, AgentFamilyMember } from 'src/app/core/interfaces/agent'; +import { AgentService } from 'src/app/core/services/agent'; +import { AgentFamilyMemberService } from 'src/app/core/services/agent-family-member'; +import { TpeService } from 'src/app/core/services/tpe'; +import { TpeDevice, TpeStatus } from 'src/app/core/interfaces/tpe'; +import { AgentFullForm } from '@shared/forms/agent-full-form/agent-full-form'; +import { forkJoin, of } from 'rxjs'; +import { switchMap, catchError } from 'rxjs/operators'; + +@Component({ + standalone: true, + selector: 'app-agents', + templateUrl: './agents.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + DataTable, + Paginator, + SearchBar, + Modal, + ZardButtonComponent, + ZardCardComponent, + ZardSelectComponent, + ZardSelectItemComponent, + ZardFormModule, + AgentFullForm, + ], +}) +export class AgentsPage { + rows = signal([]); + total = signal(0); + loading = signal(false); + + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'code', dir: 'asc' }); + + modalOpen = signal(false); + modalTitle = signal('Nouvel agent'); + editingItem = signal(null); + + detailModalOpen = signal(false); + detailItem = signal(null); + detailFamilyMembers = signal([]); + + // TPE Assignment modal + assignTpeModalOpen = signal(false); + assigningAgent = signal(null); + availableTpes = signal([]); + selectedTpeId = signal(''); + tpesLoading = signal(false); + + @ViewChild(AgentFullForm) formComp?: AgentFullForm; + + formatTpeStatut(statut: TpeStatus): string { + const statutMap: Record = { + VALIDE: 'Valide', + INVALIDE: 'Invalide', + EN_PANNE: 'En panne', + BLOQUE: 'Bloqué', + DISPONIBLE: 'Disponible', + AFFECTE: 'Affecté', + EN_MAINTENANCE: 'En maintenance', + HORS_SERVICE: 'Hors service', + VOLE: 'Volé', + }; + return statutMap[statut] || statut; + } + + cols: TableColumn[] = [ + { key: 'code', label: 'Code', sortable: true }, + { key: 'nom', label: 'Nom', sortable: true }, + { key: 'prenom', label: 'Prénom', sortable: true }, + { key: 'phone', label: 'Téléphone', sortable: true }, + { + key: 'tpes', + label: 'TPE assignés', + cell: (a) => { + const tpes = this.agentTpesMap.get(a.id) || []; + if (tpes.length === 0) { + return 'Aucun'; + } + // Show up to 2 TPEs with full details, then count for the rest + const displayCount = Math.min(2, tpes.length); + const displayed = tpes.slice(0, displayCount); + const remaining = tpes.length - displayCount; + + const tpeCards = displayed + .map((t) => { + const imei = `
    ${t.imei}
    `; + const details = [ + t.marque && t.modele ? `${t.marque} ${t.modele}` : t.marque || t.modele || '', + t.statut ? this.formatTpeStatut(t.statut) : '', + ] + .filter(Boolean) + .join(' • '); + const detailsHtml = details + ? `
    ${details}
    ` + : ''; + return `
    ${imei}${detailsHtml}
    `; + }) + .join(' '); + + const moreHtml = + remaining > 0 + ? `
    +${remaining} autre${ + remaining > 1 ? 's' : '' + }
    ` + : ''; + + return `
    ${tpeCards}${moreHtml}
    `; + }, + }, + { key: 'zone', label: 'Zone', sortable: true }, + { key: 'kiosk', label: 'Kiosque', sortable: true }, + { key: 'profile', label: 'Profil', sortable: true }, + { key: 'statut', label: 'Statut', sortable: true }, + { key: 'limiteSuperieure', label: 'Limite sup.', sortable: true }, + ]; + + tpeMap = new Map(); + agentTpesMap = new Map(); + + constructor( + private api: AgentService, + private tpeSvc: TpeService, + private familyMemberService: AgentFamilyMemberService + ) { + // Preload TPE maps for display + this.tpeSvc + .list({ page: 1, perPage: 200, search: '', sortKey: 'imei', sortDir: 'asc' } as any) + .subscribe((res) => { + const tpes = res.data as TpeDevice[]; + this.rebuildTpeMaps(tpes); + }); + effect(() => { + const params = { + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }; + untracked(() => this.fetch(params)); + }); + } + + private fetch(params: { + page: number; + perPage: number; + search: string; + sortKey: string; + sortDir: SortDir; + }) { + this.loading.set(true); + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.loading.set(false); + // Refresh TPE map to ensure we have latest data + this.refreshTpeMap(); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + + private refreshTpeMap() { + this.tpeSvc + .list({ page: 1, perPage: 200, search: '', sortKey: 'imei', sortDir: 'asc' } as any) + .subscribe((res) => { + const tpes = res.data as TpeDevice[]; + this.rebuildTpeMaps(tpes); + }); + } + + private rebuildTpeMaps(tpes: TpeDevice[]) { + this.tpeMap.clear(); + this.agentTpesMap.clear(); + tpes.forEach((t) => { + this.tpeMap.set(t.id, t); + const agentId = t.agent?.id; + if (agentId) { + const list = this.agentTpesMap.get(agentId) || []; + list.push(t); + this.agentTpesMap.set(agentId, list); + } + }); + } + + getAgentTpes(agentId: string): TpeDevice[] { + return this.agentTpesMap.get(agentId) || []; + } + + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + } + openCreate() { + this.modalTitle.set('Nouvel agent'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + openEdit(row: Agent) { + this.modalTitle.set("Modifier l'agent"); + this.editingItem.set(row); + queueMicrotask(() => this.modalOpen.set(true)); + } + closeModal() { + this.modalOpen.set(false); + } + openDetail(row: Agent) { + // Fetch full agent details + this.api.getById(row.id).subscribe({ + next: (agent) => { + if (agent) { + this.detailItem.set(agent); + // Load family members separately + this.familyMemberService.getByAgentId(agent.id).subscribe({ + next: (members) => { + this.detailFamilyMembers.set(members); + }, + error: () => { + this.detailFamilyMembers.set([]); + }, + }); + this.detailModalOpen.set(true); + } + }, + error: () => { + // If fetch fails, use the row data + this.detailItem.set(row); + // Try to load family members anyway + this.familyMemberService.getByAgentId(row.id).subscribe({ + next: (members) => { + this.detailFamilyMembers.set(members); + }, + error: () => { + this.detailFamilyMembers.set([]); + }, + }); + this.detailModalOpen.set(true); + }, + }); + } + closeDetailModal() { + this.detailModalOpen.set(false); + this.detailItem.set(null); + this.detailFamilyMembers.set([]); + } + submitChildForm() { + this.formComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const familyMembersData = this.formComp?.getFamilyMembersData() || []; + + // Save agent first + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + + req$ + .pipe( + switchMap((result) => { + if (!result && current?.id) { + // Update failed + throw new Error("Erreur lors de la sauvegarde de l'agent"); + } + + const savedAgentId = result?.id || current?.id || ''; + if (!savedAgentId) { + throw new Error("Impossible d'obtenir l'ID de l'agent sauvegardé"); + } + + // Get existing family members for this agent + return this.familyMemberService.getByAgentId(savedAgentId).pipe( + switchMap((existingMembers) => { + const existingIds = new Set(existingMembers.map((m) => m.id)); + const newMembers = familyMembersData.filter((fm) => !fm.id); + const updatedMembers = familyMembersData.filter( + (fm) => fm.id && existingIds.has(fm.id) + ); + const deletedIds = existingMembers + .filter((em) => !familyMembersData.some((fm) => fm.id === em.id)) + .map((em) => em.id); + + const operations: any[] = []; + + // Delete removed members + deletedIds.forEach((id) => { + operations.push( + this.familyMemberService.delete(id).pipe( + catchError((err) => { + console.error(`Error deleting family member ${id}:`, err); + return of(false); + }) + ) + ); + }); + + // Create new members + newMembers.forEach((member) => { + operations.push( + this.familyMemberService + .create({ + agentId: savedAgentId, + nom: member.nom, + statut: member.statut, + dateNaissance: member.dateNaissance, + sexe: member.sexe as 'M' | 'F' | undefined, + }) + .pipe( + catchError((err) => { + console.error('Error creating family member:', err); + return of(null); + }) + ) + ); + }); + + // Update existing members + updatedMembers.forEach((member) => { + if (member.id) { + operations.push( + this.familyMemberService + .update(member.id, { + nom: member.nom, + statut: member.statut, + dateNaissance: member.dateNaissance, + sexe: member.sexe as 'M' | 'F' | undefined, + }) + .pipe( + catchError((err) => { + console.error(`Error updating family member ${member.id}:`, err); + return of(null); + }) + ) + ); + } + }); + + return operations.length > 0 ? forkJoin(operations) : of([]); + }) + ); + }) + ) + .subscribe({ + next: () => { + // Reset form after successful save + this.formComp?.resetForm(); + // Clear editing item + this.editingItem.set(null); + // Close modal + this.closeModal(); + // Refresh data + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + }, + error: (err) => { + console.error('Error saving agent:', err); + alert("Erreur lors de la sauvegarde de l'agent"); + }, + }); + } + + remove(row: Agent) { + if (!confirm(`Supprimer l\'agent ${row.code} ?`)) return; + this.api.delete(row.id).subscribe(() => + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }) + ); + } + + openAssignTpe(agent: Agent) { + this.assigningAgent.set(agent); + this.selectedTpeId.set(''); + this.loadAvailableTpes(); + this.assignTpeModalOpen.set(true); + } + + loadAvailableTpes() { + this.tpesLoading.set(true); + const agent = this.assigningAgent(); + if (!agent) { + this.availableTpes.set([]); + this.tpesLoading.set(false); + return; + } + + const currentAgentTpes = this.agentTpesMap.get(agent.id) || []; + const agentTpeIds = new Set(currentAgentTpes.map((t) => t.id)); + + // Load available TPEs (DISPONIBLE or VALIDE status) + forkJoin([this.tpeSvc.getByStatut('DISPONIBLE'), this.tpeSvc.getByStatut('VALIDE')]).subscribe({ + next: ([disponibleTpes, valideTpes]) => { + // Combine and filter: only show TPEs that are not assigned to any agent AND not already assigned to this agent + const allTpes = [...disponibleTpes, ...valideTpes]; + const available = allTpes.filter( + (t) => + !t.assigne && + (t.statut === 'DISPONIBLE' || t.statut === 'VALIDE') && + !agentTpeIds.has(t.id) + ); + // Remove duplicates + const uniqueTpes = Array.from(new Map(available.map((t) => [t.id, t])).values()); + this.availableTpes.set(uniqueTpes); + this.tpesLoading.set(false); + }, + error: () => { + this.availableTpes.set([]); + this.tpesLoading.set(false); + }, + }); + } + + confirmAssignTpe() { + const agent = this.assigningAgent(); + const tpeId = this.selectedTpeId(); + if (!agent || !tpeId) { + alert('Veuillez sélectionner un TPE'); + return; + } + + // Assign TPE to agent + this.tpeSvc.assigner(tpeId, agent.id).subscribe({ + next: (tpe) => { + if (tpe) { + // Fermer le modal et recharger complètement la page + this.assignTpeModalOpen.set(false); + this.assigningAgent.set(null); + this.selectedTpeId.set(''); + // Rechargement complet pour s'assurer que la liste des agents / TPE est à jour + window.location.reload(); + } + }, + error: () => { + alert("Erreur lors de l'assignation du TPE"); + }, + }); + } + + closeAssignTpeModal() { + this.assignTpeModalOpen.set(false); + this.assigningAgent.set(null); + this.selectedTpeId.set(''); + } +} diff --git a/src/app/dashboard/pages/courses/courses.css b/src/app/dashboard/pages/courses/courses.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dashboard/pages/courses/courses.html b/src/app/dashboard/pages/courses/courses.html new file mode 100644 index 0000000..cffc9e0 --- /dev/null +++ b/src/app/dashboard/pages/courses/courses.html @@ -0,0 +1,171 @@ +
    + +
    +

    Courses

    + +
    + + +
    + +
    Total des courses
    +
    + {{ totalCourses() }} +
    +
    + + +
    En cours
    +
    + {{ runningCourses() }} +
    +
    + + +
    Clôturées
    +
    + {{ closedCourses() }} +
    +
    + + +
    Par type
    +
    + @for (type of (byType() | keyvalue); track type.key) { +
    + {{ type.key }} + {{ type.value }} +
    + } +
    +
    +
    + + + + + +
    + + + @if (!isClosed(row)) { +
    + + + + +
    + } @else { + + + Fermée + + } +
    +
    + + +
    + + + + @if(modalOpen()) { + + } +
    + Annuler + Enregistrer +
    +
    + + @if(selectedCourse()) { + + + +
    + Annuler + Enregistrer +
    +
    + } @if(selectedCourseForResultat()) { + + +
    + Fermer +
    +
    + } +
    diff --git a/src/app/dashboard/pages/courses/courses.spec.ts b/src/app/dashboard/pages/courses/courses.spec.ts new file mode 100644 index 0000000..8866c16 --- /dev/null +++ b/src/app/dashboard/pages/courses/courses.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Courses } from './courses'; + +describe('Courses', () => { + let component: Courses; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Courses] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Courses); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/pages/courses/courses.ts b/src/app/dashboard/pages/courses/courses.ts new file mode 100644 index 0000000..a0600ee --- /dev/null +++ b/src/app/dashboard/pages/courses/courses.ts @@ -0,0 +1,554 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + effect, + signal, + ViewChild, + untracked, +} from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { Course as CourseType } from 'src/app/core/interfaces/course'; +import { SortDir } from '@shared/paging/paging'; +import { CourseService } from 'src/app/core/services/course'; +import { ResultatService } from 'src/app/core/services/resultat'; +import { Resultat } from 'src/app/core/interfaces/resultat'; +import { A11yModule } from '@angular/cdk/a11y'; +import { CourseForm } from '@shared/forms/course-form/course-form'; +import { NonPartantForm } from '@shared/forms/nonpartant-form/nonpartant-form'; +import { LucideAngularModule } from 'lucide-angular'; +import { ResultatForm } from '@shared/forms/resultat-form/resultat-form'; +import { toast } from 'ngx-sonner'; +import { forkJoin, of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +@Component({ + standalone: true, + selector: 'app-course-list', + imports: [ + CommonModule, + DataTable, + Paginator, + SearchBar, + Modal, + CourseForm, + NonPartantForm, + ResultatForm, + ZardCardComponent, + ZardButtonComponent, + A11yModule, + LucideAngularModule, + ], + templateUrl: './courses.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Course { + rows = signal([]); + resultatsMap = signal>(new Map()); + loading = signal(false); + total = signal(0); + totalRunning = signal(0); + totalClosed = signal(0); + totalByType = signal>({}); + + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'numero', dir: 'asc' }); + pageSize = [10, 20, 50]; + + modalOpen = signal(false); + modalTitle = signal('Nouvelle course'); + editingItem = signal(null); + + @ViewChild(CourseForm) formComp?: CourseForm; + + // 🟩 Corrected columns + cols: TableColumn[] = [ + { key: 'numero', label: 'N°', sortable: true }, + { key: 'nom', label: 'Nom', sortable: true }, + { + key: 'type', + label: 'Type', + sortable: true, + cell: (c) => `${c.type}`, + }, + { + key: 'dateDepartCourse', + label: 'Date et Heure Départ', + sortable: true, + cell: (c) => + new Date(c.dateDepartCourse).toLocaleDateString('fr-FR', { + hour: '2-digit', + minute: '2-digit', + }), + }, + { + key: 'partants', + label: 'Partants', + cell: (c) => + `${c.partants} (${ + c.nonPartants?.length ?? 0 + } NP)`, + }, + { + key: 'resultat', + label: 'Résultat', + cell: (c) => { + const resultat = this.resultatsMap().get(c.id); + if (!resultat || !resultat.ordreArrivee || resultat.ordreArrivee.length === 0) { + return ''; + } + + // Group horses that are at the same place (ex-aequo/dead heat). + // Backend/Resultat model store ordreArrivee as cheval numbers (1,2,3,...) and + // chevauxDeadHeat as the subset that are ex-aequo. + const deadHeatSet = new Set(resultat.chevauxDeadHeat || []); + + const groups: number[][] = []; + let currentGroup: number[] = []; + + resultat.ordreArrivee.forEach((num, index) => { + const isInDeadHeat = deadHeatSet.has(num); + const prevNum = index > 0 ? resultat.ordreArrivee[index - 1] : null; + const prevIsInDeadHeat = prevNum !== null && deadHeatSet.has(prevNum); + + if (isInDeadHeat && prevIsInDeadHeat && currentGroup.length > 0) { + // Continue the current dead heat group + currentGroup.push(num); + } else { + // Start a new group + if (currentGroup.length > 0) { + groups.push(currentGroup); + } + currentGroup = [num]; + } + }); + + // Don't forget the last group + if (currentGroup.length > 0) { + groups.push(currentGroup); + } + + const s = groups.map((nums) => nums.join('=')).join(' - '); + + // For now, we'll show the resultat. In the future, we might add a statut field to Resultat + return `${s}`; + }, + }, + { + key: 'statut', + label: 'Statut', + sortable: true, + cell: (c) => { + const colorMap: Record = { + PROGRAMMEE: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300', + CREATED: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-300', + VALIDATED: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', + RUNNING: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', + CLOSED: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300', + CANCELED: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300', + }; + const labelMap: Record = { + PROGRAMMEE: 'Programmée', + CREATED: 'Créée', + VALIDATED: 'Validée', + RUNNING: 'En cours', + CLOSED: 'Clôturée', + CANCELED: 'Annulée', + }; + return `${ + labelMap[c.statut] + }`; + }, + }, + { + key: 'reunion.hippodrome.nom', + label: 'Hippodrome', + cell: (c) => (c.reunion?.hippodrome ? `${c.reunion.hippodrome.nom}` : '—'), + }, + { + key: 'reunion.nom', + label: 'Réunion', + cell: (c) => c.reunion?.nom ?? '—', + }, + { + key: 'distance', + label: 'Distance (m)', + sortable: true, + cell: (c) => c.distance.toLocaleString('fr-FR'), + }, + + { + key: 'createdAt', + label: 'Créée le', + cell: (c) => + c.createdAt + ? new Date(c.createdAt).toLocaleDateString('fr-FR', { + day: '2-digit', + month: 'short', + year: 'numeric', + }) + : '—', + }, + ]; + + visibleKeys = signal([]); + + constructor(private api: CourseService, private resultatService: ResultatService) { + effect(() => { + const params = { + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }; + untracked(() => this.fetch(params)); + }); + } + + private fetch(params: { + page: number; + perPage: number; + search: string; + sortKey: string; + sortDir: SortDir; + }) { + this.loading.set(true); + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.totalRunning.set(res.meta['totalRunning'] ?? 0); + this.totalClosed.set(res.meta['totalClosed'] ?? 0); + this.totalByType.set(res.meta['totalByType'] ?? {}); + + // Fetch resultats for all courses in parallel + const courseIds = res.data.map((c) => c.id); + if (courseIds.length > 0) { + const resultatRequests = courseIds.map((id) => + this.resultatService.getByCourseId(id).pipe(catchError(() => of(undefined))) + ); + + forkJoin(resultatRequests).subscribe({ + next: (resultats) => { + const resultatsMap = new Map(); + courseIds.forEach((id, index) => { + const resultat = resultats[index]; + if (resultat) { + resultatsMap.set(id, resultat); + } + }); + this.resultatsMap.set(resultatsMap); + this.loading.set(false); + }, + error: () => { + this.resultatsMap.set(new Map()); + this.loading.set(false); + }, + }); + } else { + this.resultatsMap.set(new Map()); + this.loading.set(false); + } + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.totalRunning.set(0); + this.totalClosed.set(0); + this.totalByType.set({}); + this.resultatsMap.set(new Map()); + this.loading.set(false); + }, + }); + } + + // === UI Actions === + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + } + + openCreate() { + this.modalTitle.set('Nouvelle course'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + + isClosed = (c: CourseType | null | undefined) => + c?.statut === 'CLOSED' || c?.statut === 'CANCELED'; + + openEdit(row: CourseType) { + if (this.isClosed(row)) return; + this.modalTitle.set('Modifier la course'); + this.editingItem.set(row); + queueMicrotask(() => this.modalOpen.set(true)); + } + + closeModal() { + this.modalOpen.set(false); + } + + submitChildForm() { + this.formComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + + req$.subscribe(() => { + this.closeModal(); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }); + }); + } + + remove(row: CourseType) { + if (this.isClosed(row)) return; + if (!confirm(`Supprimer la course « ${row.nom} » ?`)) return; + this.api.delete(row.id).subscribe(() => + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }) + ); + } + + // === Stats Computed === + totalCourses = computed(() => this.total()); + runningCourses = computed(() => this.totalRunning()); + closedCourses = computed(() => this.totalClosed()); + byType = computed(() => this.totalByType()); + + nonPartantModalOpen = signal(false); + selectedCourse = signal(null); + @ViewChild(NonPartantForm) npForm?: NonPartantForm; + + openResultat(row: CourseType) { + if (this.isClosed(row)) return; + this.selectedCourseForResultat.set(row); + this.resultatModalOpen.set(true); + } + + openNonPartant(row: CourseType) { + if (this.isClosed(row)) return; + this.selectedCourse.set(row); + this.nonPartantModalOpen.set(true); + } + + closeNonPartantModal() { + this.nonPartantModalOpen.set(false); + this.selectedCourse.set(null); + } + + submitNonPartant() { + this.npForm?.onSubmit(); + } + + onNonPartantSave(payload: string[]) { + const course = this.selectedCourse(); + if (!course) return; + + this.api.setNonPartants(course.id, payload).subscribe({ + next: (updatedCourse) => { + if (updatedCourse) { + toast.success('Non-partants mis à jour avec succès'); + } else { + toast.error('Erreur lors de la mise à jour des non-partants'); + } + this.closeNonPartantModal(); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }); + }, + error: (err) => { + console.error('Error saving non-partants:', err); + toast.error('Erreur lors de la mise à jour des non-partants'); + }, + }); + } + + resultatModalOpen = signal(false); + selectedCourseForResultat = signal(null); + + closeResultatModal() { + this.resultatModalOpen.set(false); + this.selectedCourseForResultat.set(null); + } + + onResultatSave(places: number[][]) { + const c = this.selectedCourseForResultat(); + if (!c) return; + + // Determine required number of horses based on course type + const getRequiredHorses = (type: string): number => { + const typeStr = String(type).toUpperCase(); + if (typeStr.includes('TIERCE') || typeStr === 'PLAT') return 3; + if (typeStr.includes('QUARTE')) return 4; + if (typeStr.includes('QUINTE')) return 5; + return 3; // Default + }; + + const requiredHorses = getRequiredHorses(c.type); + + // Collect all selected horses (flatten the places array) + const allHorses: number[] = places + .flatMap((placeGroup) => placeGroup.filter((n) => typeof n === 'number' && n > 0)) + .slice(0, requiredHorses); // Only take the first N horses + + // Check if all horses are in first place (ex-aequo) + const firstPlaceHorses = places[0]?.filter((n) => typeof n === 'number' && n > 0) || []; + const isAllExAequo = + firstPlaceHorses.length === requiredHorses && allHorses.length === requiredHorses; + + // Convert to ordreArrivee format + // If all are ex-aequo, they all go in ordreArrivee as they are (first place) + // Otherwise, distribute them across places + const ordreArrivee: Array = []; + const chevauxDeadHeat: number[] = []; + + if (isAllExAequo) { + // All horses are in first place (ex-aequo) + allHorses.forEach((numero) => { + ordreArrivee.push(numero.toString()); + chevauxDeadHeat.push(numero); + }); + } else { + // Horses are distributed across places + places.forEach((placeGroup, placeIndex) => { + const validHorses = placeGroup.filter((n) => typeof n === 'number' && n > 0); + if (validHorses.length === 0) return; + + const isDeadHeat = validHorses.length > 1; + + validHorses.forEach((numero) => { + ordreArrivee.push(numero.toString()); + + if (isDeadHeat) { + chevauxDeadHeat.push(numero); + } + }); + }); + } + + // Check if resultat already exists + const existingResultat = this.resultatsMap().get(c.id); + + const payload = { + course: { id: c.id }, + ordreArrivee, + chevauxDeadHeat: chevauxDeadHeat.map((n) => String(n)), + totalMises: 0, + masseAPartager: 0, + prelevementsLegaux: 0, + montantRembourse: 0, + montantCagnotte: 0, + adeadHeat: chevauxDeadHeat.length > 0, + }; + + const request$ = existingResultat + ? this.resultatService.update(existingResultat.id, payload) + : this.resultatService.create(payload); + + request$.subscribe({ + next: () => { + this.closeResultatModal(); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }); + toast.success('Résultat enregistré avec succès'); + }, + error: (err) => { + console.error('Error saving resultat:', err); + toast.error("Erreur lors de l'enregistrement du résultat"); + }, + }); + } + + onResultatValidate() { + const c = this.selectedCourseForResultat(); + if (!c) return; + + const resultat = this.resultatsMap().get(c.id); + if (!resultat) { + toast.error('Aucun résultat à valider'); + return; + } + + // For now, validation is just an update. In the future, you might add a statut field + this.resultatService.update(resultat.id, {}).subscribe({ + next: () => { + this.closeResultatModal(); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }); + toast.success('Résultat validé avec succès'); + }, + error: (err) => { + console.error('Error validating resultat:', err); + toast.error('Erreur lors de la validation du résultat'); + }, + }); + } + + onResultatConfirm() { + const c = this.selectedCourseForResultat(); + if (!c) return; + + const resultat = this.resultatsMap().get(c.id); + if (!resultat) { + toast.error('Aucun résultat à confirmer'); + return; + } + + // For now, confirmation is just an update. In the future, you might add a statut field + this.resultatService.update(resultat.id, {}).subscribe({ + next: () => { + this.closeResultatModal(); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }); + toast.success('Résultat confirmé avec succès'); + }, + error: (err) => { + console.error('Error confirming resultat:', err); + toast.error('Erreur lors de la confirmation du résultat'); + }, + }); + } +} diff --git a/src/app/dashboard/pages/hippodrome/hippodrome.css b/src/app/dashboard/pages/hippodrome/hippodrome.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dashboard/pages/hippodrome/hippodrome.html b/src/app/dashboard/pages/hippodrome/hippodrome.html new file mode 100644 index 0000000..62f9a9d --- /dev/null +++ b/src/app/dashboard/pages/hippodrome/hippodrome.html @@ -0,0 +1,132 @@ +
    +
    +

    Hippodromes

    + +
    + + +
    + +
    Total des hippodromes
    +
    + {{ total() }} +
    +
    + + +
    Pays représentés
    +
    + {{ uniqueCountries() }} +
    +
    + + +
    Villes uniques
    +
    + {{ uniqueCities() }} +
    +
    + + +
    Moyenne par pays
    +
    + {{ averageByCountry() }} +
    +
    + +
    Réunions totales
    +
    + {{ totalReunions() }} +
    +
    + +
    Courses totales
    +
    + {{ totalCourses() }} +
    +
    +
    + + + +
    + + + + + + {{ row.actif ? 'Actif' : 'Inactif' }} + + + + + + + {{ row.createdAt | date : 'shortDate' }} + + + + + +
    + + +
    +
    +
    + + +
    + + + + + +
    + Annuler + Enregistrer +
    +
    +
    diff --git a/src/app/dashboard/pages/hippodrome/hippodrome.spec.ts b/src/app/dashboard/pages/hippodrome/hippodrome.spec.ts new file mode 100644 index 0000000..ba008ca --- /dev/null +++ b/src/app/dashboard/pages/hippodrome/hippodrome.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Hippodrome } from './hippodrome'; + +describe('Hippodrome', () => { + let component: Hippodrome; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Hippodrome] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Hippodrome); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/pages/hippodrome/hippodrome.ts b/src/app/dashboard/pages/hippodrome/hippodrome.ts new file mode 100644 index 0000000..4cf34ee --- /dev/null +++ b/src/app/dashboard/pages/hippodrome/hippodrome.ts @@ -0,0 +1,204 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + TemplateRef, + ViewChild, + computed, + effect, + signal, +} from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Modal } from '@shared/components/modal/modal'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { HippodromeForm } from '@shared/forms/hippodrome-form/hippodrome-form'; +import { Hippodrome as HippodromeType } from 'src/app/core/interfaces/hippodrome'; +import { HippodromeService } from 'src/app/core/services/hippodrome'; +import { ZardBreadcrumbModule } from '@shared/components/sheet/sheet.module'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { LucideAngularModule } from 'lucide-angular'; + +@Component({ + standalone: true, + selector: 'app-hippodrome-list', + imports: [ + CommonModule, + DataTable, + Paginator, + SearchBar, + Modal, + HippodromeForm, + ZardBreadcrumbModule, + ZardCardComponent, + LucideAngularModule, + ], + templateUrl: './hippodrome.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Hippodrome { + rows = signal([]); + loading = signal(false); + total = signal(0); + uniqueCountries = signal(0); + uniqueCities = signal(0); + averageByCountry = signal(0); + totalReunions = signal(0); + totalCourses = signal(0); + + page = signal(1); + perPage = signal(10); + pageSize = [10, 20, 50]; + search = signal(''); + sort = signal({ key: 'nom', dir: 'asc' }); + + @ViewChild(HippodromeForm) formComp?: HippodromeForm; + + cols: TableColumn[] = [ + { key: 'nom', label: 'Nom', sortable: true }, + { key: 'ville', label: 'Ville', sortable: true }, + { key: 'pays', label: 'Pays', sortable: true }, + { + key: 'reunionCount', + label: 'Réunions', + sortable: true, + cell: (h) => (h.reunionCount ?? 0).toString(), + }, + { + key: 'courseCount', + label: 'Courses', + sortable: true, + cell: (h) => (h.courseCount ?? 0).toString(), + }, + { + key: 'capacite', + label: 'Capacité', + sortable: true, + cell: (h) => (h.capacite ? h.capacite.toLocaleString('fr-FR') : '—'), + }, + { + key: 'actif', + label: 'Statut', + sortable: true, + cell: (h) => + ` + + ${h.actif ? 'Actif' : 'Inactif'} + `, + }, + { + key: 'createdAt', + label: 'Créé le', + sortable: true, + cell: (h) => + new Date(h.createdAt).toLocaleDateString('fr-FR', { + day: '2-digit', + month: 'short', + year: 'numeric', + }), + }, + ]; + + // Modale + modalOpen = signal(false); + modalTitle = signal('Nouvel hippodrome'); + editingItem = signal | null>(null); // null => création + + constructor(private api: HippodromeService) { + effect(() => this.fetch()); + } + + private fetch() { + this.loading.set(true); + this.api + .list({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }) + .subscribe({ + next: (res) => { + this.rows.set(res.data); + + const meta = res.meta ?? {}; + + this.total.set(meta['total'] ?? 0); + this.uniqueCities.set(meta['uniqueCities'] ?? 0); + this.uniqueCountries.set(meta['uniqueCountries'] ?? 0); + this.averageByCountry.set(meta['averageByCountry'] ?? 0); + this.totalReunions.set(meta['totalReunions'] ?? 0); + this.totalCourses.set(meta['totalCourses'] ?? 0); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.uniqueCities.set(0); + this.uniqueCountries.set(0); + this.averageByCountry.set(0); + this.totalReunions.set(0); + this.totalCourses.set(0); + this.loading.set(false); + }, + }); + } + + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + } + onSort(s: SortState) { + this.sort.set(s); + this.page.set(1); + } + onPerPage(n: number) { + this.perPage.set(n); + this.page.set(1); + } + + openCreate() { + this.modalTitle.set('Nouvel hippodrome'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + + openEdit(ev: HippodromeType) { + this.modalTitle.set('Modifier l’hippodrome'); + this.editingItem.set(ev); + queueMicrotask(() => this.modalOpen.set(true)); + } + + closeModal() { + this.modalOpen.set(false); + } + + submitChildForm() { + // Déclenche le submit du formulaire enfant + this.formComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + + req$.subscribe(() => { + this.closeModal(); + // Reset editing item to null to clear the form + this.editingItem.set(null); + this.fetch(); + }); + } + + remove(ev: HippodromeType) { + if (!confirm(`Supprimer l’hippodrome « ${ev.nom} » ?`)) return; + this.api.delete(ev.id).subscribe(() => this.fetch()); + } +} diff --git a/src/app/dashboard/pages/limits/limits.html b/src/app/dashboard/pages/limits/limits.html new file mode 100644 index 0000000..ec942be --- /dev/null +++ b/src/app/dashboard/pages/limits/limits.html @@ -0,0 +1,63 @@ +
    +
    +

    Gestion des Limites

    + Nouvelle limite +
    + + +
    + Filtrer par statut: + + + +
    + + + + + +
    + + +
    +
    +
    + + +
    + + + +
    + Annuler + Enregistrer +
    +
    + + diff --git a/src/app/dashboard/pages/limits/limits.ts b/src/app/dashboard/pages/limits/limits.ts new file mode 100644 index 0000000..1a6b0f8 --- /dev/null +++ b/src/app/dashboard/pages/limits/limits.ts @@ -0,0 +1,294 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, ViewChild, effect, signal, untracked, OnInit } from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { SortDir } from '@shared/paging/paging'; +import { AgentLimit } from 'src/app/core/interfaces/agent-limit'; +import { AgentLimitService } from 'src/app/core/services/agent-limit'; +import { LimitForm } from '@shared/forms/limit-form/limit-form'; +import { Subject, of } from 'rxjs'; +import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; + +@Component({ + standalone: true, + selector: 'app-limits', + templateUrl: './limits.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, DataTable, Paginator, SearchBar, Modal, ZardButtonComponent, LimitForm], +}) +export class LimitsPage implements OnInit { + rows = signal([]); + total = signal(0); + loading = signal(false); + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'code', dir: 'asc' }); + selectedActif = signal(null); + + modalOpen = signal(false); + modalTitle = signal('Nouvelle limite'); + editingItem = signal(null); + + // Live search + private searchSubject = new Subject(); + + @ViewChild(LimitForm) formComp?: LimitForm; + + cols: TableColumn[] = [ + { key: 'code', label: 'Code', sortable: true }, + { key: 'configCode', label: 'Config', sortable: true }, + { key: 'nom', label: 'Nom', sortable: true }, + { + key: 'isDefault', + label: 'Défaut', + cell: (l) => + l.isDefault + ? ' Par défaut' + : '', + }, + { + key: 'actif', + label: 'Actif', + cell: (l) => + l.actif + ? ' Actif' + : ' Inactif', + }, + { + key: 'betMin', + label: 'Min Bet', + cell: (l) => (l.betMin ?? 0).toLocaleString('fr-FR'), + }, + { + key: 'betMax', + label: 'Max Bet', + cell: (l) => (l.betMax ?? 0).toLocaleString('fr-FR'), + }, + { + key: 'maxBet', + label: 'Max Bet (tx)', + cell: (l) => (l.maxBet ?? 0).toLocaleString('fr-FR'), + }, + { + key: 'maxDisburseBet', + label: 'Max Disburse', + cell: (l) => (l.maxDisburseBet ?? 0).toLocaleString('fr-FR'), + }, + { + key: 'airtimeMin', + label: 'Airtime Min', + cell: (l) => (l.airtimeMin ?? 0).toLocaleString('fr-FR'), + }, + { + key: 'airtimeMax', + label: 'Airtime Max', + cell: (l) => (l.airtimeMax ?? 0).toLocaleString('fr-FR'), + }, + ]; + + constructor(private api: AgentLimitService) { + effect(() => { + // Only trigger fetch when page, perPage, or sort changes (not search - handled by searchSubject) + const searchValue = this.search(); + const params = { + page: this.page(), + perPage: this.perPage(), + search: searchValue, + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }; + // Only fetch if search is empty (search is handled by searchSubject) + if (!searchValue.trim()) { + untracked(() => this.fetch(params)); + } + }); + + // Setup live search with debounce + this.searchSubject + .pipe( + debounceTime(300), + distinctUntilChanged(), + switchMap((query) => { + if (query.trim()) { + // Use search API which returns array + return this.api.search(query); + } else { + // If empty, use normal list + return this.api.list({ + page: this.page(), + perPage: this.perPage(), + search: '', + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }).pipe( + switchMap((res) => { + // Convert PagedResult to array for consistency + return of(res.data); + }) + ); + } + }) + ) + .subscribe({ + next: (res) => { + // Search API always returns array + if (Array.isArray(res)) { + this.rows.set(res); + this.total.set(res.length); + } + this.loading.set(false); + }, + error: (err) => { + console.error('Search error:', err); + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + + ngOnInit() { + // Initial fetch + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + + private fetch(params: { page: number; perPage: number; search: string; sortKey: string; sortDir: SortDir }) { + // Don't fetch if there's a search query - it's handled by searchSubject + const searchQuery = params.search.trim(); + if (searchQuery) { + return; // Search is handled by searchSubject subscription + } + + this.loading.set(true); + const actif = this.selectedActif(); + + if (actif !== null) { + // Filter by actif status - returns array + this.api.getByActif(actif).subscribe({ + next: (res: AgentLimit[]) => { + this.rows.set(res); + this.total.set(res.length); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } else { + // Normal list with pagination + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + } + + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + // Trigger search via subject for live search + if (q.trim()) { + this.loading.set(true); + this.searchSubject.next(q); + } else { + // If empty, fetch normally + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: '', + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + } + + onActifFilter(actif: boolean | null) { + this.selectedActif.set(actif); + this.page.set(1); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + openCreate() { this.modalTitle.set('Nouvelle limite'); this.editingItem.set(null); queueMicrotask(() => this.modalOpen.set(true)); } + openEdit(row: AgentLimit) { this.modalTitle.set('Modifier la limite'); this.editingItem.set(row); queueMicrotask(() => this.modalOpen.set(true)); } + closeModal() { this.modalOpen.set(false); } + submitChildForm() { this.formComp?.onSubmit(); } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const isSettingDefault = payload.isDefault === true; + const wasDefault = current?.isDefault; + + // If setting as default and it wasn't default before, show confirmation + if (isSettingDefault && !wasDefault) { + if (!confirm('Définir cette limite comme limite par défaut ?\n\nTous les agents recevront automatiquement cette limite, et l\'ancienne limite par défaut perdra son statut.')) { + return; + } + } + + const req$ = current?.id ? this.api.update(current.id, payload) : this.api.create(payload as Omit); + req$.subscribe({ + next: (result) => { + if (!result && current?.id) { + // Update failed + alert('Erreur lors de la sauvegarde de la limite'); + return; + } + this.closeModal(); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + if (isSettingDefault && !wasDefault) { + alert('La limite a été définie comme limite par défaut. Tous les agents ont été mis à jour.'); + } + }, + error: (err) => { + console.error('Error saving limit:', err); + alert('Erreur lors de la sauvegarde de la limite'); + }, + }); + } + + remove(row: AgentLimit) { + if (!confirm(`Supprimer la limite ${row.code} ?`)) return; + this.api.delete(row.id).subscribe(() => { + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + }); + } +} + + diff --git a/src/app/dashboard/pages/main/main.css b/src/app/dashboard/pages/main/main.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dashboard/pages/main/main.html b/src/app/dashboard/pages/main/main.html new file mode 100644 index 0000000..7c4dcd8 --- /dev/null +++ b/src/app/dashboard/pages/main/main.html @@ -0,0 +1,333 @@ +
    +

    Dashboard principale PJP

    + + + @if (statsLoading()) { +
    Chargement des statistiques…
    + } @if (statsError()) { +
    {{ statsError() }}
    + } + + +
    + +
    +
    +
    Courses en direct & à venir
    +
    + Statuts RUNNING & PROGRAMMEE, triés par heure de départ +
    +
    +
    + @if (liveCourses().length) { +
    + @for (c of liveCourses(); track c.id) { +
    +
    +
    +
    + + N° {{ c.numero }} + + + {{ c.nom }} + + @if (c.type) { + + {{ c.type }} + + } +
    +
    + + + + + {{ + c.dateDepartCourse + ? (c.dateDepartCourse | date : 'short' : undefined : 'fr-FR') + : '—' + }} + + + + {{ c.reunion.hippodrome.nom }} + + + Réunion {{ c.reunion.nom }} + + Distance {{ c.distance | number : '1.0-0' }} m +
    +
    + + + + + {{ c.partants }} partant{{ c.partants > 1 ? 's' : '' }} + + @if (c.nonPartants && c.nonPartants.length > 0) { + + + {{ c.nonPartants.length }} non-partant{{ c.nonPartants.length > 1 ? 's' : '' }} + + } @if (c.condition) { + + {{ c.condition }} + } @if (c.particularite) { + + ⭐ {{ c.particularite }} + } +
    +
    +
    + + {{ + (c.statut || '').toUpperCase() === 'RUNNING' + ? 'En cours' + : (c.statut || '').toUpperCase() === 'PROGRAMMEE' + ? 'Programmée' + : (c.statut || '').toUpperCase() === 'VALIDATED' + ? 'Validée' + : c.statut + }} + +
    +
    +
    + } +
    + } @else { +
    + Aucune course en cours ou à venir pour le moment. +
    + } +
    +
    + + +
    + +
    Utilisateurs
    +
    + {{ totalUsers() }} +
    +
    + + +
    Agents
    +
    + {{ totalAgents() }} +
    +
    + + +
    TPE
    +
    + {{ totalTpes() }} +
    +
    + + +
    Limites agents
    +
    + {{ totalAgentLimits() }} +
    +
    +
    + + +
    + +
    Hippodromes
    +
    + {{ totalHippodromes() }} +
    +
    + + +
    Réunions
    +
    + {{ totalReunions() }} +
    +
    + + +
    Courses
    +
    + {{ totalCourses() }} +
    +
    + + +
    Membres de famille d'agents
    +
    + {{ totalAgentFamilyMembers() }} +
    +
    +
    + + +
    + + +
    +
    Répartition TPE par statut
    +
    Total : {{ totalTpes() }}
    +
    +
    + +
    +
    +
    + {{ totalTpes() }} +
    +
    + +
    + @for (item of tpeStatusBreakdown(); track item.statut) { +
    + + + {{ item.statut }} ({{ item.count }}) — {{ item.percent }}% + +
    + } @if (!tpeStatusBreakdown().length) { +
    + Aucune donnée de statut TPE disponible. +
    + } +
    +
    +
    + + + +
    TPE assignés
    +
    +
    + {{ tpeAssignRate() }}% +
    +
    + ({{ tpeAssignedCount() }} / {{ totalTpes() }}) +
    +
    +
    +
    +
    +

    + Pourcentage de TPE actuellement affectés à un agent. +

    +
    + + + +
    +
    Activité par entité
    +
    +
    + + + + + + + + + + + + + @if (entityPolylinePoints()) { + + + + } + +
    +
    +
    +
    Users
    +
    {{ totalUsers() }}
    +
    +
    +
    Agents
    +
    {{ totalAgents() }}
    +
    +
    +
    TPE
    +
    {{ totalTpes() }}
    +
    +
    +
    Reunions
    +
    {{ totalReunions() }}
    +
    +
    +
    Courses
    +
    {{ totalCourses() }}
    +
    +
    +
    +
    +
    diff --git a/src/app/dashboard/pages/main/main.spec.ts b/src/app/dashboard/pages/main/main.spec.ts new file mode 100644 index 0000000..c005dc2 --- /dev/null +++ b/src/app/dashboard/pages/main/main.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Main } from './main'; + +describe('Main', () => { + let component: Main; + let fixture: ComponentFixture
    ; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Main] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Main); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/pages/main/main.ts b/src/app/dashboard/pages/main/main.ts new file mode 100644 index 0000000..ead5990 --- /dev/null +++ b/src/app/dashboard/pages/main/main.ts @@ -0,0 +1,257 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { UserService } from 'src/app/core/services/user'; +import { AgentService } from 'src/app/core/services/agent'; +import { TpeService } from 'src/app/core/services/tpe'; +import { AgentLimitService } from 'src/app/core/services/agent-limit'; +import { HippodromeService } from 'src/app/core/services/hippodrome'; +import { ReunionService } from 'src/app/core/services/reunion'; +import { CourseService } from 'src/app/core/services/course'; +import { RoleService } from 'src/app/core/services/role'; +import { AgentFamilyMemberService } from 'src/app/core/services/agent-family-member'; +import { ListParams, SortDir } from '@shared/paging/paging'; +import { forkJoin, of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import type { Course as CourseModel } from 'src/app/core/interfaces/course'; + +@Component({ + selector: 'app-main', + imports: [ZardCardComponent, CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './main.html', + styleUrl: './main.css', +}) +export class Main { + // Loading & error state + statsLoading = signal(false); + statsError = signal(null); + + // Global totals + totalUsers = signal(0); + totalAgents = signal(0); + totalTpes = signal(0); + totalAgentLimits = signal(0); + totalHippodromes = signal(0); + totalReunions = signal(0); + totalCourses = signal(0); + totalRoles = signal(0); + totalPermissions = signal(0); + totalAgentFamilyMembers = signal(0); + + // TPE status breakdown for charts + tpeStatusBreakdown = signal<{ statut: string; count: number; percent: number; color: string }[]>( + [] + ); + tpeAssignedCount = signal(0); + + // Derived value for TPE assignment rate + tpeAssignRate = computed(() => { + const total = this.totalTpes(); + const assigned = this.tpeAssignedCount(); + if (!total) return 0; + return Math.round((assigned / total) * 100); + }); + + // CSS conic-gradient for a TPE status pie/donut chart + tpePieGradient = computed(() => { + const segments = this.tpeStatusBreakdown(); + if (!segments.length) { + return 'conic-gradient(#e5e7eb 0deg 360deg)'; + } + let current = 0; + const parts: string[] = []; + for (const seg of segments) { + const start = current; + const sweep = (seg.percent || 0) * 3.6; // percent -> degrees + const end = start + sweep; + parts.push(`${seg.color} ${start}deg ${end}deg`); + current = end; + } + return `conic-gradient(${parts.join(', ')})`; + }); + + // Simple entity activity series for a line chart (users, agents, tpes, reunions, courses) + entityLabels: string[] = ['USERS', 'AGENTS', 'TPE', 'REUNIONS', 'COURSES']; + entitySeries = computed(() => [ + this.totalUsers(), + this.totalAgents(), + this.totalTpes(), + this.totalReunions(), + this.totalCourses(), + ]); + + // SVG polyline points for the entitySeries (normalized to 0–40 viewport) + entityPolylinePoints = computed(() => { + const values = this.entitySeries(); + const max = Math.max(...values, 1); + const n = values.length; + if (!n) return ''; + const stepX = n > 1 ? 100 / (n - 1) : 100; + return values + .map((v, i) => { + const x = i * stepX; + const norm = v / max; // 0–1 + const y = 40 - norm * 30; // keep some top/bottom padding + return `${x},${y}`; + }) + .join(' '); + }); + + // Live / upcoming courses (RUNNING or PROGRAMMEE, nearest in time) + liveCourses = signal([]); + + constructor( + private userService: UserService, + private agentService: AgentService, + private tpeService: TpeService, + private agentLimitService: AgentLimitService, + private hippodromeService: HippodromeService, + private reunionService: ReunionService, + private courseService: CourseService, + private roleService: RoleService, + private familyService: AgentFamilyMemberService + ) { + this.loadStats(); + } + + private baseParams(): ListParams { + return { + page: 1, + perPage: 1, + search: '', + sortKey: 'id', + sortDir: 'asc' as SortDir, + }; + } + + private loadStats() { + this.statsLoading.set(true); + this.statsError.set(null); + + const params = this.baseParams(); + // Fetch more courses to filter for live/upcoming ones + const coursesParams = { + ...params, + perPage: 100, // Fetch up to 100 courses to filter from + sortKey: 'dateDepartCourse', + sortDir: 'desc' as SortDir, + }; + + forkJoin({ + users: this.userService + .list(params) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + agents: this.agentService + .list(params) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + tpes: this.tpeService + .list(params) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + limits: this.agentLimitService + .list(params) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + hippodromes: this.hippodromeService + .list(params, true) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + reunions: this.reunionService + .list(params, true) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + courses: this.courseService + .list(coursesParams, true) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + roles: this.roleService + .list(params) + .pipe(catchError(() => of({ data: [], meta: { total: 0 } } as any))), + permissions: this.roleService.allPermissions().pipe(catchError(() => of([]))), + familyMembers: this.familyService.list().pipe(catchError(() => of([]))), + tpeByStatut: this.tpeService.getCountByStatut().pipe(catchError(() => of({}))), + tpeAssignes: this.tpeService.getAssignesStats().pipe(catchError(() => of(0))), + }).subscribe({ + next: (res) => { + this.totalUsers.set(res.users.meta?.total ?? 0); + this.totalAgents.set(res.agents.meta?.total ?? 0); + this.totalTpes.set(res.tpes.meta?.total ?? 0); + this.totalAgentLimits.set(res.limits.meta?.total ?? 0); + this.totalHippodromes.set(res.hippodromes.meta?.total ?? 0); + this.totalReunions.set(res.reunions.meta?.total ?? 0); + this.totalCourses.set(res.courses.meta?.total ?? 0); + this.totalRoles.set(res.roles.meta?.total ?? 0); + this.totalPermissions.set((res.permissions as any[]).length ?? 0); + this.totalAgentFamilyMembers.set((res.familyMembers as any[]).length ?? 0); + + // TPE status breakdown + const totalTpes = res.tpes.meta?.total ?? 0; + const statusColors: Record = { + VALIDE: '#16a34a', // green + DISPONIBLE: '#22c55e', + AFFECTE: '#3b82f6', // blue + EN_PANNE: '#f97316', // orange + EN_MAINTENANCE: '#eab308', // yellow + BLOQUE: '#ef4444', // red + INVALIDE: '#6b7280', // gray + HORS_SERVICE: '#4b5563', + VOLE: '#7c3aed', + }; + const rawStats = res.tpeByStatut || {}; + const entries = Object.entries(rawStats as Record).filter( + ([, count]) => count > 0 + ); + const totalFromStats = + entries.reduce((sum, [, count]) => sum + (count || 0), 0) || totalTpes || 1; + const breakdown = entries.map(([statut, count]) => { + const upper = statut.toUpperCase(); + const color = statusColors[upper] || '#6b7280'; + const percent = Math.round(((count || 0) / totalFromStats) * 100); + return { statut: upper, count: count || 0, percent, color }; + }); + this.tpeStatusBreakdown.set(breakdown); + this.tpeAssignedCount.set(res.tpeAssignes ?? 0); + + // Live / upcoming courses: filter by statut & date + const allCourses = (res.courses.data as CourseModel[]) ?? []; + const now = new Date(); + const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000); + const oneDayAhead = new Date(now.getTime() + 24 * 60 * 60 * 1000); + + const live = allCourses + .filter((c) => { + const statut = String(c.statut || '').toUpperCase(); + + // Include RUNNING courses + if (statut === 'RUNNING') return true; + + // Include PROGRAMMEE courses that are scheduled within the next 24 hours + if (statut === 'PROGRAMMEE') { + const d = c.dateDepartCourse ? new Date(c.dateDepartCourse) : null; + if (!d) return false; + // Include if departure is in the past hour (just started) or within next 24 hours + return d >= oneHourAgo && d <= oneDayAhead; + } + + // Also include VALIDATED courses that are about to start (within next 24 hours) + if (statut === 'VALIDATED') { + const d = c.dateDepartCourse ? new Date(c.dateDepartCourse) : null; + if (!d) return false; + return d >= now && d <= oneDayAhead; + } + + return false; + }) + .sort((a, b) => { + const da = a.dateDepartCourse ? new Date(a.dateDepartCourse).getTime() : 0; + const db = b.dateDepartCourse ? new Date(b.dateDepartCourse).getTime() : 0; + return da - db; // Sort by departure time ascending (earliest first) + }) + .slice(0, 6); + this.liveCourses.set(live); + + this.statsLoading.set(false); + }, + error: () => { + this.statsError.set('Erreur lors du chargement des statistiques du dashboard.'); + this.statsLoading.set(false); + }, + }); + } +} diff --git a/src/app/dashboard/pages/profile/profile.html b/src/app/dashboard/pages/profile/profile.html new file mode 100644 index 0000000..fe50a4d --- /dev/null +++ b/src/app/dashboard/pages/profile/profile.html @@ -0,0 +1,121 @@ +
    +
    +
    +

    Mon profil

    +

    Gérez vos informations et la sécurité du compte

    +
    + +
    + +
    + + +
    +
    Informations du compte
    +
    Nom, prénom et identifiant
    +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    +
    +
    + Enregistrer +
    +
    + + + +
    +
    Sécurité
    +
    Changez votre mot de passe
    +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    +
    +
    + Mettre à jour +
    +
    +
    +
    diff --git a/src/app/dashboard/pages/profile/profile.ts b/src/app/dashboard/pages/profile/profile.ts new file mode 100644 index 0000000..70d6ce9 --- /dev/null +++ b/src/app/dashboard/pages/profile/profile.ts @@ -0,0 +1,75 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; +import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardAvatarComponent } from '@shared/components/avatar/avatar.component'; + +@Component({ + standalone: true, + selector: 'app-profile', + templateUrl: './profile.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + ReactiveFormsModule, + ZardCardComponent, + ZardFormModule, + ZardInputDirective, + ZardButtonComponent, + ZardAvatarComponent, + ], +}) +export class ProfilePage { + profileForm; + passwordForm; + savingProfile = signal(false); + savingPassword = signal(false); + submittedProfile = false; + submittedPassword = false; + + avatar = { fallback: 'PM', url: '/assets/images/avatar.svg', alt: 'Profil' }; + + constructor(private fb: FormBuilder) { + this.profileForm = this.fb.group({ + nom: ['', Validators.required], + prenom: ['', Validators.required], + identifiant: ['', Validators.required], + }); + + this.passwordForm = this.fb.group({ + current: ['', Validators.required], + next: ['', [Validators.required, Validators.minLength(6)]], + confirm: ['', [Validators.required, Validators.minLength(6)]], + }); + } + + saveProfile() { + this.submittedProfile = true; + if (this.profileForm.invalid) { + this.profileForm.markAllAsTouched(); + return; + } + this.savingProfile.set(true); + setTimeout(() => this.savingProfile.set(false), 600); + } + + changePassword() { + this.submittedPassword = true; + if (this.passwordForm.invalid) { + this.passwordForm.markAllAsTouched(); + return; + } + const { next, confirm } = this.passwordForm.getRawValue(); + if (next !== confirm) { + // simple inline mismatch handling; in real app, set an error + return; + } + this.savingPassword.set(true); + setTimeout(() => this.savingPassword.set(false), 600); + } +} + + diff --git a/src/app/dashboard/pages/report-courses/report-detail.html b/src/app/dashboard/pages/report-courses/report-detail.html new file mode 100644 index 0000000..52ef8e4 --- /dev/null +++ b/src/app/dashboard/pages/report-courses/report-detail.html @@ -0,0 +1,114 @@ +@if(detail()){ +
    +
    +

    + Rapport pour la course n° {{ detail()!.summary.course.numero }} + ({{ detail()!.summary.statut }}) +

    +
    + Retour + {{ editMode() ? 'Quitter édition' : 'Modifier' }} + Valider + Confirmer + Réinitialiser +
    +
    + + +
    +
    + Date: + {{ detail()!.summary.course.dateDepartCourse | date : 'dd/MM/yyyy' }} +
    +
    Nom: {{ detail()!.summary.course.nom }}
    +
    Type: {{ detail()!.summary.course.type }}
    +
    + Lieu: {{ detail()!.summary.course.reunion.hippodrome.nom }} +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + +
    Type de gainType de jeuMontantNombreStatutDistribuéExterne
    {{ r.typeGain }}{{ r.typeJeu }} + @if(editMode() && !detail()!.summary.confirmed){ + + } @else { + {{ r.montant | number : '1.0-0' : 'fr-FR' }} + } + + @if(editMode() && !detail()!.summary.confirmed){ + + } @else { + {{ r.nombre | number : '1.0-0' : 'fr-FR' }} + } + {{ r.statut }} + @if(editMode() && !detail()!.summary.confirmed){ + + } @else { + + } + + @if(editMode() && !detail()!.summary.confirmed){ + + } @else { + + } +
    +
    +
    +} diff --git a/src/app/dashboard/pages/report-courses/report-detail.ts b/src/app/dashboard/pages/report-courses/report-detail.ts new file mode 100644 index 0000000..f003edc --- /dev/null +++ b/src/app/dashboard/pages/report-courses/report-detail.ts @@ -0,0 +1,132 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { ReportService } from 'src/app/core/services/report'; +import { CourseReportDetail, CourseReportDetailRow } from 'src/app/core/interfaces/report'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; + +@Component({ + standalone: true, + selector: 'app-report-courses-detail', + templateUrl: './report-detail.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, RouterModule, ZardCardComponent, ZardButtonComponent], +}) +export class ReportCoursesDetailPage { + detail = signal(undefined); + editMode = signal(false); + editedRows = signal([]); + private originalRows = signal([]); + + constructor(private route: ActivatedRoute, private api: ReportService) { + const id = this.route.snapshot.params['id']; + this.api.getDetail(id).subscribe((d) => { + this.detail.set(d); + this.editedRows.set(d?.rows ?? []); + this.originalRows.set(d?.rows ? d.rows.map((r) => ({ ...r })) : []); + }); + } + + onValidate() { + const id = this.detail()?.summary.id; + if (!id) return; + // Persist edited rows then validate + this.api.modifyRows(id, this.editedRows()).subscribe(() => { + this.api.validate(id).subscribe((s) => { + if (this.detail()) this.detail.set({ summary: s!, rows: this.editedRows() }); + // Commit current edits as the new baseline + this.originalRows.set(this.editedRows().map((r) => ({ ...r }))); + this.editMode.set(false); + }); + }); + } + onConfirm() { + const id = this.detail()?.summary.id; + if (!id) return; + this.api.confirm(id).subscribe((s) => { + if (this.detail()) this.detail.set({ summary: s!, rows: this.editedRows() }); + // Confirm also commits the current edits as baseline + this.originalRows.set(this.editedRows().map((r) => ({ ...r }))); + this.editMode.set(false); + }); + } + onReset() { + const id = this.detail()?.summary.id; + if (!id) return; + this.api.resetStatus(id).subscribe((s) => { + if (this.detail()) this.detail.set({ summary: s!, rows: this.detail()!.rows }); + // Reset discards uncommitted edits + this.editedRows.set(this.originalRows().map((r) => ({ ...r }))); + this.editMode.set(false); + }); + } + + onEditToggle() { + if (this.detail()?.summary.confirmed) return; + const currentlyEditing = this.editMode(); + if (currentlyEditing) { + // Leaving edit mode without validation: revert to original snapshot + this.editedRows.set(this.originalRows().map((r) => ({ ...r }))); + this.editMode.set(false); + } else { + this.editMode.set(true); + } + } + + onChangeMontant(index: number, value: any) { + const v = Number(value); + this.editedRows.update((rows: CourseReportDetailRow[]) => { + const current = rows[index]; + if (!current) return rows; + current.montant = Number.isFinite(v) ? v : current.montant; + return rows; + }); + } + + onChangeNombre(index: number, value: any) { + const v = Number(value); + this.editedRows.update((rows: CourseReportDetailRow[]) => { + const current = rows[index]; + if (!current) return rows; + current.nombre = Number.isFinite(v) ? v : current.nombre; + return rows; + }); + } + + onToggleDistributed(index: number, value: any) { + const checked = !!value; + this.editedRows.update((rows: CourseReportDetailRow[]) => { + const current = rows[index]; + if (!current) return rows; + current.distributed = checked; + return rows; + }); + } + + onToggleExterne(index: number, value: any) { + const checked = !!value; + this.editedRows.update((rows: CourseReportDetailRow[]) => { + const current = rows[index]; + if (!current) return rows; + current.externe = checked; + return rows; + }); + } + + trackByRow(index: number, row: CourseReportDetailRow) { + return row.typeGain + '|' + row.typeJeu + '|' + index; + } + + isRowDirty(index: number): boolean { + const current = this.editedRows()[index]; + const original = this.originalRows()[index]; + if (!current || !original) return false; + return ( + current.montant !== original.montant || + current.nombre !== original.nombre || + !!current.distributed !== !!original.distributed || + !!current.externe !== !!original.externe + ); + } +} diff --git a/src/app/dashboard/pages/report-courses/report-list.html b/src/app/dashboard/pages/report-courses/report-list.html new file mode 100644 index 0000000..3d32fe9 --- /dev/null +++ b/src/app/dashboard/pages/report-courses/report-list.html @@ -0,0 +1,19 @@ +
    +
    +

    Rapport des courses

    +
    + + + + + +
    + +
    +
    +
    + + +
    + + diff --git a/src/app/dashboard/pages/report-courses/report-list.ts b/src/app/dashboard/pages/report-courses/report-list.ts new file mode 100644 index 0000000..7e1d1e3 --- /dev/null +++ b/src/app/dashboard/pages/report-courses/report-list.ts @@ -0,0 +1,61 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, signal, effect, untracked } from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { SortDir } from '@shared/paging/paging'; +import { Router } from '@angular/router'; +import { CourseReportSummary } from 'src/app/core/interfaces/report'; +import { ReportService } from 'src/app/core/services/report'; + +@Component({ + standalone: true, + selector: 'app-report-courses-list', + templateUrl: './report-list.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, DataTable, Paginator, SearchBar, ZardButtonComponent], +}) +export class ReportCoursesListPage { + rows = signal([]); + total = signal(0); + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'date', dir: 'desc' }); + loading = signal(false); + + cols: TableColumn[] = [ + { key: 'course.dateDepartCourse', label: 'Date', sortable: true }, + { key: 'course.numero', label: 'Numéro', sortable: true }, + { key: 'course.nom', label: 'Nom', sortable: true }, + { key: 'course.type', label: 'Type', sortable: true }, + { key: 'course.reunion.hippodrome.nom', label: 'Lieu', sortable: true }, + { key: 'course.particularite', label: 'Particularité' }, + { key: 'statut', label: 'Statut', sortable: true }, + ]; + + constructor(private api: ReportService, private router: Router) { + effect(() => { + const params = { page: this.page(), perPage: this.perPage(), search: this.search(), sortKey: this.sort().key, sortDir: this.sort().dir as SortDir }; + untracked(() => this.fetch(params)); + }); + } + + fetch(params: { page: number; perPage: number; search: string; sortKey: string; sortDir: SortDir }) { + this.loading.set(true); + this.api.list(params).subscribe((res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.loading.set(false); + }); + } + + onSearch(q: string) { this.search.set(q); this.page.set(1); } + + open(row: CourseReportSummary) { + this.router.navigate(['/rapport-courses', row.id]); + } +} + + diff --git a/src/app/dashboard/pages/reunion/reunion.css b/src/app/dashboard/pages/reunion/reunion.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/dashboard/pages/reunion/reunion.html b/src/app/dashboard/pages/reunion/reunion.html new file mode 100644 index 0000000..0cd01af --- /dev/null +++ b/src/app/dashboard/pages/reunion/reunion.html @@ -0,0 +1,87 @@ +
    +
    +

    Réunions

    + +
    + +
    + +
    Total des réunions
    +
    + {{ total() }} +
    +
    + + +
    Réunions à venir
    +
    + {{ upcomingReunions() }} +
    +
    + + +
    Réunions passées
    +
    + {{ pastReunions() }} +
    +
    + + +
    Hippodromes concernés
    +
    + {{ uniqueHippodromes() }} +
    +
    +
    + + + +
    + + +
    + + +
    +
    +
    + + +
    + + + + +
    + Annuler + Enregistrer +
    +
    +
    diff --git a/src/app/dashboard/pages/reunion/reunion.spec.ts b/src/app/dashboard/pages/reunion/reunion.spec.ts new file mode 100644 index 0000000..6aed4ad --- /dev/null +++ b/src/app/dashboard/pages/reunion/reunion.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Reunion } from './reunion'; + +describe('Reunion', () => { + let component: Reunion; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Reunion] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Reunion); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/pages/reunion/reunion.ts b/src/app/dashboard/pages/reunion/reunion.ts new file mode 100644 index 0000000..bc7a3c9 --- /dev/null +++ b/src/app/dashboard/pages/reunion/reunion.ts @@ -0,0 +1,234 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + effect, + signal, + ViewChild, + untracked, +} from '@angular/core'; +import { Reunion as ReunionType } from 'src/app/core/interfaces/reunion'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ReunionForm } from '@shared/forms/reunion-form/reunion-form'; +import { ReunionService } from 'src/app/core/services/reunion'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { SortDir } from '@shared/paging/paging'; +import { LucideAngularModule } from 'lucide-angular'; + +@Component({ + standalone: true, + selector: 'app-reunion-list', + imports: [ + CommonModule, + DataTable, + Paginator, + SearchBar, + Modal, + ReunionForm, + ZardButtonComponent, + ZardCardComponent, + LucideAngularModule, + ], + templateUrl: './reunion.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ReunionList { + // Core reactive state + rows = signal([]); + loading = signal(false); + total = signal(0); + upcomingReunions = signal(0); + pastReunions = signal(0); + uniqueHippodromes = signal(0); + + // pagination, sorting, search + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'date', dir: 'asc' }); + pageSize = [10, 20, 50]; + + // modal management + modalOpen = signal(false); + modalTitle = signal('Nouvelle réunion'); + editingItem = signal | null>(null); + + @ViewChild(ReunionForm) formComp?: ReunionForm; + + cols: TableColumn[] = [ + { key: 'code', label: 'Code', sortable: true }, + { key: 'nom', label: 'Nom', sortable: true }, + { + key: 'date', + label: 'Date', + sortable: true, + cell: (r) => + new Date(r.date).toLocaleDateString('fr-FR', { + day: '2-digit', + month: 'short', + year: 'numeric', + }), + }, + { key: 'numero', label: 'Numéro', sortable: true }, + { + key: 'hippodrome.nom', + label: 'Hippodrome', + cell: (r) => + r.hippodrome ? `${r.hippodrome.nom} (${r.hippodrome.ville}, ${r.hippodrome.pays})` : '—', + }, + { + key: 'statut', + label: 'Statut', + cell: (r) => { + const colorMap: Record = { + PLANIFIEE: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', + EN_COURS: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', + TERMINEE: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300', + ANNULEE: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300', + }; + const labelMap: Record = { + PLANIFIEE: 'Planifiée', + EN_COURS: 'En cours', + TERMINEE: 'Terminée', + ANNULEE: 'Annulée', + }; + return `${ + labelMap[r.statut] + }`; + }, + }, + { + key: 'totalCourses', + label: 'Courses', + cell: (r) => (r.totalCourses ? r.totalCourses.toString() : '—'), + }, + { + key: 'createdAt', + label: 'Créée le', + cell: (r) => + new Date(r.createdAt).toLocaleDateString('fr-FR', { + day: '2-digit', + month: 'short', + year: 'numeric', + }), + }, + { + key: 'updatedAt', + label: 'Modifiée le', + cell: (r) => + new Date(r.updatedAt).toLocaleDateString('fr-FR', { + day: '2-digit', + month: 'short', + year: 'numeric', + }), + }, + ]; + + constructor(private api: ReunionService) { + // Effect will run only when one of these dependencies change + effect(() => { + const params = { + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }; + untracked(() => this.fetch(params)); // avoids recursive dependency + }); + } + + private fetch(params: { + page: number; + perPage: number; + search: string; + sortKey: string; + sortDir: SortDir; + }) { + this.loading.set(true); + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + const meta = res.meta; + + this.upcomingReunions.set(res.meta['upcomingReunions'] ?? 0); + this.pastReunions.set(res.meta['pastReunions'] ?? 0); + this.uniqueHippodromes.set(res.meta['uniqueHippodromes'] ?? 0); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.upcomingReunions.set(0); + this.pastReunions.set(0); + this.uniqueHippodromes.set(0); + this.loading.set(false); + }, + }); + } + + // === UI interactions === + onSearch(q: string) { + this.search.set(q); + this.page.set(1); // reset pagination on new search + } + + openCreate() { + this.modalTitle.set('Nouvelle réunion'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + + openEdit(row: ReunionType) { + this.modalTitle.set('Modifier la réunion'); + this.editingItem.set(row); + queueMicrotask(() => this.modalOpen.set(true)); + } + + closeModal() { + this.modalOpen.set(false); + } + + submitChildForm() { + this.formComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + req$.subscribe(() => { + this.closeModal(); + // Reset editing item to null to clear the form + this.editingItem.set(null); + // refetch current page + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }); + }); + } + + remove(row: ReunionType) { + if (!confirm(`Supprimer la réunion « ${row.nom} » ?`)) return; + this.api.delete(row.id).subscribe(() => + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir, + }) + ); + } +} diff --git a/src/app/dashboard/pages/roles/roles.html b/src/app/dashboard/pages/roles/roles.html new file mode 100644 index 0000000..52343d0 --- /dev/null +++ b/src/app/dashboard/pages/roles/roles.html @@ -0,0 +1,111 @@ +
    +
    +
    +

    Rôles & Permissions

    +

    Gérez les rôles et assignez les permissions

    +
    + Nouveau rôle +
    + + + +
    + + +
    + + +
    +
    +
    +
    + + +
    +
    +
    +

    Permissions

    +

    Gérez la liste des permissions disponibles

    +
    + Nouvelle permission +
    + +
    + + + + + + + + + + @for (p of permissions(); track p.id) { + + + + + + } @if (!permissions().length) { + + + + } + +
    CodeDescriptionActions
    {{ p.name }}{{ p.description || '—' }} +
    + + +
    +
    + Aucune permission définie. +
    +
    +
    + + + @if (modalOpen()) { + + } +
    + Annuler + Enregistrer +
    +
    + + + + @if (permissionModalOpen()) { + + } +
    + Annuler + Enregistrer +
    +
    +
    diff --git a/src/app/dashboard/pages/roles/roles.ts b/src/app/dashboard/pages/roles/roles.ts new file mode 100644 index 0000000..e1487c4 --- /dev/null +++ b/src/app/dashboard/pages/roles/roles.ts @@ -0,0 +1,241 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ViewChild, + effect, + signal, + untracked, +} from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { Permission, Role } from 'src/app/core/interfaces/role'; +import { RoleService } from 'src/app/core/services/role'; +import { SortDir } from '@shared/paging/paging'; +import { RoleForm } from '@shared/forms/role-form/role-form'; +import { PermissionForm } from '@shared/forms/permission-form/permission-form'; +import { toast } from 'ngx-sonner'; + +@Component({ + standalone: true, + selector: 'app-roles', + templateUrl: './roles.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + DataTable, + SearchBar, + Modal, + ZardButtonComponent, + RoleForm, + PermissionForm, + ], +}) +export class RolesPage { + rows = signal([]); + total = signal(0); + loading = signal(false); + permissions = signal([]); + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'name', dir: 'asc' }); + + modalOpen = signal(false); + modalTitle = signal('Nouveau rôle'); + editingItem = signal(null); + + permissionModalOpen = signal(false); + permissionModalTitle = signal('Nouvelle permission'); + editingPermission = signal(null); + + @ViewChild(RoleForm) formComp?: RoleForm; + @ViewChild(PermissionForm) permFormComp?: PermissionForm; + + cols: TableColumn[] = [ + { key: 'name', label: 'Rôle', sortable: true }, + { key: 'description', label: 'Description', sortable: true }, + { key: 'permissions', label: 'Permissions', cell: (r) => r.permissions.length }, + ]; + + constructor(public api: RoleService) { + this.api.allPermissions().subscribe((list) => this.permissions.set(list)); + effect(() => { + const params = { + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }; + untracked(() => this.fetch(params)); + }); + } + + private fetch(params: { + page: number; + perPage: number; + search: string; + sortKey: string; + sortDir: SortDir; + }) { + this.loading.set(true); + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + } + openCreate() { + this.modalTitle.set('Nouveau rôle'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + openEdit(row: Role) { + this.modalTitle.set('Modifier le rôle'); + this.editingItem.set(row); + queueMicrotask(() => this.modalOpen.set(true)); + } + closeModal() { + this.modalOpen.set(false); + } + submitChildForm() { + this.formComp?.onSubmit(); + } + + submitPermissionForm() { + this.permFormComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + req$.subscribe({ + next: (role) => { + this.closeModal(); + toast.success( + current?.id + ? `Le rôle « ${role?.name ?? ''} » a été mis à jour avec succès` + : `Le rôle « ${role?.name ?? ''} » a été créé avec succès` + ); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + }, + error: () => { + toast.error( + current?.id + ? 'Erreur lors de la mise à jour du rôle' + : 'Erreur lors de la création du rôle', + { duration: 5000 } + ); + }, + }); + } + + remove(row: Role) { + if (!confirm(`Supprimer le rôle « ${row.name} » ?`)) return; + this.api.delete(row.id).subscribe((result) => { + if (result.success) { + toast.success(`Le rôle « ${row.name} » a été supprimé avec succès`); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } else { + toast.error(result.error || 'Erreur lors de la suppression du rôle', { + duration: 5000, + }); + } + }); + } + + // ------- Permissions CRUD ------- + + openCreatePermission() { + this.permissionModalTitle.set('Nouvelle permission'); + this.editingPermission.set(null); + this.permissionModalOpen.set(true); + } + + openEditPermission(p: Permission) { + this.permissionModalTitle.set('Modifier la permission'); + this.editingPermission.set(p); + this.permissionModalOpen.set(true); + } + + closePermissionModal() { + this.permissionModalOpen.set(false); + this.editingPermission.set(null); + } + + onPermissionFormSave(payload: Permission) { + const current = this.editingPermission(); + const isEdit = !!current?.id; + const req$ = isEdit + ? this.api.updatePermission(current.id, { + name: payload.name, + description: payload.description, + }) + : this.api.createPermission({ name: payload.name, description: payload.description }); + + req$.subscribe({ + next: (perm) => { + this.closePermissionModal(); + const permName = perm?.name || payload.name; + toast.success( + isEdit + ? `La permission « ${permName} » a été mise à jour avec succès` + : `La permission « ${permName} » a été créée avec succès` + ); + this.api.allPermissions().subscribe((list) => this.permissions.set(list)); + }, + error: (err) => { + const permName = payload.name; + toast.error( + isEdit + ? `Erreur lors de la mise à jour de la permission « ${permName} »` + : `Erreur lors de la création de la permission « ${permName} »`, + { duration: 5000 } + ); + }, + }); + } + + removePermission(p: Permission) { + if (!confirm(`Supprimer la permission « ${p.name} » ?`)) return; + this.api.deletePermission(p.id).subscribe((result) => { + if (result.success) { + toast.success(`La permission « ${p.name} » a été supprimée avec succès`); + this.api.allPermissions().subscribe((list) => this.permissions.set(list)); + } else { + toast.error(result.error || 'Erreur lors de la suppression de la permission', { + duration: 5000, + }); + } + }); + } +} diff --git a/src/app/dashboard/pages/tpe/tpe.html b/src/app/dashboard/pages/tpe/tpe.html new file mode 100644 index 0000000..314e04e --- /dev/null +++ b/src/app/dashboard/pages/tpe/tpe.html @@ -0,0 +1,194 @@ +
    +
    +

    TPES (Terminal Point de Vente)

    + Nouvel équipement +
    + + + @if (statsLoading()) { +
    + @for (i of [1, 2, 3, 4]; track i) { +
    +
    +
    +
    + } +
    + } @else { +
    +
    +
    Total TPEs
    +
    {{ assignmentStats().total }}
    +
    +
    +
    Assignés
    +
    {{ assignmentStats().assignes }}
    +
    +
    +
    Disponibles
    +
    {{ assignmentStats().disponibles }}
    +
    +
    +
    Valides
    +
    {{ statsByStatut()['VALIDE'] || 0 }}
    +
    +
    + } + + +
    + Filtrer par statut: + + @for (statut of allStatuses; track statut) { + + } +
    + + + + + +
    + @if (row.assigne) { + + } @else { + + } +
    + +
    + @for (statut of allStatuses; track statut) { + + } +
    +
    + +
    + + +
    +
    +
    + + +
    + + + +
    + Annuler + Enregistrer +
    +
    + + + +
    + @if (agentsLoading()) { +
    Chargement des agents...
    + } @else if (agents().length === 0) { +
    Aucun agent actif disponible
    + } @else { + + +
    + + @for (agent of agents(); track agent.id) { + + {{ agent.code }} - {{ agent.nom }} {{ agent.prenom }} + + } + +
    +
    + } +
    +
    + Annuler + +
    +
    diff --git a/src/app/dashboard/pages/tpe/tpe.ts b/src/app/dashboard/pages/tpe/tpe.ts new file mode 100644 index 0000000..cf80d36 --- /dev/null +++ b/src/app/dashboard/pages/tpe/tpe.ts @@ -0,0 +1,487 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ViewChild, + effect, + signal, + untracked, + OnInit, +} from '@angular/core'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardMenuModule } from '@shared/components/menu/menu.module'; +import { ZardTooltipModule } from '@shared/components/tooltip/tooltip'; +import { SortDir } from '@shared/paging/paging'; +import { TpeDevice, TpeStatus } from 'src/app/core/interfaces/tpe'; +import { TpeService } from 'src/app/core/services/tpe'; +import { TpeForm } from '@shared/forms/tpe-form/tpe-form'; +import { Agent } from 'src/app/core/interfaces/agent'; +import { AgentService } from 'src/app/core/services/agent'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { forkJoin, Subject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators'; + +@Component({ + standalone: true, + selector: 'app-tpe-list', + templateUrl: './tpe.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + DataTable, + Paginator, + SearchBar, + Modal, + ZardButtonComponent, + TpeForm, + ZardMenuModule, + ZardTooltipModule, + ZardSelectComponent, + ZardSelectItemComponent, + ZardFormModule, + ], +}) +export class TpePage implements OnInit { + rows = signal([]); + total = signal(0); + loading = signal(false); + + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'imei', dir: 'asc' }); + selectedStatut = signal(null); + + modalOpen = signal(false); + modalTitle = signal('Nouvel équipement'); + editingItem = signal(null); + + // Agent assignment modal + assignModalOpen = signal(false); + assigningTpe = signal(null); + agents = signal([]); + selectedAgentId = signal(''); + agentsLoading = signal(false); + + // Stats + statsByStatut = signal>({}); + assignmentStats = signal({ total: 0, assignes: 0, disponibles: 0 }); + statsLoading = signal(false); + + // Live search + private searchSubject = new Subject(); + + @ViewChild(TpeForm) formComp?: TpeForm; + + formatStatut(statut: string): string { + const statutMap: Record = { + VALIDE: 'Valide', + INVALIDE: 'Invalide', + EN_PANNE: 'En panne', + BLOQUE: 'Bloqué', + DISPONIBLE: 'Disponible', + AFFECTE: 'Affecté', + EN_MAINTENANCE: 'En maintenance', + HORS_SERVICE: 'Hors service', + VOLE: 'Volé', + }; + return statutMap[statut] || statut; + } + + cols: TableColumn[] = [ + { key: 'imei', label: 'IMEI', sortable: true }, + { key: 'serial', label: 'N° de Série', sortable: true }, + { key: 'type', label: 'Type', sortable: true }, + { key: 'marque', label: 'Marque', sortable: true }, + { key: 'modele', label: 'Modèle', sortable: true }, + { key: 'statut', label: 'Statut', sortable: true, cell: (d) => this.formatStatut(d.statut) }, + { + key: 'assigne', + label: 'Assigné à', + cell: (d) => { + if (!d.assigne || !d.agent) { + return 'Non assigné'; + } + const agent = d.agent; + const code = agent.code + ? `${agent.code}` + : ''; + const name = + agent.nom && agent.prenom + ? `
    ${agent.nom} ${agent.prenom}
    ` + : agent.nom || agent.prenom + ? `
    ${agent.nom || agent.prenom}
    ` + : ''; + const phone = agent.phone + ? `
    ${agent.phone}
    ` + : ''; + const zone = agent.zone + ? `
    Zone: ${agent.zone}
    ` + : ''; + + const parts = [code, name, phone, zone].filter(Boolean); + if (parts.length === 0) { + return 'Agent assigné'; + } + return `
    ${parts.join('')}
    `; + }, + }, + ]; + + allStatuses: TpeStatus[] = [ + 'VALIDE', + 'INVALIDE', + 'EN_PANNE', + 'BLOQUE', + 'DISPONIBLE', + 'AFFECTE', + 'EN_MAINTENANCE', + 'HORS_SERVICE', + 'VOLE', + ]; + + constructor(private api: TpeService, private agentService: AgentService) { + effect(() => { + // Only trigger fetch when page, perPage, or sort changes (not search - handled by searchSubject) + const searchValue = this.search(); + const params = { + page: this.page(), + perPage: this.perPage(), + search: searchValue, + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }; + // Only fetch if search is empty (search is handled by searchSubject) + if (!searchValue.trim()) { + untracked(() => this.fetch(params)); + } + }); + + // Setup live search with debounce + this.searchSubject + .pipe( + debounceTime(300), + distinctUntilChanged(), + switchMap((query) => { + if (query.trim()) { + return this.api.search(query); + } else { + // If empty, use normal list + return this.api.list({ + page: this.page(), + perPage: this.perPage(), + search: '', + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + }) + ) + .subscribe({ + next: (res) => { + if (Array.isArray(res)) { + // Search API returns array + this.rows.set(res); + this.total.set(res.length); + } else { + // List returns PagedResult + this.rows.set(res.data); + this.total.set(res.meta.total); + } + this.loading.set(false); + }, + error: (err) => { + console.error('Search error:', err); + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + + ngOnInit() { + this.loadStats(); + // Initial fetch if no search query + if (!this.search().trim()) { + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: '', + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + } + + loadStats() { + this.statsLoading.set(true); + forkJoin({ + byStatut: this.api.getCountByStatut(), + assignes: this.api.getAssignesStats(), + }).subscribe({ + next: ({ byStatut, assignes }) => { + this.statsByStatut.set(byStatut || {}); + // Calculate total from statsByStatut + const total = Object.values(byStatut || {}).reduce((sum, count) => sum + count, 0); + // Calculate disponibles (total - assignes) + const disponibles = Math.max(0, total - (assignes || 0)); + this.assignmentStats.set({ + total: total, + assignes: assignes || 0, + disponibles: disponibles, + }); + this.statsLoading.set(false); + }, + error: (err) => { + console.error('Error loading stats:', err); + this.statsByStatut.set({}); + this.assignmentStats.set({ total: 0, assignes: 0, disponibles: 0 }); + this.statsLoading.set(false); + }, + }); + } + + private fetch(params: { + page: number; + perPage: number; + search: string; + sortKey: string; + sortDir: SortDir; + }) { + // Don't fetch if there's a search query - it's handled by searchSubject + const searchQuery = params.search.trim(); + if (searchQuery) { + return; // Search is handled by searchSubject subscription + } + + this.loading.set(true); + const statut = this.selectedStatut(); + + if (statut) { + // Filter by statut - returns array + this.api.getByStatut(statut).subscribe({ + next: (res: TpeDevice[]) => { + this.rows.set(res); + this.total.set(res.length); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } else { + // Normal list with pagination + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + } + + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + // Trigger search via subject for live search + if (q.trim()) { + this.loading.set(true); + this.searchSubject.next(q); + } else { + // If empty, fetch normally + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: '', + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + } + + onStatutFilter(statut: TpeStatus | null) { + this.selectedStatut.set(statut); + this.page.set(1); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } + + onUpdateStatut(row: TpeDevice, newStatut: TpeStatus) { + if (!confirm(`Changer le statut de ${row.imei} vers ${this.formatStatut(newStatut)} ?`)) return; + this.api.updateStatut(row.id, newStatut).subscribe({ + next: () => { + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + this.loadStats(); + }, + }); + } + + onLiberer(row: TpeDevice) { + if (!confirm(`Libérer le TPE ${row.imei} ?`)) return; + this.api.liberer(row.id).subscribe({ + next: () => { + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + this.loadStats(); + }, + }); + } + + onAssigner(row: TpeDevice) { + this.assigningTpe.set(row); + this.selectedAgentId.set(''); + this.loadAgents(); + this.assignModalOpen.set(true); + } + + loadAgents() { + this.agentsLoading.set(true); + // Load active agents only + this.agentService.getByStatut('ACTIF').subscribe({ + next: (agents) => { + this.agents.set(agents); + this.agentsLoading.set(false); + }, + error: () => { + this.agents.set([]); + this.agentsLoading.set(false); + }, + }); + } + + confirmAssign() { + const tpe = this.assigningTpe(); + const agentId = this.selectedAgentId(); + if (!tpe || !agentId) { + alert('Veuillez sélectionner un agent'); + return; + } + this.api.assigner(tpe.id, agentId).subscribe({ + next: () => { + this.assignModalOpen.set(false); + this.assigningTpe.set(null); + this.selectedAgentId.set(''); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + this.loadStats(); + }, + error: () => { + alert("Erreur lors de l'assignation du TPE"); + }, + }); + } + + closeAssignModal() { + this.assignModalOpen.set(false); + this.assigningTpe.set(null); + this.selectedAgentId.set(''); + } + openCreate() { + this.modalTitle.set('Nouvel équipement'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + openEdit(row: TpeDevice) { + this.modalTitle.set("Modifier l'équipement"); + this.editingItem.set(row); + queueMicrotask(() => this.modalOpen.set(true)); + } + closeModal() { + this.modalOpen.set(false); + setTimeout(() => { + this.editingItem.set(null); + }, 0); + } + + submitChildForm() { + this.formComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const isCreating = !current?.id; + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + req$.subscribe({ + next: (result) => { + // For update, check if result is valid (update can return undefined on error) + if (current?.id && !result) { + console.error('Update failed - result is undefined'); + // Don't close modal, let user retry + return; + } + // Success - close modal first + this.modalOpen.set(false); + // Then reset form and clear editing item after a short delay + setTimeout(() => { + this.editingItem.set(null); + this.formComp?.resetForm(); + }, 100); + // Refresh data + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + this.loadStats(); + }, + error: (err) => { + console.error('Error saving TPE:', err); + // Don't close modal on error, let user fix and retry + // Form stays filled so user can correct and resubmit + }, + }); + } + + remove(row: TpeDevice) { + if (!confirm(`Supprimer l\'équipement IMEI ${row.imei} ?`)) return; + this.api.delete(row.id).subscribe(() => { + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + this.loadStats(); + }); + } +} diff --git a/src/app/dashboard/pages/users/users.html b/src/app/dashboard/pages/users/users.html new file mode 100644 index 0000000..35d248c --- /dev/null +++ b/src/app/dashboard/pages/users/users.html @@ -0,0 +1,41 @@ +
    +
    +

    + La liste des utilisateurs +

    + Nouvel utilisateur +
    + + + + + +
    + + +
    +
    +
    + + +
    + + + +
    + Annuler + Enregistrer +
    +
    diff --git a/src/app/dashboard/pages/users/users.ts b/src/app/dashboard/pages/users/users.ts new file mode 100644 index 0000000..25f6250 --- /dev/null +++ b/src/app/dashboard/pages/users/users.ts @@ -0,0 +1,198 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ViewChild, + signal, + effect, + untracked, +} from '@angular/core'; +import { DataTable, TableColumn, SortState } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { SortDir } from '@shared/paging/paging'; +import { User } from 'src/app/core/interfaces/user'; +import { UserService } from 'src/app/core/services/user'; +import { Role } from 'src/app/core/interfaces/role'; +import { RoleService } from 'src/app/core/services/role'; +import { UserForm } from '@shared/forms/user-form/user-form'; +import { toast } from 'ngx-sonner'; + +@Component({ + standalone: true, + selector: 'app-users', + templateUrl: './users.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, DataTable, Paginator, SearchBar, Modal, ZardButtonComponent, UserForm], +}) +export class UsersPage { + rows = signal([]); + total = signal(0); + loading = signal(false); + roleMap = new Map(); + + page = signal(1); + perPage = signal(10); + search = signal(''); + sort = signal({ key: 'nom', dir: 'asc' }); + + modalOpen = signal(false); + modalTitle = signal('Nouvel utilisateur'); + editingItem = signal(null); + + @ViewChild(UserForm) formComp?: UserForm; + + cols: TableColumn[] = [ + { key: 'nom', label: 'Nom', sortable: true }, + { key: 'prenom', label: 'Prénom', sortable: true }, + { key: 'identifiant', label: 'Identifiant', sortable: true }, + { key: 'matriculeAgent', label: 'Matricule', sortable: true }, + { + key: 'role.name', + label: 'Rôle', + sortable: true, + cell: (u) => this.roleMap.get(u.roleId) ?? u.role?.name ?? '—', + }, + { key: 'statut', label: 'Statut', sortable: true }, + { + key: 'derniereConnexion', + label: 'Dernière connexion', + cell: (u) => + u.derniereConnexion ? new Date(u.derniereConnexion).toLocaleDateString('fr-FR') : '—', + }, + { + key: 'restrictionConnexion', + label: 'Restr. Conn.', + cell: (u) => String(u.restrictionConnexion), + }, + { + key: 'restrictionAutomatique', + label: 'Restr. Auto', + cell: (u) => String(u.restrictionAutomatique), + }, + { key: 'nombreIpAutorise', label: 'IP Autorisé' }, + { key: 'nombreIpAutoAutorise', label: 'IP Auto' }, + ]; + + constructor(private api: UserService, private roleService: RoleService) { + this.roleService + .list({ page: 1, perPage: 100, search: '', sortKey: 'name', sortDir: 'asc' } as any) + .subscribe((res) => (res.data as Role[]).forEach((r) => this.roleMap.set(r.id, r.name))); + effect(() => { + const params = { + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }; + untracked(() => this.fetch(params)); + }); + } + + private fetch(params: { + page: number; + perPage: number; + search: string; + sortKey: string; + sortDir: SortDir; + }) { + this.loading.set(true); + this.api.list(params).subscribe({ + next: (res) => { + this.rows.set(res.data); + this.total.set(res.meta.total); + this.loading.set(false); + }, + error: () => { + this.rows.set([]); + this.total.set(0); + this.loading.set(false); + }, + }); + } + + onSearch(q: string) { + this.search.set(q); + this.page.set(1); + } + + openCreate() { + this.modalTitle.set('Nouvel utilisateur'); + this.editingItem.set(null); + queueMicrotask(() => this.modalOpen.set(true)); + } + + openEdit(row: User) { + this.modalTitle.set("Modifier l'utilisateur"); + this.editingItem.set(row); + queueMicrotask(() => this.modalOpen.set(true)); + } + + closeModal() { + this.modalOpen.set(false); + } + + submitChildForm() { + this.formComp?.onSubmit(); + } + + onFormSave(payload: Partial) { + const current = this.editingItem(); + const req$ = current?.id + ? this.api.update(current.id, payload) + : this.api.create(payload as Omit); + req$.subscribe({ + next: (user) => { + this.closeModal(); + toast.success( + current?.id + ? `L'utilisateur « ${user?.nom ?? ''} ${ + user?.prenom ?? '' + } » a été mis à jour avec succès` + : `L'utilisateur « ${user?.nom ?? ''} ${user?.prenom ?? ''} » a été créé avec succès` + ); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + }, + error: () => { + toast.error( + current?.id + ? "Erreur lors de la mise à jour de l'utilisateur" + : "Erreur lors de la création de l'utilisateur", + { duration: 5000 } + ); + }, + }); + } + + remove(row: User) { + if (!confirm(`Supprimer l\'utilisateur « ${row.nom} ${row.prenom} » ?`)) return; + this.api.delete(row.id).subscribe({ + next: (ok) => { + if (ok) { + toast.success(`L'utilisateur « ${row.nom} ${row.prenom} » a été supprimé avec succès`); + this.fetch({ + page: this.page(), + perPage: this.perPage(), + search: this.search(), + sortKey: this.sort().key, + sortDir: this.sort().dir as SortDir, + }); + } else { + toast.error("Erreur lors de la suppression de l'utilisateur", { duration: 5000 }); + } + }, + error: () => { + toast.error("Erreur lors de la suppression de l'utilisateur", { duration: 5000 }); + }, + }); + } +} diff --git a/src/app/shared/components/accordion/accordion-item.component.ts b/src/app/shared/components/accordion/accordion-item.component.ts new file mode 100644 index 0000000..28edb4b --- /dev/null +++ b/src/app/shared/components/accordion/accordion-item.component.ts @@ -0,0 +1,76 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, inject, input, signal, ViewEncapsulation } from '@angular/core'; + +import { ZardAccordionComponent } from './accordion.component'; + +import type { ClassValue } from 'clsx'; + +@Component({ + selector: 'z-accordion-item', + exportAs: 'zAccordionItem', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + + +
    +
    +
    + +
    +
    +
    +
    + `, +}) +export class ZardAccordionItemComponent { + private cdr = inject(ChangeDetectorRef); + + readonly zTitle = input(''); + readonly zValue = input(''); + readonly class = input(''); + + private isOpenSignal = signal(false); + + accordion?: ZardAccordionComponent; + + isOpen = computed(() => this.isOpenSignal()); + + setOpen(open: boolean): void { + this.isOpenSignal.set(open); + this.cdr.markForCheck(); + } + + toggle(): void { + if (this.accordion) { + this.accordion.toggleItem(this); + } else { + this.setOpen(!this.isOpen()); + } + } +} diff --git a/src/app/shared/components/accordion/accordion.component.ts b/src/app/shared/components/accordion/accordion.component.ts new file mode 100644 index 0000000..c046bbb --- /dev/null +++ b/src/app/shared/components/accordion/accordion.component.ts @@ -0,0 +1,85 @@ +import { AfterContentInit, ChangeDetectionStrategy, Component, contentChildren, input, ViewEncapsulation } from '@angular/core'; + +import { ZardAccordionItemComponent } from './accordion-item.component'; + +import type { ClassValue } from 'clsx'; + +@Component({ + selector: 'z-accordion', + exportAs: 'zAccordion', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class ZardAccordionComponent implements AfterContentInit { + readonly items = contentChildren(ZardAccordionItemComponent); + + readonly class = input(''); + readonly zType = input<'single' | 'multiple'>('single'); + readonly zCollapsible = input(true); + readonly zDefaultValue = input(''); + + ngAfterContentInit(): void { + setTimeout(() => { + this.items().forEach(item => { + item.accordion = this; + }); + + const defaultValue = this.zDefaultValue(); + if (defaultValue) { + if (typeof defaultValue === 'string') { + const item = this.items().find(i => i.zValue() === defaultValue); + if (item) { + item.setOpen(true); + } + } else if (Array.isArray(defaultValue)) { + defaultValue.forEach(value => { + const item = this.items().find(i => i.zValue() === value); + if (item) { + item.setOpen(true); + } + }); + } + } + }); + } + + toggleItem(selectedItem: ZardAccordionItemComponent): void { + const isClosing = selectedItem.isOpen(); + + if (this.zType() === 'single') { + if (isClosing && !this.zCollapsible()) { + return; + } + + this.items().forEach(item => { + const shouldBeOpen = item === selectedItem ? !item.isOpen() : false; + item.setOpen(shouldBeOpen); + }); + } else { + if (isClosing && !this.zCollapsible()) { + const openItemsCount = this.countOpenItems(); + if (openItemsCount <= 1) { + return; + } + } + + selectedItem.setOpen(!selectedItem.isOpen()); + } + } + + private countOpenItems(): number { + let count = 0; + this.items().forEach(item => { + if (item.isOpen()) { + count++; + } + }); + return count; + } +} diff --git a/src/app/shared/components/alert-dialog/alert-dialog-ref.ts b/src/app/shared/components/alert-dialog/alert-dialog-ref.ts new file mode 100644 index 0000000..7ebbf0d --- /dev/null +++ b/src/app/shared/components/alert-dialog/alert-dialog-ref.ts @@ -0,0 +1,104 @@ +import { filter, Observable, Subject, takeUntil } from 'rxjs'; + +import { OverlayRef } from '@angular/cdk/overlay'; + +import { OnClickCallback, ZardAlertDialogComponent, ZardAlertDialogOptions } from './alert-dialog.component'; + +export class ZardAlertDialogRef { + componentInstance?: T; + private destroy$ = new Subject(); + private isClosing = false; + private readonly afterClosedSubject: Subject = new Subject(); + + constructor( + private overlayRef: OverlayRef, + private config: ZardAlertDialogOptions, + private containerInstance: ZardAlertDialogComponent, + ) { + containerInstance.cancelTriggered.subscribe(() => { + this.handleCancel(); + }); + + containerInstance.okTriggered.subscribe(() => { + this.handleOk(); + }); + + this.handleMaskClick(); + this.handleEscapeKey(); + } + + close(dialogResult?: R): void { + if (this.isClosing) { + return; + } + + this.isClosing = true; + this.containerInstance.state.set('close'); + + setTimeout(() => { + if (this.overlayRef) { + this.overlayRef.dispose(); + } + + this.afterClosedSubject.next(dialogResult); + this.afterClosedSubject.complete(); + + if (!this.destroy$.closed) { + this.destroy$.next(); + this.destroy$.complete(); + } + }, 150); + } + + afterClosed(): Observable { + return this.afterClosedSubject.asObservable(); + } + + private handleCancel() { + const cancelFn = this.config.zOnCancel; + + if (typeof cancelFn === 'function') { + const result = (cancelFn as OnClickCallback)(this.componentInstance as T); + if (result !== false) { + this.close(result as R); + } + } else { + this.close(); + } + } + + private handleOk() { + const okFn = this.config.zOnOk; + + if (typeof okFn === 'function') { + const result = (okFn as OnClickCallback)(this.componentInstance as T); + if (result !== false) { + this.close(result as R); + } + } else { + this.close(); + } + } + + private handleMaskClick() { + const hasMaskClosable = this.config.zMaskClosable ?? true; + if (hasMaskClosable) { + this.overlayRef + .outsidePointerEvents() + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.close(); + }); + } + } + + private handleEscapeKey() { + this.overlayRef + .keydownEvents() + .pipe( + filter(event => event.key === 'Escape'), + takeUntil(this.destroy$), + ) + .subscribe(() => this.close()); + } +} diff --git a/src/app/shared/components/alert-dialog/alert-dialog.component.html b/src/app/shared/components/alert-dialog/alert-dialog.component.html new file mode 100644 index 0000000..ae0670c --- /dev/null +++ b/src/app/shared/components/alert-dialog/alert-dialog.component.html @@ -0,0 +1,35 @@ +
    + @if (config.zTitle || config.zDescription) { +
    + @if (config.zTitle) { +

    {{ config.zTitle }}

    + } + + @if (config.zDescription) { +

    {{ config.zDescription }}

    + } +
    + } + +
    + + + @if (isStringContent) { +
    + } +
    + +
    + @if (config.zCancelText !== null) { + + } + + @if (config.zOkText !== null) { + + } +
    +
    diff --git a/src/app/shared/components/alert-dialog/alert-dialog.component.ts b/src/app/shared/components/alert-dialog/alert-dialog.component.ts new file mode 100644 index 0000000..eb40351 --- /dev/null +++ b/src/app/shared/components/alert-dialog/alert-dialog.component.ts @@ -0,0 +1,154 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { A11yModule } from '@angular/cdk/a11y'; +import { OverlayModule } from '@angular/cdk/overlay'; +import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, PortalModule, TemplatePortal } from '@angular/cdk/portal'; +import { + ChangeDetectionStrategy, + Component, + ComponentRef, + computed, + ElementRef, + EmbeddedViewRef, + EventEmitter, + inject, + NgModule, + output, + signal, + TemplateRef, + Type, + viewChild, + ViewContainerRef, + ViewEncapsulation, +} from '@angular/core'; +import { ClassValue } from 'clsx'; + +import { alertDialogVariants, ZardAlertDialogVariants } from './alert-dialog.variants'; +import { ZardButtonComponent } from '../button/button.component'; +import { ZardAlertDialogService } from './alert-dialog.service'; +import { ZardAlertDialogRef } from './alert-dialog-ref'; +import { generateId, mergeClasses } from '@shared/utils/merge-classes'; + +const noopFun = () => void 0; +export type OnClickCallback = (instance: T) => false | void | object; + +export class ZardAlertDialogOptions { + zCancelText?: string | null; + zClosable?: boolean; + zContent?: string | TemplateRef | Type; + zCustomClasses?: ClassValue; + zData?: object; + zDescription?: string; + zIcon?: string; + zMaskClosable?: boolean; + zOkDestructive?: boolean; + zOkDisabled?: boolean; + zOkText?: string | null; + zOnCancel?: EventEmitter | OnClickCallback = noopFun; + zOnOk?: EventEmitter | OnClickCallback = noopFun; + zTitle?: string | TemplateRef; + zType?: ZardAlertDialogVariants['zType']; + zViewContainerRef?: ViewContainerRef; + zWidth?: string; +} + +@Component({ + selector: 'z-alert-dialog', + exportAs: 'zAlertDialog', + standalone: true, + imports: [OverlayModule, PortalModule, ZardButtonComponent, A11yModule], + templateUrl: './alert-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + host: { + '[class]': 'classes()', + '[@alertDialogAnimation]': 'state()', + '[style.width]': 'config.zWidth ? config.zWidth : null', + role: 'alertdialog', + '[attr.aria-modal]': 'true', + '[attr.aria-labelledby]': 'titleId()', + '[attr.aria-describedby]': 'descriptionId()', + }, + styles: [ + ` + z-alert-dialog { + inset: 0; + margin: auto; + width: fit-content; + height: fit-content; + transform-origin: center center; + } + `, + ], + animations: [ + trigger('alertDialogAnimation', [ + state('close', style({ opacity: 0, transform: 'scale(0.9)' })), + state('open', style({ opacity: 1, transform: 'scale(1)' })), + transition('close => open', animate('150ms ease-out')), + transition('open => close', animate('150ms ease-in')), + ]), + ], +}) +export class ZardAlertDialogComponent extends BasePortalOutlet { + private readonly host = inject(ElementRef); + protected readonly config = inject(ZardAlertDialogOptions); + + protected readonly classes = computed(() => + mergeClasses( + alertDialogVariants({ + zType: this.config.zType, + }), + this.config.zCustomClasses, + ), + ); + + private alertDialogId = generateId('alert-dialog'); + protected readonly titleId = computed(() => (this.config.zTitle ? `${this.alertDialogId}-title` : null)); + protected readonly descriptionId = computed(() => (this.config.zDescription ? `${this.alertDialogId}-description` : null)); + + public alertDialogRef?: ZardAlertDialogRef; + + protected readonly isStringContent = typeof this.config.zContent === 'string'; + + readonly portalOutlet = viewChild.required(CdkPortalOutlet); + + okTriggered = output(); + cancelTriggered = output(); + state = signal<'close' | 'open'>('close'); + + constructor() { + super(); + } + + getNativeElement(): HTMLElement { + return this.host.nativeElement; + } + + attachComponentPortal(portal: ComponentPortal): ComponentRef { + if (this.portalOutlet()?.hasAttached()) { + throw Error('Attempting to attach alert dialog content after content is already attached'); + } + return this.portalOutlet()?.attachComponentPortal(portal); + } + + attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { + if (this.portalOutlet()?.hasAttached()) { + throw Error('Attempting to attach alert dialog content after content is already attached'); + } + + return this.portalOutlet()?.attachTemplatePortal(portal); + } + + onOkClick() { + this.okTriggered.emit(); + } + + onCancelClick() { + this.cancelTriggered.emit(); + } +} + +@NgModule({ + imports: [ZardButtonComponent, ZardAlertDialogComponent, OverlayModule, PortalModule, A11yModule], + providers: [ZardAlertDialogService], +}) +export class ZardAlertDialogModule {} diff --git a/src/app/shared/components/alert-dialog/alert-dialog.service.ts b/src/app/shared/components/alert-dialog/alert-dialog.service.ts new file mode 100644 index 0000000..0868d39 --- /dev/null +++ b/src/app/shared/components/alert-dialog/alert-dialog.service.ts @@ -0,0 +1,144 @@ +import { ComponentType, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal'; +import { inject, Injectable, InjectionToken, Injector, PLATFORM_ID, TemplateRef } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; + +import { ZardAlertDialogRef } from './alert-dialog-ref'; +import { ZardAlertDialogComponent, ZardAlertDialogOptions } from './alert-dialog.component'; + +type ContentType = ComponentType | TemplateRef | string | undefined; +export const Z_ALERT_MODAL_DATA = new InjectionToken('Z_ALERT_MODAL_DATA'); + +@Injectable({ + providedIn: 'root', +}) +export class ZardAlertDialogService { + private overlay = inject(Overlay); + private injector = inject(Injector); + private platformId = inject(PLATFORM_ID); + + create(config: ZardAlertDialogOptions): ZardAlertDialogRef { + return this.open(config.zContent, config); + } + + confirm( + config: Omit, 'zOkText' | 'zCancelText'> & { + zOkText?: string; + zCancelText?: string; + }, + ): ZardAlertDialogRef { + const confirmConfig: ZardAlertDialogOptions = { + ...config, + zOkText: config.zOkText || 'Confirm', + zCancelText: config.zCancelText || 'Cancel', + zOkDestructive: config.zOkDestructive ?? false, + zIcon: config.zIcon, + zType: config.zType || 'default', + }; + return this.create(confirmConfig); + } + + warning(config: Omit, 'zOkText'> & { zOkText?: string }): ZardAlertDialogRef { + const warningConfig: ZardAlertDialogOptions = { + ...config, + zOkText: config.zOkText || 'OK', + zCancelText: null, + zIcon: config.zIcon || 'alert-triangle', + zType: config.zType || 'warning', + }; + return this.create(warningConfig); + } + + info(config: Omit, 'zOkText'> & { zOkText?: string }): ZardAlertDialogRef { + const infoConfig: ZardAlertDialogOptions = { + ...config, + zOkText: config.zOkText || 'OK', + zCancelText: null, + zIcon: config.zIcon || 'info', + zType: config.zType || 'default', + }; + return this.create(infoConfig); + } + + private open(componentOrTemplateRef: ContentType, config: ZardAlertDialogOptions) { + const overlayRef = this.createOverlay(); + + if (!overlayRef) { + // Return a mock alert dialog ref for SSR environments + return new ZardAlertDialogRef(undefined as any, config, undefined as any); + } + + const alertDialogContainer = this.attachAlertDialogContainer(overlayRef, config); + + const alertDialogRef = this.attachAlertDialogContent(componentOrTemplateRef, alertDialogContainer, overlayRef, config); + alertDialogContainer.alertDialogRef = alertDialogRef; + + return alertDialogRef; + } + + private createOverlay(): OverlayRef | undefined { + if (isPlatformBrowser(this.platformId)) { + const overlayConfig = new OverlayConfig({ + hasBackdrop: true, + backdropClass: 'cdk-overlay-dark-backdrop', + positionStrategy: this.overlay.position().global(), + }); + + return this.overlay.create(overlayConfig); + } + return undefined; + } + + private attachAlertDialogContainer(overlayRef: OverlayRef, config: ZardAlertDialogOptions) { + const injector = Injector.create({ + parent: this.injector, + providers: [ + { provide: OverlayRef, useValue: overlayRef }, + { provide: ZardAlertDialogOptions, useValue: config }, + ], + }); + + const containerPortal = new ComponentPortal>(ZardAlertDialogComponent, config.zViewContainerRef, injector); + const containerRef = overlayRef.attach>(containerPortal); + + setTimeout(() => { + containerRef.instance.state.set('open'); + }, 0); + + return containerRef.instance; + } + + private attachAlertDialogContent( + componentOrTemplateRef: ContentType, + alertDialogContainer: ZardAlertDialogComponent, + overlayRef: OverlayRef, + config: ZardAlertDialogOptions, + ) { + const alertDialogRef = new ZardAlertDialogRef(overlayRef, config, alertDialogContainer); + + if (componentOrTemplateRef instanceof TemplateRef) { + alertDialogContainer.attachTemplatePortal( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + new TemplatePortal(componentOrTemplateRef, null!, { + alertDialogRef: alertDialogRef, + } as any), + ); + } else if (componentOrTemplateRef && typeof componentOrTemplateRef !== 'string') { + const injector = this.createInjector(alertDialogRef, config); + const contentRef = alertDialogContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, config.zViewContainerRef, injector)); + alertDialogRef.componentInstance = contentRef.instance; + } + + return alertDialogRef; + } + + private createInjector(alertDialogRef: ZardAlertDialogRef, config: ZardAlertDialogOptions) { + return Injector.create({ + parent: this.injector, + providers: [ + { provide: ZardAlertDialogRef, useValue: alertDialogRef }, + { provide: Z_ALERT_MODAL_DATA, useValue: config.zData }, + ], + }); + } +} diff --git a/src/app/shared/components/alert-dialog/alert-dialog.variants.ts b/src/app/shared/components/alert-dialog/alert-dialog.variants.ts new file mode 100644 index 0000000..a6e85a1 --- /dev/null +++ b/src/app/shared/components/alert-dialog/alert-dialog.variants.ts @@ -0,0 +1,16 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const alertDialogVariants = cva('fixed z-50 w-full max-w-[calc(100%-2rem)] border bg-background shadow-lg rounded-lg sm:max-w-lg', { + variants: { + zType: { + default: '', + destructive: 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + warning: 'border-warning/50 text-warning dark:border-warning [&>svg]:text-warning', + }, + }, + defaultVariants: { + zType: 'default', + }, +}); + +export type ZardAlertDialogVariants = VariantProps; diff --git a/src/app/shared/components/alert/alert.component.ts b/src/app/shared/components/alert/alert.component.ts new file mode 100644 index 0000000..1fc378e --- /dev/null +++ b/src/app/shared/components/alert/alert.component.ts @@ -0,0 +1,50 @@ +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; +import type { ClassValue } from 'clsx'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { alertVariants, ZardAlertVariants } from './alert.variants'; + +@Component({ + selector: 'z-alert', + standalone: true, + exportAs: 'zAlert', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (iconName()) { + + } + +
    +
    {{ zTitle() }}
    + {{ zDescription() }} +
    + `, + host: { + '[class]': 'classes()', + '[attr.data-type]': 'zType()', + '[attr.data-appearance]': 'zAppearance()', + }, +}) +export class ZardAlertComponent { + readonly class = input(''); + readonly zTitle = input.required(); + readonly zDescription = input.required(); + readonly zIcon = input(); + readonly zType = input('default'); + readonly zAppearance = input('outline'); + + protected readonly classes = computed(() => mergeClasses(alertVariants({ zType: this.zType(), zAppearance: this.zAppearance() }), this.class())); + + protected readonly iconsType: Record, string> = { + default: '', + info: 'icon-info', + success: 'icon-circle-check', + warning: 'icon-triangle-alert', + error: 'icon-circle-x', + }; + + protected readonly iconName = computed(() => { + return this.zIcon() ?? this.iconsType[this.zType() ?? 'default']; + }); +} diff --git a/src/app/shared/components/alert/alert.variants.ts b/src/app/shared/components/alert/alert.variants.ts new file mode 100644 index 0000000..50edb96 --- /dev/null +++ b/src/app/shared/components/alert/alert.variants.ts @@ -0,0 +1,23 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const alertVariants = cva('relative flex gap-2 w-full rounded-lg p-4', { + variants: { + zType: { + default: 'dark:data-[appearance="soft"]:text-zinc-800 data-[appearance="fill"]:text-white', + info: 'text-blue-500 data-[appearance="fill"]:text-white', + success: 'text-green-600 data-[appearance="fill"]:text-white', + warning: 'text-yellow-600 data-[appearance="fill"]:text-white', + error: 'text-red-500 data-[appearance="fill"]:text-white', + }, + zAppearance: { + outline: 'border data-[type="info"]:border-blue-500 data-[type="success"]:border-green-600 data-[type="warning"]:border-yellow-600 data-[type="error"]:border-red-500', + soft: 'bg-zinc-100 data-[type="info"]:bg-blue-50 data-[type="success"]:bg-green-50 data-[type="warning"]:bg-yellow-50 data-[type="error"]:bg-red-50', + fill: 'bg-zinc-500 data-[type="info"]:bg-blue-500 data-[type="success"]:bg-green-600 data-[type="warning"]:bg-yellow-600 data-[type="error"]:bg-red-500', + }, + }, + defaultVariants: { + zType: 'default', + zAppearance: 'outline', + }, +}); +export type ZardAlertVariants = VariantProps; diff --git a/src/app/shared/components/avatar/avatar.component.ts b/src/app/shared/components/avatar/avatar.component.ts new file mode 100644 index 0000000..04a61c9 --- /dev/null +++ b/src/app/shared/components/avatar/avatar.component.ts @@ -0,0 +1,127 @@ +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { avatarVariants, imageVariants, ZardAvatarImage, ZardAvatarVariants } from './avatar.variants'; + +@Component({ + selector: 'z-avatar', + exportAs: 'zAvatar', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (zLoading()) { + + } @else { + @if (zImage()?.fallback) { + {{ zImage()?.fallback }} + } + @if (zImage()?.url) { + + } + } + @if (zStatus()) { + @switch (zStatus()) { + @case ('online') { + + + + } + @case ('offline') { + + + + } + @case ('doNotDisturb') { + + + + + } + @case ('away') { + + + + } + @case ('invisible') { + + + + } + } + } + `, + host: { + '[class]': 'containerClasses()', + }, +}) +export class ZardAvatarComponent { + readonly zType = input('default'); + readonly zSize = input('default'); + readonly zShape = input('default'); + readonly zStatus = input(null); + readonly zBorder = input(false, { transform }); + readonly zLoading = input(false, { transform }); + readonly zImage = input({ fallback: 'ZA' }); + + readonly class = input(''); + + protected readonly containerClasses = computed(() => + mergeClasses(avatarVariants({ zType: this.zType(), zSize: this.zSize(), zShape: this.zShape(), zBorder: this.zBorder() }), this.class()), + ); + protected readonly imgClasses = computed(() => mergeClasses(imageVariants({ zShape: this.zShape() }))); +} diff --git a/src/app/shared/components/avatar/avatar.variants.ts b/src/app/shared/components/avatar/avatar.variants.ts new file mode 100644 index 0000000..9b0279b --- /dev/null +++ b/src/app/shared/components/avatar/avatar.variants.ts @@ -0,0 +1,65 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const avatarVariants = cva('relative flex flex-row items-center justify-center box-content hover:bg-primary/90 cursor-default', { + variants: { + zType: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: 'border border-input hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground shadow-sm shadow-black', + }, + zSize: { + default: 'w-12 h-12', + sm: 'w-10 h-10', + md: 'w-18 h-18', + lg: 'w-37 h-37', + full: 'w-full h-full', + }, + zShape: { + default: 'rounded-md', + circle: 'rounded-full', + square: 'rounded-none', + }, + zStatus: { + online: 'online', + offline: 'offline', + doNotDisturb: 'doNotDisturb', + away: 'away', + invisible: 'invisible', + }, + zBorder: { + true: 'border border-3 border-white', + }, + zLoading: { + true: 'opacity-100', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + zShape: 'default', + }, +}); + +export const imageVariants = cva('relative object-cover object-center w-full h-full z-10', { + variants: { + zShape: { + default: 'rounded-md', + circle: 'rounded-full', + square: 'rounded-none', + }, + }, + defaultVariants: { + zShape: 'default', + }, +}); + +export type ZardAvatarImage = { + zImage: { + fallback: string; + url?: string; + alt?: string; + }; +}; +export type ZardAvatarVariants = VariantProps & ZardAvatarImage; diff --git a/src/app/shared/components/badge/badge.component.ts b/src/app/shared/components/badge/badge.component.ts new file mode 100644 index 0000000..1ad0e2a --- /dev/null +++ b/src/app/shared/components/badge/badge.component.ts @@ -0,0 +1,26 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { badgeVariants, ZardBadgeVariants } from './badge.variants'; + +@Component({ + selector: 'z-badge', + exportAs: 'zBadge', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardBadgeComponent { + readonly zType = input('default'); + readonly zShape = input('default'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(badgeVariants({ zType: this.zType(), zShape: this.zShape() }), this.class())); +} diff --git a/src/app/shared/components/badge/badge.variants.ts b/src/app/shared/components/badge/badge.variants.ts new file mode 100644 index 0000000..5dc3c36 --- /dev/null +++ b/src/app/shared/components/badge/badge.variants.ts @@ -0,0 +1,24 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const badgeVariants = cva( + 'inline-flex items-center border text-xs px-2.5 py-0.5 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', + { + variants: { + zType: { + default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', + secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', + outline: 'text-foreground', + }, + zShape: { + default: 'rounded-full', + square: 'rounded-none', + }, + }, + defaultVariants: { + zType: 'default', + zShape: 'default', + }, + }, +); +export type ZardBadgeVariants = VariantProps; diff --git a/src/app/shared/components/breadcrumb/breadcrumb.component.ts b/src/app/shared/components/breadcrumb/breadcrumb.component.ts new file mode 100644 index 0000000..6506035 --- /dev/null +++ b/src/app/shared/components/breadcrumb/breadcrumb.component.ts @@ -0,0 +1,159 @@ +import { ChangeDetectionStrategy, Component, computed, input, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { RouterLink } from '@angular/router'; +import { ClassValue } from 'clsx'; + +import { + breadcrumbVariants, + breadcrumbListVariants, + breadcrumbSeparatorVariants, + breadcrumbItemVariants, + breadcrumbLinkVariants, + breadcrumbEllipsisVariants, + breadcrumbPageVariants, + ZardBreadcrumbVariants, + ZardBreadcrumbListVariants, + ZardBreadcrumbItemVariants, + ZardBreadcrumbLinkVariants, + ZardBreadcrumbPageVariants, + ZardBreadcrumbSeparatorVariants, + ZardBreadcrumbEllipsisVariants, +} from './breadcrumb.variants'; +import { mergeClasses } from '@shared/utils/merge-classes'; + +@Component({ + selector: 'z-breadcrumb', + exportAs: 'zBreadcrumb', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class ZardBreadcrumbComponent { + readonly zSize = input('md'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(breadcrumbVariants({ zSize: this.zSize() }), this.class())); +} + +@Component({ + selector: 'z-breadcrumb-list', + exportAs: 'zBreadcrumbList', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
      + +
    + `, +}) +export class ZardBreadcrumbListComponent { + readonly zAlign = input('start'); + readonly zWrap = input('wrap'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(breadcrumbListVariants({ zAlign: this.zAlign(), zWrap: this.zWrap() }), this.class())); +} + +@Component({ + selector: 'z-breadcrumb-item', + exportAs: 'zBreadcrumbItem', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
  1. + +
  2. + `, +}) +export class ZardBreadcrumbItemComponent { + readonly zType = input('default'); + readonly zShape = input('default'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(breadcrumbItemVariants({ zType: this.zType(), zShape: this.zShape() }), this.class())); +} + +@Component({ + selector: 'z-breadcrumb-link', + exportAs: 'zBreadcrumbLink', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [RouterLink], + template: ` + + + + `, +}) +export class ZardBreadcrumbLinkComponent { + readonly zLink = input('/'); + readonly zType = input('default'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(breadcrumbLinkVariants({ zType: this.zType() }), this.class())); +} + +@Component({ + selector: 'z-breadcrumb-page', + exportAs: 'zBreadcrumbPage', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + + + `, +}) +export class ZardBreadcrumbPageComponent { + readonly zType = input('default'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(breadcrumbPageVariants({ zType: this.zType() }), this.class())); +} + +@Component({ + selector: 'z-breadcrumb-separator', + exportAs: 'zBreadcrumbSeparator', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class ZardBreadcrumbSeparatorComponent { + readonly zSeparator = input | null>('/'); + readonly zType = input('default'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(breadcrumbSeparatorVariants({ zType: this.zType() }), this.class())); +} + +@Component({ + selector: 'z-breadcrumb-ellipsis', + exportAs: 'zBreadcrumbEllipsis', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardBreadcrumbEllipsisComponent { + readonly zColor = input('muted'); + + readonly class = input(''); + protected readonly classes = computed(() => mergeClasses(breadcrumbEllipsisVariants({ zColor: this.zColor() }), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/breadcrumb/breadcrumb.module.ts b/src/app/shared/components/breadcrumb/breadcrumb.module.ts new file mode 100644 index 0000000..354c2a0 --- /dev/null +++ b/src/app/shared/components/breadcrumb/breadcrumb.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; + +import { + ZardBreadcrumbComponent, + ZardBreadcrumbEllipsisComponent, + ZardBreadcrumbItemComponent, + ZardBreadcrumbLinkComponent, + ZardBreadcrumbListComponent, + ZardBreadcrumbPageComponent, + ZardBreadcrumbSeparatorComponent, +} from './breadcrumb.component'; + +const components = [ + ZardBreadcrumbComponent, + ZardBreadcrumbListComponent, + ZardBreadcrumbItemComponent, + ZardBreadcrumbLinkComponent, + ZardBreadcrumbPageComponent, + ZardBreadcrumbSeparatorComponent, + ZardBreadcrumbEllipsisComponent, +]; + +@NgModule({ + imports: components, + exports: components, +}) +export class ZardBreadcrumbModule {} \ No newline at end of file diff --git a/src/app/shared/components/breadcrumb/breadcrumb.variants.ts b/src/app/shared/components/breadcrumb/breadcrumb.variants.ts new file mode 100644 index 0000000..8e66f08 --- /dev/null +++ b/src/app/shared/components/breadcrumb/breadcrumb.variants.ts @@ -0,0 +1,111 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const breadcrumbVariants = cva('w-full', { + variants: { + zSize: { + sm: 'text-xs', + md: 'text-sm', + lg: 'text-base', + }, + }, + defaultVariants: { + zSize: 'md', + }, +}); +export type ZardBreadcrumbVariants = VariantProps; + +export const breadcrumbListVariants = cva('text-muted-foreground flex flex-wrap items-center gap-1.5 break-words sm:gap-2.5', { + variants: { + zAlign: { + start: 'justify-start', + center: 'justify-center', + end: 'justify-end', + }, + zWrap: { + wrap: 'flex-wrap', + nowrap: 'flex-nowrap', + }, + }, + defaultVariants: { + zAlign: 'start', + zWrap: 'wrap', + }, +}); +export type ZardBreadcrumbListVariants = VariantProps; + +export const breadcrumbItemVariants = cva('flex items-center gap-1.5 transition-colors', { + variants: { + zType: { + default: '', + muted: 'text-muted-foreground', + bold: 'font-semibold text-foreground', + subtle: 'text-sm text-muted-foreground hover:text-foreground', + }, + zShape: { + default: '', + square: 'px-1 py-0.5 rounded-none', + rounded: 'px-2 py-0.5 rounded-md', + }, + }, + defaultVariants: { + zType: 'default', + zShape: 'default', + }, +}); +export type ZardBreadcrumbItemVariants = VariantProps; + +export const breadcrumbLinkVariants = cva('flex items-center gap-1 transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', { + variants: { + zType: { + default: 'hover:text-foreground', + underline: 'underline text-foreground hover:no-underline', + subtle: 'text-muted-foreground hover:text-foreground', + }, + }, + defaultVariants: { + zType: 'default', + }, +}); +export type ZardBreadcrumbLinkVariants = VariantProps; + +export const breadcrumbPageVariants = cva('flex items-center gap-1 transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', { + variants: { + zType: { + default: 'text-foreground', + underline: 'underline text-foreground hover:no-underline', + subtle: 'text-muted-foreground hover:text-foreground', + current: 'font-semibold text-foreground cursor-default ' + 'hover:text-foreground focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-ring', + }, + }, + defaultVariants: { + zType: 'default', + }, +}); +export type ZardBreadcrumbPageVariants = VariantProps; + +export const breadcrumbSeparatorVariants = cva('select-none', { + variants: { + zType: { + default: 'text-muted-foreground', + strong: 'text-foreground', + primary: 'text-primary', + }, + }, + defaultVariants: { + zType: 'default', + }, +}); +export type ZardBreadcrumbSeparatorVariants = VariantProps; + +export const breadcrumbEllipsisVariants = cva('flex', { + variants: { + zColor: { + muted: 'text-muted-foreground', + strong: 'text-foreground', + }, + }, + defaultVariants: { + zColor: 'muted', + }, +}); +export type ZardBreadcrumbEllipsisVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/button/button.component.ts b/src/app/shared/components/button/button.component.ts new file mode 100644 index 0000000..fee9553 --- /dev/null +++ b/src/app/shared/components/button/button.component.ts @@ -0,0 +1,49 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, ElementRef, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { buttonVariants, ZardButtonVariants } from './button.variants'; + +@Component({ + selector: 'z-button, button[z-button], a[z-button]', + exportAs: 'zButton', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (zLoading()) { + + } + + + `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardButtonComponent { + private readonly elementRef = inject(ElementRef); + + readonly zType = input('default'); + readonly zSize = input('default'); + readonly zShape = input('default'); + + readonly class = input(''); + + readonly zFull = input(false, { transform }); + readonly zLoading = input(false, { transform }); + + protected readonly classes = computed(() => + mergeClasses( + buttonVariants({ + zType: this.zType(), + zSize: this.zSize(), + zShape: this.zShape(), + zFull: this.zFull(), + zLoading: this.zLoading(), + }), + this.class(), + ), + ); +} diff --git a/src/app/shared/components/button/button.variants.ts b/src/app/shared/components/button/button.variants.ts new file mode 100644 index 0000000..8141248 --- /dev/null +++ b/src/app/shared/components/button/button.variants.ts @@ -0,0 +1,40 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const buttonVariants = cva( + "cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all active:scale-95 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + zType: { + default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', + }, + zSize: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', + }, + zShape: { + default: 'rounded-md', + circle: 'rounded-full', + square: 'rounded-none', + }, + zFull: { + true: 'w-full', + }, + zLoading: { + true: 'opacity-50 pointer-events-none', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + zShape: 'default', + }, + }, +); +export type ZardButtonVariants = VariantProps; diff --git a/src/app/shared/components/calendar/calendar.component.ts b/src/app/shared/components/calendar/calendar.component.ts new file mode 100644 index 0000000..8cc81e5 --- /dev/null +++ b/src/app/shared/components/calendar/calendar.component.ts @@ -0,0 +1,591 @@ +import { ChangeDetectionStrategy, Component, computed, ElementRef, input, linkedSignal, model, signal, viewChild, ViewEncapsulation } from '@angular/core'; +import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop'; +import { filter } from 'rxjs'; + +import { calendarDayButtonVariants, calendarDayVariants, calendarNavVariants, calendarVariants, calendarWeekdayVariants, ZardCalendarVariants } from './calendar.variants'; +import { ZardSelectItemComponent } from '../select/select-item.component'; +import { ZardSelectComponent } from '../select/select.component'; +import { ZardButtonComponent } from '../button/button.component'; +import type { ClassValue } from 'clsx'; +import { mergeClasses } from '@shared/utils/merge-classes'; + +export interface CalendarDay { + date: Date; + isCurrentMonth: boolean; + isToday: boolean; + isSelected: boolean; + isDisabled: boolean; + id?: string; +} + +export type { ZardCalendarVariants }; + +@Component({ + selector: 'z-calendar, [z-calendar]', + exportAs: 'zCalendar', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZardButtonComponent, ZardSelectComponent, ZardSelectItemComponent], + host: { + '(keydown)': 'onKeyDown($event)', + '[attr.tabindex]': '0', + '[attr.role]': '"grid"', + '[attr.aria-label]': '"Calendar"', + }, + template: ` +
    + +
    + + + +
    + + + @for (month of months; track $index) { + {{ month }} + } + + + + + @for (year of availableYears(); track year) { + {{ year }} + } + +
    + + +
    + + +
    + @for (weekday of weekdays; track $index) { +
    + {{ weekday }} +
    + } +
    + + +
    + @for (day of calendarDays(); track day.date.getTime(); let i = $index) { +
    + +
    + } +
    +
    + `, +}) +export class ZardCalendarComponent { + private readonly calendarContainer = viewChild.required>('calendarContainer'); + + // Public method to reset navigation (useful for date-picker) + resetNavigation(): void { + const value = this.currentDate(); + this.currentMonthValue.set(value.getMonth().toString()); + this.currentYearValue.set(value.getFullYear().toString()); + this.focusedDayIndex.set(-1); + } + readonly class = input(''); + readonly zSize = input('default'); + readonly value = model(null); + readonly minDate = input(null); + readonly maxDate = input(null); + readonly disabled = input(false); + + readonly dateChange = outputFromObservable(outputToObservable(this.value).pipe(filter(v => v !== null))); + + private readonly focusedDayIndex = signal(-1); + + private readonly currentDate = computed(() => this.value() ?? new Date(), { + equal: (a, b) => a.getTime() === b.getTime(), + }); + + readonly weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']; + readonly months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + + protected readonly classes = computed(() => + mergeClasses( + calendarVariants({ + zSize: this.zSize(), + }), + this.class(), + ), + ); + + protected readonly navClasses = computed(() => mergeClasses(calendarNavVariants())); + + protected readonly weekdayClasses = computed(() => mergeClasses(calendarWeekdayVariants({ zSize: this.zSize() }))); + + protected readonly dayContainerClasses = computed(() => mergeClasses(calendarDayVariants({ zSize: this.zSize() }))); + + protected readonly selectSize = computed(() => { + const size = this.zSize(); + if (size === 'lg') return 'lg'; + if (size === 'sm') return 'sm'; + return 'default'; + }); + + protected readonly navButtonSize = computed(() => { + const size = this.zSize(); + if (size === 'lg') return 'default'; + return 'sm'; + }); + + protected readonly navButtonClasses = computed(() => { + const size = this.zSize(); + const baseClasses = 'p-0 opacity-50 hover:opacity-100'; + + switch (size) { + case 'sm': + return `h-6 w-6 ${baseClasses}`; + case 'lg': + return `h-8 w-8 ${baseClasses}`; + default: + return `h-7 w-7 ${baseClasses}`; + } + }); + + protected readonly currentMonthValue = linkedSignal(() => this.currentDate().getMonth().toString()); + protected readonly currentYearValue = linkedSignal(() => this.currentDate().getFullYear().toString()); + + protected readonly availableYears = computed(() => { + const currentYear = new Date().getFullYear(); + const years = []; + for (let i = currentYear - 10; i <= currentYear + 10; i++) { + years.push(i); + } + return years; + }); + + protected readonly currentMonthYear = computed(() => { + const date = this.currentDate(); + return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); + }); + + protected readonly currentMonthName = computed(() => { + const selectedMonth = parseInt(this.currentMonthValue()); + if (!isNaN(selectedMonth) && this.months[selectedMonth]) return this.months[selectedMonth]; + return this.months[this.currentDate().getMonth()]; + }); + + protected onMonthChange(monthIndex: string): void { + if (!monthIndex || monthIndex.trim() === '') { + console.warn('Invalid month index received:', monthIndex); + return; + } + + const parsedMonth = parseInt(monthIndex, 10); + if (isNaN(parsedMonth) || parsedMonth < 0 || parsedMonth > 11) { + console.warn('Invalid month value:', monthIndex, 'parsed as:', parsedMonth); + return; + } + + const currentDate = this.currentDate(); + const selectedYear = parseInt(this.currentYearValue()); + const newDate = new Date(isNaN(selectedYear) ? currentDate.getFullYear() : selectedYear, parsedMonth, 1); + this.currentMonthValue.set(newDate.getMonth().toString()); + this.focusedDayIndex.set(-1); + } + + protected onYearChange(year: string): void { + if (!year || year.trim() === '') { + console.warn('Invalid year received:', year); + return; + } + + const parsedYear = parseInt(year, 10); + if (isNaN(parsedYear) || parsedYear < 1900 || parsedYear > 2100) { + console.warn('Invalid year value:', year, 'parsed as:', parsedYear); + return; + } + + const currentDate = this.currentDate(); + const selectedMonth = parseInt(this.currentMonthValue()); + const newDate = new Date(parsedYear, isNaN(selectedMonth) ? currentDate.getMonth() : selectedMonth, 1); + this.currentYearValue.set(newDate.getFullYear().toString()); + this.focusedDayIndex.set(-1); + } + + protected readonly calendarDays = computed(() => { + const currentDate = this.currentDate(); + const navigationDate = new Date(parseInt(this.currentYearValue()), parseInt(this.currentMonthValue()), currentDate.getDate()); + const selectedDate = isNaN(navigationDate.getTime()) ? currentDate : navigationDate; + const today = new Date(); + const minDate = this.minDate(); + const maxDate = this.maxDate(); + + const year = selectedDate.getFullYear(); + const month = selectedDate.getMonth(); + + // Get first day of the month + const firstDay = new Date(year, month, 1); + // Get last day of the month + const lastDay = new Date(year, month + 1, 0); + + // Get the first day of the week for the first day of the month + const startDate = new Date(firstDay); + startDate.setDate(startDate.getDate() - startDate.getDay()); + + // Get the last day of the week for the last day of the month + const endDate = new Date(lastDay); + endDate.setDate(endDate.getDate() + (6 - endDate.getDay())); + + const days: CalendarDay[] = []; + const currentWeekDate = new Date(startDate); + + while (currentWeekDate <= endDate) { + const date = new Date(currentWeekDate); + const isCurrentMonth = date.getMonth() === month; + const isToday = this.isSameDay(date, today); + const isSelected = currentDate ? this.isSameDay(date, currentDate) : false; + const isDisabled = this.disabled() || this.isDateDisabled(date, minDate, maxDate); + + days.push({ + date, + isCurrentMonth, + isToday, + isSelected, + isDisabled, + }); + + currentWeekDate.setDate(currentWeekDate.getDate() + 1); + } + + return days; + }); + + protected dayButtonClasses(day: CalendarDay): string { + return mergeClasses( + calendarDayButtonVariants({ + zSize: this.zSize(), + selected: day.isSelected, + today: day.isToday, + outside: !day.isCurrentMonth, + disabled: day.isDisabled, + }), + ); + } + + protected previousMonth() { + const currentDate = this.currentDate(); + const currentMonth = parseInt(this.currentMonthValue()); + const previous = new Date(currentDate.getFullYear(), (isNaN(currentMonth) ? currentDate.getMonth() : currentMonth) - 1, 1); + this.currentMonthValue.set(previous.getMonth().toString()); + this.focusedDayIndex.set(-1); + } + + protected nextMonth() { + const currentDate = this.currentDate(); + const currentMonth = parseInt(this.currentMonthValue()); + const next = new Date(currentDate.getFullYear(), (isNaN(currentMonth) ? currentDate.getMonth() : currentMonth) + 1, 1); + this.currentMonthValue.set(next.getMonth().toString()); + this.focusedDayIndex.set(-1); + } + + protected isPreviousDisabled(): boolean { + if (this.disabled()) return true; + + const minDate = this.minDate(); + if (!minDate) return false; + + const currentDate = this.currentDate(); + const currentMonth = parseInt(this.currentMonthValue()); + const lastDayOfPreviousMonth = new Date(currentDate.getFullYear(), isNaN(currentMonth) ? currentDate.getMonth() : currentMonth, 0); + + return lastDayOfPreviousMonth.getTime() < minDate.getTime(); + } + + protected isNextDisabled(): boolean { + if (this.disabled()) return true; + + const maxDate = this.maxDate(); + if (!maxDate) return false; + + const currentDate = this.currentDate(); + const currentMonth = parseInt(this.currentMonthValue()); + const nextMonth = new Date(currentDate.getFullYear(), (isNaN(currentMonth) ? currentDate.getMonth() : currentMonth) + 1, 1); + + return nextMonth.getTime() > maxDate.getTime(); + } + + selectDate(date: Date, i?: number) { + if (this.disabled()) return; + + const minDate = this.minDate(); + const maxDate = this.maxDate(); + + if (this.isDateDisabled(date, minDate, maxDate)) return; + + this.value.set(date); + this.focusedDayIndex.set(i ?? this.calendarDays().findIndex(day => this.isSameDay(day.date, date))); + } + + protected getDayAriaLabel(day: CalendarDay): string { + const dateStr = day.date.toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }); + + const labels = [dateStr]; + + if (day.isToday) labels.push('Today'); + if (day.isSelected) labels.push('Selected'); + if (!day.isCurrentMonth) labels.push('Outside month'); + if (day.isDisabled) labels.push('Disabled'); + + return labels.join(', '); + } + + protected getDayId(index: number): string { + return `calendar-day-${index}`; + } + + protected getFocusedDayIndex(): number { + const focused = this.focusedDayIndex(); + if (focused >= 0) return focused; + + // Default focus to selected date or today + const days = this.calendarDays(); + const selectedIndex = days.findIndex(day => day.isSelected); + if (selectedIndex >= 0) return selectedIndex; + + const todayIndex = days.findIndex(day => day.isToday && day.isCurrentMonth); + if (todayIndex >= 0) return todayIndex; + + // Fall back to first enabled day of current month + const firstCurrentMonthIndex = days.findIndex(day => day.isCurrentMonth && !day.isDisabled); + return firstCurrentMonthIndex >= 0 ? firstCurrentMonthIndex : 0; + } + + onKeyDown(event: KeyboardEvent): void { + if (this.disabled()) return; + + const days = this.calendarDays(); + if (days.length === 0) return; + + const currentIndex = this.getFocusedDayIndex(); + let newIndex: number | null = null; + + switch (event.key) { + case 'ArrowLeft': + event.preventDefault(); + newIndex = this.navigate(currentIndex, -1, days); + break; + case 'ArrowRight': + event.preventDefault(); + newIndex = this.navigate(currentIndex, 1, days); + break; + case 'ArrowUp': + event.preventDefault(); + newIndex = this.navigate(currentIndex, -7, days); + break; + case 'ArrowDown': + event.preventDefault(); + newIndex = this.navigate(currentIndex, 7, days); + break; + case 'Home': + event.preventDefault(); + newIndex = this.findEnabledInRange(Math.floor(currentIndex / 7) * 7, Math.floor(currentIndex / 7) * 7 + 6, days); + break; + case 'End': + event.preventDefault(); + newIndex = this.findEnabledInRange(Math.floor(currentIndex / 7) * 7 + 6, Math.floor(currentIndex / 7) * 7, days, true); + break; + case 'PageUp': + event.preventDefault(); + if (event.ctrlKey) { + this.navigateYear(-1); + } else { + this.previousMonth(); + } + this.resetFocusAfterNavigation(); + return; + case 'PageDown': + event.preventDefault(); + if (event.ctrlKey) { + this.navigateYear(1); + } else { + this.nextMonth(); + } + this.resetFocusAfterNavigation(); + return; + case 'Enter': + case ' ': { + event.preventDefault(); + const focusedDay = days[currentIndex]; + if (focusedDay && !focusedDay.isDisabled) { + this.selectDate(focusedDay.date, currentIndex); + } + return; + } + default: + return; + } + + if (newIndex !== null && newIndex !== currentIndex) { + this.setFocus(newIndex); + } + } + + private navigate(currentIndex: number, step: number, days: CalendarDay[]): number | null { + const targetIndex = currentIndex + step; + + // If within bounds, find enabled day + if (targetIndex >= 0 && targetIndex < days.length) { + return this.findEnabledInRange(targetIndex, currentIndex, days); + } + + // Handle month boundaries + const dayOfWeek = currentIndex % 7; + + if (step === -1) { + // Going left - navigate to previous month, focus last day + this.previousMonth(); + setTimeout(() => this.resetFocusAfterNavigation('last'), 0); + } else if (step === 1) { + // Going right - navigate to next month, focus first day + this.nextMonth(); + setTimeout(() => this.resetFocusAfterNavigation('first'), 0); + } else if (step === -7) { + // Going up - navigate to previous month, preserve column + this.previousMonth(); + setTimeout(() => this.resetFocusAfterNavigation('lastWeek', dayOfWeek), 0); + } else if (step === 7) { + // Going down - navigate to next month, preserve column + this.nextMonth(); + setTimeout(() => this.resetFocusAfterNavigation('firstWeek', dayOfWeek), 0); + } + + return null; + } + + private findEnabledInRange(start: number, fallback: number, days: CalendarDay[], reverse = false): number { + const clampedStart = Math.max(0, Math.min(start, days.length - 1)); + const clampedFallback = Math.max(0, Math.min(fallback, days.length - 1)); + + if (!reverse) { + // Search forward from start + for (let i = clampedStart; i < days.length; i++) { + if (!days[i].isDisabled) return i; + } + // Search backward from start + for (let i = clampedStart - 1; i >= 0; i--) { + if (!days[i].isDisabled) return i; + } + } else { + // Search backward from start + for (let i = clampedStart; i >= 0; i--) { + if (!days[i].isDisabled) return i; + } + // Search forward from start + for (let i = clampedStart + 1; i < days.length; i++) { + if (!days[i].isDisabled) return i; + } + } + + return clampedFallback; + } + + private setFocus(index: number): void { + this.focusedDayIndex.set(index); + setTimeout(() => { + const dayElement = this.calendarContainer()?.nativeElement.querySelector(`#${this.getDayId(index)}`) as HTMLElement; + dayElement?.focus(); + }, 0); + } + + private navigateYear(direction: number): void { + const current = this.currentDate(); + const newDate = new Date(current.getFullYear() + direction, current.getMonth(), 1); + this.currentYearValue.set(newDate.getFullYear().toString()); + } + + private resetFocusAfterNavigation(position = 'default', dayOfWeek = -1): void { + setTimeout(() => { + const days = this.calendarDays(); + let targetIndex = -1; + + switch (position) { + case 'first': + // Focus first enabled day + targetIndex = days.findIndex(day => !day.isDisabled); + break; + case 'last': + // Focus last enabled day + for (let i = days.length - 1; i >= 0; i--) { + if (!days[i].isDisabled) { + targetIndex = i; + break; + } + } + break; + case 'firstWeek': + // Focus same day of week in first week + if (dayOfWeek >= 0 && dayOfWeek < 7) { + targetIndex = this.findEnabledInRange(dayOfWeek, 0, days); + } + break; + case 'lastWeek': + // Focus same day of week in last week + if (dayOfWeek >= 0) { + const lastWeekStart = Math.floor((days.length - 1) / 7) * 7; + const targetIdx = Math.min(lastWeekStart + dayOfWeek, days.length - 1); + targetIndex = this.findEnabledInRange(targetIdx, days.length - 1, days); + } + break; + default: { + // Default priority: selected > today > first enabled + const selectedIndex = days.findIndex(day => day.isSelected); + const todayIndex = days.findIndex(day => day.isToday && day.isCurrentMonth); + const firstEnabledIndex = days.findIndex(day => day.isCurrentMonth && !day.isDisabled); + + targetIndex = selectedIndex >= 0 ? selectedIndex : todayIndex >= 0 ? todayIndex : firstEnabledIndex >= 0 ? firstEnabledIndex : 0; + break; + } + } + + if (targetIndex >= 0) { + this.setFocus(targetIndex); + } + }, 0); + } + + private isSameDay(date1: Date, date2: Date): boolean { + return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate(); + } + + private isDateDisabled(date: Date, minDate: Date | null, maxDate: Date | null): boolean { + if (minDate && date < minDate) return true; + if (maxDate && date > maxDate) return true; + return false; + } +} \ No newline at end of file diff --git a/src/app/shared/components/calendar/calendar.variants.ts b/src/app/shared/components/calendar/calendar.variants.ts new file mode 100644 index 0000000..88b724a --- /dev/null +++ b/src/app/shared/components/calendar/calendar.variants.ts @@ -0,0 +1,105 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const calendarVariants = cva('bg-background p-3 w-fit rounded-lg border', { + variants: { + zSize: { + sm: 'text-sm', + default: '', + lg: 'text-lg', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export const calendarMonthVariants = cva('flex flex-col w-full gap-4'); + +export const calendarNavVariants = cva('flex items-center justify-between gap-2 w-full mb-4'); + +export const calendarNavButtonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100', +); + +export const calendarWeekdaysVariants = cva('flex'); + +export const calendarWeekdayVariants = cva('text-muted-foreground font-normal text-center', { + variants: { + zSize: { + sm: 'text-xs w-7', + default: 'text-[0.8rem] w-9', + lg: 'text-sm w-11', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export const calendarWeekVariants = cva('flex w-full mt-2'); + +export const calendarDayVariants = cva('text-center p-0 relative focus-within:relative focus-within:z-20', { + variants: { + zSize: { + sm: 'h-7 w-7 text-xs', + default: 'h-9 w-9 text-sm', + lg: 'h-11 w-11 text-base', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export const calendarDayButtonVariants = cva( + 'p-0 font-normal inline-flex items-center justify-center whitespace-nowrap rounded-md ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground', + { + variants: { + zSize: { + sm: 'h-7 w-7 text-xs', + default: 'h-9 w-9 text-sm', + lg: 'h-11 w-11 text-base', + }, + selected: { + true: 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground', + false: '', + }, + today: { + true: 'bg-accent text-accent-foreground', + false: '', + }, + outside: { + true: 'text-muted-foreground opacity-50', + false: '', + }, + disabled: { + true: 'text-muted-foreground opacity-50 cursor-not-allowed', + false: '', + }, + }, + compoundVariants: [ + { + today: true, + selected: false, + className: 'bg-accent text-accent-foreground', + }, + { + today: true, + selected: true, + className: 'bg-primary text-primary-foreground', + }, + ], + defaultVariants: { + zSize: 'default', + selected: false, + today: false, + outside: false, + disabled: false, + }, + }, +); + +export type ZardCalendarVariants = VariantProps; +export type ZardCalendarWeekdayVariants = VariantProps; +export type ZardCalendarDayVariants = VariantProps; +export type ZardCalendarDayButtonVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/card/card.component.ts b/src/app/shared/components/card/card.component.ts new file mode 100644 index 0000000..afe2b35 --- /dev/null +++ b/src/app/shared/components/card/card.component.ts @@ -0,0 +1,55 @@ +import type { ClassValue } from 'clsx'; + +import { + ChangeDetectionStrategy, + Component, + computed, + input, + TemplateRef, + ViewEncapsulation, +} from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardStringTemplateOutletDirective } from '../core/directives/string-template-outlet/string-template-outlet.directive'; +import { cardBodyVariants, cardHeaderVariants, cardVariants } from './card.variants'; + +@Component({ + selector: 'z-card', + exportAs: 'zCard', + standalone: true, + imports: [ZardStringTemplateOutletDirective], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (zTitle()) { +
    +
    + {{ zTitle() }} +
    + + @if (zDescription()) { +
    + {{ zDescription() }} +
    + } +
    + } + +
    + +
    + `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardCardComponent { + readonly zTitle = input>(); + readonly zDescription = input>(); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(cardVariants(), this.class())); + protected readonly headerClasses = computed(() => mergeClasses(cardHeaderVariants())); + protected readonly bodyClasses = computed(() => mergeClasses(cardBodyVariants())); +} diff --git a/src/app/shared/components/card/card.variants.ts b/src/app/shared/components/card/card.variants.ts new file mode 100644 index 0000000..d28a67f --- /dev/null +++ b/src/app/shared/components/card/card.variants.ts @@ -0,0 +1,19 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const cardVariants = cva( + 'block rounded-lg border bg-card text-card-foreground shadow-sm w-full p-6', + { + variants: {}, + } +); +export type ZardCardVariants = VariantProps; + +export const cardHeaderVariants = cva('flex flex-col space-y-1.5 pb-0 gap-1.5 mb-6', { + variants: {}, +}); +export type ZardCardHeaderVariants = VariantProps; + +export const cardBodyVariants = cva('block', { + variants: {}, +}); +export type ZardCardBodyVariants = VariantProps; diff --git a/src/app/shared/components/checkbox/checkbox.component.ts b/src/app/shared/components/checkbox/checkbox.component.ts new file mode 100644 index 0000000..0b39752 --- /dev/null +++ b/src/app/shared/components/checkbox/checkbox.component.ts @@ -0,0 +1,83 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, forwardRef, inject, input, output, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { checkboxLabelVariants, checkboxVariants, ZardCheckboxVariants } from './checkbox.variants'; + +import type { ClassValue } from 'clsx'; + +type OnTouchedType = () => any; +type OnChangeType = (value: any) => void; + +@Component({ + selector: 'z-checkbox, [z-checkbox]', + standalone: true, + exportAs: 'zCheckbox', + template: ` + +
    + + +
    + +
    + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardCheckboxComponent), + multi: true, + }, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, +}) +export class ZardCheckboxComponent implements ControlValueAccessor { + private cdr = inject(ChangeDetectorRef); + + readonly checkChange = output(); + readonly class = input(''); + readonly disabled = input(false, { transform }); + readonly zType = input('default'); + readonly zSize = input('default'); + readonly zShape = input('default'); + /* eslint-disable-next-line @typescript-eslint/no-empty-function */ + private onChange: OnChangeType = () => {}; + /* eslint-disable-next-line @typescript-eslint/no-empty-function */ + private onTouched: OnTouchedType = () => {}; + + protected readonly classes = computed(() => mergeClasses(checkboxVariants({ zType: this.zType(), zSize: this.zSize(), zShape: this.zShape() }), this.class())); + protected readonly labelClasses = computed(() => mergeClasses(checkboxLabelVariants({ zSize: this.zSize() }))); + checked = false; + + writeValue(val: boolean): void { + this.checked = val; + this.cdr.markForCheck(); + } + + registerOnChange(fn: OnChangeType): void { + this.onChange = fn; + } + + registerOnTouched(fn: OnTouchedType): void { + this.onTouched = fn; + } + + onCheckboxBlur(): void { + this.onTouched(); + this.cdr.markForCheck(); + } + + onCheckboxChange(): void { + if (this.disabled()) return; + + this.checked = !this.checked; + this.onChange(this.checked); + this.checkChange.emit(this.checked); + this.cdr.markForCheck(); + } +} diff --git a/src/app/shared/components/checkbox/checkbox.variants.ts b/src/app/shared/components/checkbox/checkbox.variants.ts new file mode 100644 index 0000000..66d257f --- /dev/null +++ b/src/app/shared/components/checkbox/checkbox.variants.ts @@ -0,0 +1,42 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const checkboxVariants = cva( + 'cursor-[unset] peer appearance-none border transition shadow hover:shadow-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', + { + variants: { + zType: { + default: 'border-primary checked:bg-primary', + destructive: 'border-destructive checked:bg-destructive', + }, + zSize: { + default: 'h-4 w-4', + lg: 'h-6 w-6', + }, + zShape: { + default: 'rounded', + circle: 'rounded-full', + square: 'rounded-none', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + zShape: 'default', + }, + }, +); + +export const checkboxLabelVariants = cva('cursor-[unset] text-current empty:hidden', { + variants: { + zSize: { + default: 'text-base', + lg: 'text-lg', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export type ZardCheckboxVariants = VariantProps; +export type ZardCheckLabelVariants = VariantProps; diff --git a/src/app/shared/components/combobox/combobox.component.ts b/src/app/shared/components/combobox/combobox.component.ts new file mode 100644 index 0000000..580908e --- /dev/null +++ b/src/app/shared/components/combobox/combobox.component.ts @@ -0,0 +1,407 @@ +import type { ClassValue } from 'clsx'; + +import { + ChangeDetectionStrategy, + Component, + computed, + ElementRef, + EventEmitter, + forwardRef, + HostListener, + input, + Output, + signal, + viewChild, + ViewEncapsulation, +} from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardButtonComponent } from '../button/button.component'; +import { ZardCommandEmptyComponent } from '../command/command-empty.component'; +import { ZardCommandInputComponent } from '../command/command-input.component'; +import { ZardCommandListComponent } from '../command/command-list.component'; +import { ZardCommandOptionGroupComponent } from '../command/command-option-group.component'; +import { ZardCommandOptionComponent } from '../command/command-option.component'; +import { ZardCommandComponent, ZardCommandOption } from '../command/command.component'; +import { ZardPopoverComponent, ZardPopoverDirective } from '../popover/popover.component'; +import { comboboxVariants, ZardComboboxVariants } from './combobox.variants'; +import { ZardEmptyComponent } from '../empty/empty.component'; + +export interface ZardComboboxOption { + value: string; + label: string; + disabled?: boolean; + icon?: string; +} + +export interface ZardComboboxGroup { + label?: string; + options: ZardComboboxOption[]; +} + +@Component({ + selector: 'z-combobox', + exportAs: 'zCombobox', + standalone: true, + imports: [ + FormsModule, + ZardButtonComponent, + ZardCommandComponent, + ZardCommandInputComponent, + ZardCommandListComponent, + ZardCommandEmptyComponent, + ZardCommandOptionComponent, + ZardCommandOptionGroupComponent, + ZardPopoverDirective, + ZardPopoverComponent, + ZardEmptyComponent, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + + + + + @if (searchable()) { + + } + + + @if (emptyText()) { + + + + } + + @if (groups().length > 0) { + @for (group of groups(); track group.label || $index) { + @if (group.label) { + + @for (option of group.options; track option.value) { + + {{ option.label }} + @if (option.value === getCurrentValue()) { + + } + + } + + } @else { + @for (option of group.options; track option.value) { + + {{ option.label }} + @if (option.value === getCurrentValue()) { + + } + + } + } + } + } @else if (options().length > 0) { + @for (option of options(); track option.value) { + + {{ option.label }} + @if (option.value === getCurrentValue()) { + + } + + } + } + + + + + `, + host: { + '[class]': 'classes()', + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardComboboxComponent), + multi: true, + }, + ], +}) +export class ZardComboboxComponent implements ControlValueAccessor { + readonly class = input(''); + readonly buttonVariant = input<'default' | 'outline' | 'secondary' | 'ghost'>('outline'); + readonly zWidth = input('default'); + readonly placeholder = input('Select...'); + readonly searchPlaceholder = input('Search...'); + readonly emptyText = input('No results found.'); + readonly disabled = input(false); + readonly searchable = input(true); + readonly value = input(null); + readonly options = input([]); + readonly groups = input([]); + readonly ariaLabel = input(''); + readonly ariaDescribedBy = input(''); + + @Output() readonly zValueChange = new EventEmitter(); + @Output() readonly zOnSelect = new EventEmitter(); + + readonly popoverDirective = viewChild.required('popoverTrigger', { read: ZardPopoverDirective }); + readonly buttonRef = viewChild.required('popoverTrigger', { read: ElementRef }); + readonly commandRef = viewChild('commandRef', { read: ZardCommandComponent }); + readonly commandInputRef = viewChild('commandInputRef', { read: ZardCommandInputComponent }); + + protected readonly open = signal(false); + protected readonly internalValue = signal(null); + + protected readonly classes = computed(() => + mergeClasses( + comboboxVariants({ + zWidth: this.zWidth(), + }), + this.class(), + ), + ); + + protected readonly buttonClasses = computed(() => 'w-full justify-between'); + + protected readonly iconClasses = computed(() => 'icon-chevrons-up-down ml-2 h-4 w-4 shrink-0 opacity-50'); + + protected readonly popoverClasses = computed(() => { + const widthClass = this.zWidth() === 'full' ? 'w-full' : 'w-[200px]'; + return `${widthClass} p-0`; + }); + + protected readonly getCurrentValue = computed(() => this.value() ?? this.internalValue()); + + protected readonly displayValue = computed(() => { + const currentValue = this.getCurrentValue(); + if (!currentValue) return null; + + // Search in groups first + if (this.groups().length > 0) { + for (const group of this.groups()) { + const option = group.options.find(opt => opt.value === currentValue); + if (option) return option.label; + } + } + + // Then search in flat options + const option = this.options().find(opt => opt.value === currentValue); + return option?.label || null; + }); + + private onChange: (value: string | null) => void = () => { + // ControlValueAccessor implementation + }; + private onTouched: () => void = () => { + // ControlValueAccessor implementation + }; + + setOpen(open: boolean) { + this.open.set(open); + if (open) { + // Give time for the popover content to render and options to be detected + setTimeout(() => { + const commandRef = this.commandRef(); + if (commandRef) { + // Refresh options to ensure they're detected + commandRef.refreshOptions(); + // Focus on search input if searchable, otherwise on command component + if (this.searchable()) { + this.commandInputRef()?.focus(); + } else { + commandRef.focus(); + } + } + }, 10); + } + } + + handleSelect(commandOption: ZardCommandOption) { + const selectedValue = commandOption.value as string; + + // Toggle behavior - if same value is selected, clear it + const newValue = selectedValue === this.getCurrentValue() ? null : selectedValue; + + this.internalValue.set(newValue); + this.onChange(newValue); + this.zValueChange.emit(newValue); + + // Emit the combobox option if we have a selection + if (newValue) { + let selectedOption: ZardComboboxOption | undefined; + + if (this.groups().length > 0) { + for (const group of this.groups()) { + selectedOption = group.options.find(opt => opt.value === newValue); + if (selectedOption) break; + } + } else { + selectedOption = this.options().find(opt => opt.value === newValue); + } + + if (selectedOption) { + this.zOnSelect.emit(selectedOption); + } + } + + // Close the popover + this.popoverDirective().hide(); + + // Return focus to the combobox button after selection + this.buttonRef().nativeElement.focus(); + } + + @HostListener('keydown', ['$event']) + onKeyDown(event: KeyboardEvent) { + if (this.disabled()) return; + + // Handle different keyboard events based on combobox state + if (this.open()) { + // When popover is open + switch (event.key) { + case 'Escape': + event.preventDefault(); + event.stopPropagation(); + this.popoverDirective().hide(); + this.buttonRef().nativeElement.focus(); + break; + + case 'Tab': + // Allow tab to close and move to next element + this.popoverDirective().hide(); + break; + + case 'ArrowDown': + case 'ArrowUp': + case 'Enter': + case 'Home': + case 'End': + case 'PageUp': + case 'PageDown': + // Forward navigation to command component + event.preventDefault(); + this.commandRef()?.onKeyDown(event); + break; + } + } else { + // When popover is closed + switch (event.key) { + case 'ArrowDown': + case 'ArrowUp': + case 'Enter': + case ' ': // Space key + event.preventDefault(); + this.popoverDirective().show(); + break; + + case 'Escape': + // Clear selection if there's a value + if (this.getCurrentValue()) { + event.preventDefault(); + this.internalValue.set(null); + this.onChange(null); + this.zValueChange.emit(null); + } + break; + + default: + // For searchable comboboxes, open and start typing + if (this.searchable() && event.key.length === 1 && !event.ctrlKey && !event.altKey && !event.metaKey) { + event.preventDefault(); + this.popoverDirective().show(); + // Let the command input handle the character after opening + setTimeout(() => { + const inputElement = this.commandInputRef(); + if (inputElement) { + inputElement.focus(); + // Simulate the key press in the input + const input = inputElement as unknown as { + searchInput?: { nativeElement: HTMLInputElement }; + searchTerm: { set: (value: string) => void }; + searchSubject: { next: (value: string) => void }; + }; + if (input.searchInput?.nativeElement) { + input.searchInput.nativeElement.value = event.key; + input.searchTerm.set(event.key); + input.searchSubject.next(event.key); + } + } + }, 20); + } + break; + } + } + } + + @HostListener('document:keydown', ['$event']) + onDocumentKeyDown(event: KeyboardEvent) { + // Close on Escape from anywhere when this combobox is open + if (this.open() && event.key === 'Escape') { + const target = event.target as Element; + const buttonElement = this.buttonRef().nativeElement; + // Only handle if not already handled by the component itself + if (!buttonElement.contains(target)) { + this.popoverDirective().hide(); + this.buttonRef().nativeElement.focus(); + } + } + } + + // ControlValueAccessor implementation + writeValue(value: string | null): void { + this.internalValue.set(value); + } + + registerOnChange(fn: (value: string | null) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(): void { + // The disabled state is handled by the disabled input + } +} \ No newline at end of file diff --git a/src/app/shared/components/combobox/combobox.variants.ts b/src/app/shared/components/combobox/combobox.variants.ts new file mode 100644 index 0000000..2f4c22f --- /dev/null +++ b/src/app/shared/components/combobox/combobox.variants.ts @@ -0,0 +1,18 @@ +import { cva, type VariantProps } from 'class-variance-authority'; + +export const comboboxVariants = cva('', { + variants: { + zWidth: { + default: 'w-[200px]', + sm: 'w-[150px]', + md: 'w-[250px]', + lg: 'w-[350px]', + full: 'w-full', + }, + }, + defaultVariants: { + zWidth: 'default', + }, +}); + +export type ZardComboboxVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/command/command-divider.component.ts b/src/app/shared/components/command/command-divider.component.ts new file mode 100644 index 0000000..6c84dcd --- /dev/null +++ b/src/app/shared/components/command/command-divider.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component, computed, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardCommandComponent } from './command.component'; +import { commandSeparatorVariants } from './command.variants'; + +import type { ClassValue } from 'clsx'; +@Component({ + selector: 'z-command-divider', + exportAs: 'zCommandDivider', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (shouldShow()) { +
    + } + `, +}) +export class ZardCommandDividerComponent { + private readonly commandComponent = inject(ZardCommandComponent, { optional: true }); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(commandSeparatorVariants({}), this.class())); + + protected readonly shouldShow = computed(() => { + if (!this.commandComponent) return true; + + const searchTerm = this.commandComponent.searchTerm(); + + // If no search, always show dividers + if (searchTerm === '') return true; + + // If there's a search term, hide all dividers for now + // This is a simple approach - we can make it smarter later + return false; + }); +} \ No newline at end of file diff --git a/src/app/shared/components/command/command-empty.component.ts b/src/app/shared/components/command/command-empty.component.ts new file mode 100644 index 0000000..f13f141 --- /dev/null +++ b/src/app/shared/components/command/command-empty.component.ts @@ -0,0 +1,47 @@ +import { ChangeDetectionStrategy, Component, computed, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardCommandJsonComponent } from './command-json.component'; +import { ZardCommandComponent } from './command.component'; +import { commandEmptyVariants } from './command.variants'; + +import type { ClassValue } from 'clsx'; + +@Component({ + selector: 'z-command-empty', + exportAs: 'zCommandEmpty', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (shouldShow()) { +
    + No results found. +
    + } + `, +}) +export class ZardCommandEmptyComponent { + private readonly commandComponent = inject(ZardCommandComponent, { optional: true }); + private readonly jsonCommandComponent = inject(ZardCommandJsonComponent, { optional: true }); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(commandEmptyVariants({}), this.class())); + + protected readonly shouldShow = computed(() => { + // Check traditional command component + if (this.commandComponent) { + const filteredOptions = this.commandComponent.filteredOptions(); + return filteredOptions.length === 0; + } + + // Check JSON command component + if (this.jsonCommandComponent) { + const filteredGroups = this.jsonCommandComponent.filteredGroups(); + return filteredGroups.length === 0; + } + + return false; + }); +} \ No newline at end of file diff --git a/src/app/shared/components/command/command-input.component.ts b/src/app/shared/components/command/command-input.component.ts new file mode 100644 index 0000000..03938a0 --- /dev/null +++ b/src/app/shared/components/command/command-input.component.ts @@ -0,0 +1,173 @@ +import { Subject, switchMap, takeUntil, timer } from 'rxjs'; + +import { + ChangeDetectionStrategy, + Component, + computed, + ElementRef, + EventEmitter, + forwardRef, + inject, + input, + OnDestroy, + OnInit, + Output, + signal, + viewChild, + ViewEncapsulation, +} from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardCommandJsonComponent } from './command-json.component'; +import { ZardCommandComponent } from './command.component'; +import { commandInputVariants } from './command.variants'; + +import type { ClassValue } from 'clsx'; +@Component({ + selector: 'z-command-input', + exportAs: 'zCommandInput', + standalone: true, + imports: [FormsModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + + +
    + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardCommandInputComponent), + multi: true, + }, + ], +}) +export class ZardCommandInputComponent implements ControlValueAccessor, OnInit, OnDestroy { + private readonly commandComponent = inject(ZardCommandComponent, { optional: true }); + private readonly jsonCommandComponent = inject(ZardCommandJsonComponent, { optional: true }); + readonly searchInput = viewChild.required>('searchInput'); + + readonly placeholder = input('Type a command or search...'); + readonly class = input(''); + + @Output() readonly valueChange = new EventEmitter(); + + readonly searchTerm = signal(''); + private searchSubject = new Subject(); + private destroy$ = new Subject(); + + protected readonly classes = computed(() => mergeClasses(commandInputVariants({}), this.class())); + + private onChange = (_value: string) => { + // ControlValueAccessor implementation - intentionally empty + }; + private onTouched = () => { + // ControlValueAccessor implementation - intentionally empty + }; + + ngOnInit(): void { + // Set up debounced search stream - always send to subject + this.searchSubject + .pipe( + switchMap(value => { + // If empty, emit immediately, otherwise debounce + return value === '' ? timer(0) : timer(150); + }), + takeUntil(this.destroy$), + ) + .subscribe(() => { + // Get the current value from the signal to ensure we have the latest + const currentValue = this.searchTerm(); + this.updateParentComponents(currentValue); + }); + } + + onInput(event: Event) { + const target = event.target as HTMLInputElement; + const value = target.value; + this.searchTerm.set(value); + + // Always send to subject - let the stream handle timing + this.searchSubject.next(value); + } + + private updateParentComponents(value: string): void { + // Send search to appropriate parent component + if (this.commandComponent) { + this.commandComponent.onSearch(value); + } else if (this.jsonCommandComponent) { + this.jsonCommandComponent.onSearch(value); + } + this.onChange(value); + this.valueChange.emit(value); + } + + onKeyDown(event: KeyboardEvent) { + // Let parent command component handle navigation keys + if (['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(event.key)) { + // For Escape key, don't stop propagation to allow document listener to work + if (event.key !== 'Escape') { + event.preventDefault(); // Prevent default input behavior + event.stopPropagation(); // Stop the event from bubbling up + } + + // Try both types of parent components + if (this.commandComponent) { + this.commandComponent.onKeyDown(event); + } else if (this.jsonCommandComponent) { + this.jsonCommandComponent.handleKeydown(event); + } + return; + } + // Handle other keys as needed + } + + writeValue(value: string): void { + this.searchTerm.set(value || ''); + } + + registerOnChange(fn: (value: string) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(_isDisabled: boolean): void { + // Implementation if needed for form control disabled state + } + + /** + * Focus the input element + */ + focus(): void { + this.searchInput().nativeElement.focus(); + } + + ngOnDestroy(): void { + // Complete subjects to clean up subscriptions + this.destroy$.next(); + this.destroy$.complete(); + this.searchSubject.complete(); + } +} \ No newline at end of file diff --git a/src/app/shared/components/command/command-json.component.ts b/src/app/shared/components/command/command-json.component.ts new file mode 100644 index 0000000..1e6cb2c --- /dev/null +++ b/src/app/shared/components/command/command-json.component.ts @@ -0,0 +1,286 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, EventEmitter, forwardRef, HostListener, input, Output, signal, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardCommandDividerComponent } from './command-divider.component'; +import { ZardCommandEmptyComponent } from './command-empty.component'; +import { ZardCommandInputComponent } from './command-input.component'; +import { ZardCommandListComponent } from './command-list.component'; +import { ZardCommandOptionGroupComponent } from './command-option-group.component'; +import { ZardCommandOptionComponent } from './command-option.component'; +import { ZardCommandConfig, ZardCommandOption } from './command.component'; +import { commandVariants, ZardCommandVariants } from './command.variants'; + +@Component({ + selector: 'z-command-json', + exportAs: 'zCommandJson', + standalone: true, + imports: [ + FormsModule, + ZardCommandInputComponent, + ZardCommandListComponent, + ZardCommandEmptyComponent, + ZardCommandOptionComponent, + ZardCommandOptionGroupComponent, + ZardCommandDividerComponent, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    +
    Use arrow keys to navigate, Enter to select, Escape to clear selection.
    +
    + {{ statusMessage() }} +
    + + + {{ config().emptyText || 'No results found.' }} + + @for (group of filteredGroups(); track group.label; let groupIndex = $index) { + + @for (option of group.visibleOptions; track option.value; let optionIndex = $index) { + + + } + + + @if (config().dividers !== false && shouldShowDivider(groupIndex)) { + + } + } + +
    + `, + host: { + '[attr.role]': '"combobox"', + '[attr.aria-expanded]': 'true', + '[attr.aria-haspopup]': '"listbox"', + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardCommandJsonComponent), + multi: true, + }, + ], +}) +export class ZardCommandJsonComponent implements ControlValueAccessor { + readonly config = input.required(); + readonly size = input('default'); + readonly class = input(''); + + @Output() readonly zOnChange = new EventEmitter(); + @Output() readonly zOnSelect = new EventEmitter(); + + // Search functionality + readonly searchTerm = signal(''); + readonly selectedIndex = signal(-1); + + protected readonly classes = computed(() => mergeClasses(commandVariants({ size: this.size() }), this.class())); + + // Computed filtered groups based on search term + readonly filteredGroups = computed(() => { + const searchTerm = this.searchTerm().toLowerCase(); + const groups = this.config().groups; + + if (searchTerm === '') { + return groups.map((group, index) => ({ + label: group.label, + visibleOptions: group.options, + originalIndex: index, + })); + } + + return groups + .map((group, index) => ({ + label: group.label, + visibleOptions: group.options.filter(option => { + const label = option.label.toLowerCase(); + const command = option.command?.toLowerCase() || ''; + const value = String(option.value).toLowerCase(); + return label.includes(searchTerm) || command.includes(searchTerm) || value.includes(searchTerm); + }), + originalIndex: index, + })) + .filter(group => group.visibleOptions.length > 0); + }); + + // Status message for screen readers + protected readonly statusMessage = computed(() => { + const searchTerm = this.searchTerm(); + const filteredGroups = this.filteredGroups(); + const totalOptions = filteredGroups.reduce((acc, group) => acc + group.visibleOptions.length, 0); + + if (searchTerm === '') return ''; + + if (totalOptions === 0) { + return `No results found for "${searchTerm}"`; + } + + return `${totalOptions} result${totalOptions === 1 ? '' : 's'} found for "${searchTerm}"`; + }); + + private onChange = (_value: unknown) => { + // ControlValueAccessor implementation + }; + private onTouched = () => { + // ControlValueAccessor implementation + }; + + onSearch(searchTerm: string) { + this.searchTerm.set(searchTerm); + this.selectedIndex.set(-1); // Reset selection when searching + } + + shouldShowDivider(currentIndex: number): boolean { + const filteredGroups = this.filteredGroups(); + return currentIndex < filteredGroups.length - 1; + } + + onOptionClick(option: ZardCommandOption) { + if (option.disabled) return; + + // Execute option's action if defined + if (option.action) { + option.action(); + } + + // Execute global onSelect callback if defined + const onSelect = this.config().onSelect; + if (onSelect) { + onSelect(option); + } + + this.onChange(option.value); + this.zOnChange.emit(option); + this.zOnSelect.emit(option); + } + + // Handle keyboard navigation and shortcuts + @HostListener('keydown', ['$event']) + handleKeydown(event: KeyboardEvent) { + // Handle global shortcuts (Ctrl/Cmd + key) + if (event.metaKey || event.ctrlKey) { + const matchingOption = this.findOptionByKey(event.key.toLowerCase()); + if (matchingOption) { + event.preventDefault(); + this.onOptionClick(matchingOption); + return; + } + } + + // Handle keyboard navigation + const flatOptions = this.getFlatOptions(); + const currentIndex = this.selectedIndex(); + + switch (event.key) { + case 'ArrowDown': { + event.preventDefault(); + const nextIndex = currentIndex < flatOptions.length - 1 ? currentIndex + 1 : 0; + this.selectedIndex.set(nextIndex); + this.scrollToSelectedOption(); + break; + } + + case 'ArrowUp': { + event.preventDefault(); + const prevIndex = currentIndex > 0 ? currentIndex - 1 : flatOptions.length - 1; + this.selectedIndex.set(prevIndex); + this.scrollToSelectedOption(); + break; + } + + case 'Enter': + event.preventDefault(); + if (currentIndex >= 0 && currentIndex < flatOptions.length) { + const selectedOption = flatOptions[currentIndex]; + if (!selectedOption.disabled) { + this.onOptionClick(selectedOption); + } + } + break; + + case 'Escape': + event.preventDefault(); + this.selectedIndex.set(-1); + break; + } + } + + private findOptionByKey(key: string): ZardCommandOption | undefined { + for (const group of this.config().groups) { + const option = group.options.find(opt => opt.key?.toLowerCase() === key); + if (option) return option; + } + return undefined; + } + + private getFlatOptions(): ZardCommandOption[] { + const filteredGroups = this.filteredGroups(); + const flatOptions: ZardCommandOption[] = []; + + filteredGroups.forEach(group => { + flatOptions.push(...group.visibleOptions); + }); + + return flatOptions; + } + + getOptionClasses(groupIndex: number, optionIndex: number): string { + const flatIndex = this.getFlatOptionIndex(groupIndex, optionIndex); + const isSelected = flatIndex === this.selectedIndex(); + return isSelected ? 'bg-accent text-accent-foreground' : ''; + } + + private getFlatOptionIndex(groupIndex: number, optionIndex: number): number { + const filteredGroups = this.filteredGroups(); + let flatIndex = 0; + + for (let i = 0; i < groupIndex; i++) { + flatIndex += filteredGroups[i].visibleOptions.length; + } + + return flatIndex + optionIndex; + } + + // ControlValueAccessor implementation + writeValue(_value: unknown): void { + // Implementation if needed for form control integration + } + + registerOnChange(fn: (value: unknown) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(_isDisabled: boolean): void { + // Implementation if needed for form control disabled state + } + + private scrollToSelectedOption(): void { + const selectedIndex = this.selectedIndex(); + if (selectedIndex < 0) return; + + // Use a timeout to ensure DOM is updated + setTimeout(() => { + const selectedElement = document.querySelector(`z-command-option.bg-accent`); + if (selectedElement) { + selectedElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + }, 0); + } +} \ No newline at end of file diff --git a/src/app/shared/components/command/command-list.component.ts b/src/app/shared/components/command/command-list.component.ts new file mode 100644 index 0000000..4df61d3 --- /dev/null +++ b/src/app/shared/components/command/command-list.component.ts @@ -0,0 +1,24 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { commandListVariants } from './command.variants'; + +@Component({ + selector: 'z-command-list', + exportAs: 'zCommandList', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class ZardCommandListComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(commandListVariants({}), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/command/command-option-group.component.ts b/src/app/shared/components/command/command-option-group.component.ts new file mode 100644 index 0000000..041bd68 --- /dev/null +++ b/src/app/shared/components/command/command-option-group.component.ts @@ -0,0 +1,59 @@ +import { AfterContentInit, ChangeDetectionStrategy, Component, computed, contentChildren, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardCommandOptionComponent } from './command-option.component'; +import { ZardCommandComponent } from './command.component'; +import { commandGroupHeadingVariants, commandGroupVariants } from './command.variants'; + +import type { ClassValue } from 'clsx'; + +@Component({ + selector: 'z-command-option-group', + exportAs: 'zCommandOptionGroup', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (shouldShow()) { +
    + @if (zLabel()) { +
    + {{ zLabel() }} +
    + } +
    + +
    +
    + } + `, +}) +export class ZardCommandOptionGroupComponent implements AfterContentInit { + private readonly commandComponent = inject(ZardCommandComponent, { optional: true }); + + readonly optionComponents = contentChildren(ZardCommandOptionComponent, { descendants: true }); + + readonly zLabel = input.required(); + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(commandGroupVariants({}), this.class())); + + protected readonly headingClasses = computed(() => mergeClasses(commandGroupHeadingVariants({}))); + + protected readonly shouldShow = computed(() => { + if (!this.commandComponent || !this.optionComponents) return true; + + const searchTerm = this.commandComponent.searchTerm(); + const filteredOptions = this.commandComponent.filteredOptions(); + + // If no search term, show all groups + if (searchTerm === '') return true; + + // Check if any option in this group is in the filtered list + return this.optionComponents().some(option => filteredOptions.includes(option)); + }); + + ngAfterContentInit() { + // Component is ready when content children are initialized + } +} \ No newline at end of file diff --git a/src/app/shared/components/command/command-option.component.ts b/src/app/shared/components/command/command-option.component.ts new file mode 100644 index 0000000..0b5c499 --- /dev/null +++ b/src/app/shared/components/command/command-option.component.ts @@ -0,0 +1,104 @@ +import { ChangeDetectionStrategy, Component, computed, ElementRef, inject, input, signal, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { ZardCommandComponent } from './command.component'; +import { commandItemVariants, commandShortcutVariants, ZardCommandItemVariants } from './command.variants'; + +import type { ClassValue } from 'clsx'; + +@Component({ + selector: 'z-command-option', + exportAs: 'zCommandOption', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (shouldShow()) { +
    + @if (zIcon()) { +
    + } + {{ zLabel() }} + @if (zShortcut()) { + {{ zShortcut() }} + } +
    + } + `, +}) +export class ZardCommandOptionComponent { + private readonly elementRef = inject(ElementRef); + private readonly commandComponent = inject(ZardCommandComponent, { optional: true }); + + readonly zValue = input.required(); + readonly zLabel = input.required(); + readonly zIcon = input(''); + readonly zCommand = input(''); + readonly zShortcut = input(''); + readonly zDisabled = input(false, { transform }); + readonly variant = input('default'); + readonly class = input(''); + + readonly isSelected = signal(false); + + protected readonly classes = computed(() => { + const baseClasses = commandItemVariants({ variant: this.variant() }); + const selectedClasses = this.isSelected() ? 'bg-accent text-accent-foreground' : ''; + return mergeClasses(baseClasses, selectedClasses, this.class()); + }); + + protected readonly shortcutClasses = computed(() => mergeClasses(commandShortcutVariants({}))); + + protected readonly shouldShow = computed(() => { + if (!this.commandComponent) return true; + + const filteredOptions = this.commandComponent.filteredOptions(); + const searchTerm = this.commandComponent.searchTerm(); + + // If no search term, show all options + if (searchTerm === '') return true; + + // Check if this option is in the filtered list + return filteredOptions.includes(this); + }); + + onClick() { + if (this.zDisabled()) return; + if (this.commandComponent) { + this.commandComponent.selectOption(this); + } + } + + onKeyDown(event: KeyboardEvent) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + this.onClick(); + } + } + + onMouseEnter() { + if (this.zDisabled()) return; + // Visual feedback for hover + } + + setSelected(selected: boolean) { + this.isSelected.set(selected); + } + + focus() { + const element = this.elementRef.nativeElement; + element.focus(); + // Scroll element into view if needed + element.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } +} \ No newline at end of file diff --git a/src/app/shared/components/command/command.component.ts b/src/app/shared/components/command/command.component.ts new file mode 100644 index 0000000..ec9443f --- /dev/null +++ b/src/app/shared/components/command/command.component.ts @@ -0,0 +1,257 @@ +import { + ChangeDetectionStrategy, + Component, + computed, + contentChild, + contentChildren, + effect, + EventEmitter, + forwardRef, + HostListener, + input, + Output, + signal, + ViewEncapsulation, +} from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardCommandInputComponent } from './command-input.component'; +import { ZardCommandOptionComponent } from './command-option.component'; +import { commandVariants, ZardCommandVariants } from './command.variants'; + +import type { ClassValue } from 'clsx'; + +export interface ZardCommandOption { + value: unknown; + label: string; + disabled?: boolean; + command?: string; + shortcut?: string; + icon?: string; + action?: () => void; + key?: string; // Keyboard shortcut key (e.g., 'n' for Ctrl+N) +} + +export interface ZardCommandGroup { + label: string; + options: ZardCommandOption[]; +} + +export interface ZardCommandConfig { + placeholder?: string; + emptyText?: string; + groups: ZardCommandGroup[]; + dividers?: boolean; + onSelect?: (option: ZardCommandOption) => void; +} + +@Component({ + selector: 'z-command', + exportAs: 'zCommand', + standalone: true, + imports: [FormsModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    +
    Use arrow keys to navigate, Enter to select, Escape to clear selection.
    +
    + {{ statusMessage() }} +
    + +
    + `, + host: { + '[attr.role]': '"combobox"', + '[attr.aria-expanded]': 'true', + '[attr.aria-haspopup]': '"listbox"', + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardCommandComponent), + multi: true, + }, + ], +}) +export class ZardCommandComponent implements ControlValueAccessor { + readonly commandInput = contentChild(ZardCommandInputComponent); + readonly optionComponents = contentChildren(ZardCommandOptionComponent, { descendants: true }); + + readonly size = input('default'); + readonly class = input(''); + + @Output() readonly zOnChange = new EventEmitter(); + @Output() readonly zOnSelect = new EventEmitter(); + + // Internal signals for search functionality + readonly searchTerm = signal(''); + readonly selectedIndex = signal(-1); + + // Signal to trigger updates when optionComponents change + private readonly optionsUpdateTrigger = signal(0); + + protected readonly classes = computed(() => mergeClasses(commandVariants({ size: this.size() }), this.class())); + + // Computed signal for filtered options - this will automatically update when searchTerm or options change + readonly filteredOptions = computed(() => { + const searchTerm = this.searchTerm(); + // Include the trigger signal to make this computed reactive to option changes + this.optionsUpdateTrigger(); + + if (!this.optionComponents()) return []; + + const lowerSearchTerm = searchTerm.toLowerCase().trim(); + if (lowerSearchTerm === '') return this.optionComponents(); + + return this.optionComponents().filter(option => { + const label = option.zLabel().toLowerCase(); + const command = option.zCommand()?.toLowerCase() || ''; + return label.includes(lowerSearchTerm) || command.includes(lowerSearchTerm); + }); + }); + + // Status message for screen readers + protected readonly statusMessage = computed(() => { + const searchTerm = this.searchTerm(); + const filteredCount = this.filteredOptions().length; + + if (searchTerm === '') return ''; + + if (filteredCount === 0) { + return `No results found for "${searchTerm}"`; + } + + return `${filteredCount} result${filteredCount === 1 ? '' : 's'} found for "${searchTerm}"`; + }); + + private onChange = (_value: unknown) => { + // ControlValueAccessor implementation + }; + private onTouched = () => { + // ControlValueAccessor implementation + }; + + constructor() { + effect(() => { + this.triggerOptionsUpdate(); + }); + } + + /** + * Trigger an update to the filteredOptions computed signal + */ + private triggerOptionsUpdate(): void { + this.optionsUpdateTrigger.update(value => value + 1); + } + + onSearch(searchTerm: string) { + this.searchTerm.set(searchTerm); + this.selectedIndex.set(-1); + this.updateSelectedOption(); + } + + selectOption(option: ZardCommandOptionComponent) { + const commandOption: ZardCommandOption = { + value: option.zValue(), + label: option.zLabel(), + disabled: option.zDisabled(), + command: option.zCommand(), + shortcut: option.zShortcut(), + icon: option.zIcon(), + }; + + this.onChange(commandOption.value); + this.zOnChange.emit(commandOption); + this.zOnSelect.emit(commandOption); + } + + @HostListener('keydown', ['$event']) + onKeyDown(event: KeyboardEvent) { + const filteredOptions = this.filteredOptions(); + if (filteredOptions.length === 0) return; + + const currentIndex = this.selectedIndex(); + + switch (event.key) { + case 'ArrowDown': { + event.preventDefault(); + const nextIndex = currentIndex < filteredOptions.length - 1 ? currentIndex + 1 : 0; + this.selectedIndex.set(nextIndex); + this.updateSelectedOption(); + break; + } + + case 'ArrowUp': { + event.preventDefault(); + const prevIndex = currentIndex > 0 ? currentIndex - 1 : filteredOptions.length - 1; + this.selectedIndex.set(prevIndex); + this.updateSelectedOption(); + break; + } + + case 'Enter': + event.preventDefault(); + if (currentIndex >= 0 && currentIndex < filteredOptions.length) { + const selectedOption = filteredOptions[currentIndex]; + if (!selectedOption.zDisabled()) { + this.selectOption(selectedOption); + } + } + break; + + case 'Escape': + event.preventDefault(); + this.selectedIndex.set(-1); + this.updateSelectedOption(); + break; + } + } + + private updateSelectedOption() { + const filteredOptions = this.filteredOptions(); + const selectedIndex = this.selectedIndex(); + + // Clear previous selection + filteredOptions.forEach(option => option.setSelected(false)); + + // Set new selection + if (selectedIndex >= 0 && selectedIndex < filteredOptions.length) { + const selectedOption = filteredOptions[selectedIndex]; + selectedOption.setSelected(true); + selectedOption.focus(); + } + } + + // ControlValueAccessor implementation + writeValue(_value: unknown): void { + // Implementation if needed for form control integration + } + + registerOnChange(fn: (value: unknown) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(_isDisabled: boolean): void { + // Implementation if needed for form control disabled state + } + + /** + * Refresh the options list - useful when options are added/removed dynamically + */ + refreshOptions(): void { + this.triggerOptionsUpdate(); + } + + /** + * Focus the command input + */ + focus(): void { + this.commandInput()?.focus(); + } +} \ No newline at end of file diff --git a/src/app/shared/components/command/command.module.ts b/src/app/shared/components/command/command.module.ts new file mode 100644 index 0000000..0091616 --- /dev/null +++ b/src/app/shared/components/command/command.module.ts @@ -0,0 +1,28 @@ +import { FormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; + +import { ZardCommandOptionGroupComponent } from './command-option-group.component'; +import { ZardCommandDividerComponent } from './command-divider.component'; +import { ZardCommandOptionComponent } from './command-option.component'; +import { ZardCommandInputComponent } from './command-input.component'; +import { ZardCommandEmptyComponent } from './command-empty.component'; +import { ZardCommandListComponent } from './command-list.component'; +import { ZardCommandJsonComponent } from './command-json.component'; +import { ZardCommandComponent } from './command.component'; + +const COMMAND_COMPONENTS = [ + ZardCommandComponent, + ZardCommandInputComponent, + ZardCommandListComponent, + ZardCommandEmptyComponent, + ZardCommandOptionComponent, + ZardCommandOptionGroupComponent, + ZardCommandDividerComponent, + ZardCommandJsonComponent, +]; + +@NgModule({ + imports: [FormsModule, ...COMMAND_COMPONENTS], + exports: [...COMMAND_COMPONENTS], +}) +export class ZardCommandModule {} \ No newline at end of file diff --git a/src/app/shared/components/command/command.variants.ts b/src/app/shared/components/command/command.variants.ts new file mode 100644 index 0000000..311f6b5 --- /dev/null +++ b/src/app/shared/components/command/command.variants.ts @@ -0,0 +1,71 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const commandVariants = cva('flex h-full w-full flex-col overflow-hidden shadow-md border rounded-md bg-popover text-popover-foreground', { + variants: { + size: { + sm: 'min-h-64', + default: 'min-h-80', + lg: 'min-h-96', + xl: 'min-h-[30rem]', + }, + }, + defaultVariants: { + size: 'default', + }, +}); + +export const commandInputVariants = cva( + 'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', + { + variants: {}, + defaultVariants: {}, + }, +); + +export const commandListVariants = cva('max-h-[300px] overflow-y-auto overflow-x-hidden p-1', { + variants: {}, + defaultVariants: {}, +}); + +export const commandEmptyVariants = cva('py-6 text-center text-sm text-muted-foreground', { + variants: {}, + defaultVariants: {}, +}); + +export const commandGroupVariants = cva('overflow-hidden text-foreground', { + variants: {}, + defaultVariants: {}, +}); + +export const commandGroupHeadingVariants = cva('px-2 py-1.5 text-xs font-medium text-muted-foreground', { + variants: {}, + defaultVariants: {}, +}); + +export const commandItemVariants = cva( + 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50', + { + variants: { + variant: { + default: '', + destructive: 'aria-selected:bg-destructive aria-selected:text-destructive-foreground hover:bg-destructive hover:text-destructive-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); + +export const commandSeparatorVariants = cva('-mx-1 my-1 h-px bg-border', { + variants: {}, + defaultVariants: {}, +}); + +export const commandShortcutVariants = cva('ml-auto text-xs tracking-widest text-muted-foreground', { + variants: {}, + defaultVariants: {}, +}); + +export type ZardCommandVariants = VariantProps; +export type ZardCommandItemVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/core/directives/string-template-outlet/string-template-outlet.directive.ts b/src/app/shared/components/core/directives/string-template-outlet/string-template-outlet.directive.ts new file mode 100644 index 0000000..835d9f4 --- /dev/null +++ b/src/app/shared/components/core/directives/string-template-outlet/string-template-outlet.directive.ts @@ -0,0 +1,83 @@ +import { Directive, EmbeddedViewRef, inject, Input, OnChanges, SimpleChange, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core'; + +export function isTemplateRef(value: TemplateRef | unknown): value is TemplateRef { + return value instanceof TemplateRef; +} + +@Directive({ + selector: '[zStringTemplateOutlet]', + exportAs: 'zStringTemplateOutlet', +}) +export class ZardStringTemplateOutletDirective<_T = unknown> implements OnChanges { + private viewContainer = inject(ViewContainerRef); + private templateRef = inject(TemplateRef); + + private embeddedViewRef: EmbeddedViewRef | null = null; + private context = new ZardStringTemplateOutletContext(); + @Input() zStringTemplateOutletContext: any | null = null; + @Input() zStringTemplateOutlet: unknown | TemplateRef = null; + + static ngTemplateContextGuard(_dir: ZardStringTemplateOutletDirective, _ctx: unknown): _ctx is ZardStringTemplateOutletContext { + return true; + } + + private recreateView(): void { + this.viewContainer.clear(); + if (isTemplateRef(this.zStringTemplateOutlet)) { + this.embeddedViewRef = this.viewContainer.createEmbeddedView(this.zStringTemplateOutlet, this.zStringTemplateOutletContext); + } else { + this.embeddedViewRef = this.viewContainer.createEmbeddedView(this.templateRef, this.context); + } + } + + private updateContext(): void { + const newCtx = isTemplateRef(this.zStringTemplateOutlet) ? this.zStringTemplateOutletContext : this.context; + const oldCtx = this.embeddedViewRef?.context as any; + if (newCtx) { + for (const propName of Object.keys(newCtx)) { + oldCtx[propName] = newCtx[propName]; + } + } + } + + ngOnChanges(changes: SimpleChanges): void { + const { zStringTemplateOutletContext, zStringTemplateOutlet } = changes; + const shouldRecreateView = (): boolean => { + let shouldOutletRecreate = false; + if (zStringTemplateOutlet) { + shouldOutletRecreate = zStringTemplateOutlet.firstChange || isTemplateRef(zStringTemplateOutlet.previousValue) || isTemplateRef(zStringTemplateOutlet.currentValue); + } + const hasContextShapeChanged = (ctxChange: SimpleChange): boolean => { + const prevCtxKeys = Object.keys(ctxChange.previousValue || {}); + const currCtxKeys = Object.keys(ctxChange.currentValue || {}); + if (prevCtxKeys.length === currCtxKeys.length) { + for (const propName of currCtxKeys) { + if (prevCtxKeys.indexOf(propName) === -1) { + return true; + } + } + return false; + } else { + return true; + } + }; + const shouldContextRecreate = zStringTemplateOutletContext && hasContextShapeChanged(zStringTemplateOutletContext); + return shouldContextRecreate || shouldOutletRecreate; + }; + + if (zStringTemplateOutlet) { + this.context.$implicit = zStringTemplateOutlet.currentValue; + } + + const recreateView = shouldRecreateView(); + if (recreateView) { + this.recreateView(); + } else { + this.updateContext(); + } + } +} + +export class ZardStringTemplateOutletContext { + public $implicit: unknown; +} diff --git a/src/app/shared/components/data-table/data-table.css b/src/app/shared/components/data-table/data-table.css new file mode 100644 index 0000000..3af9500 --- /dev/null +++ b/src/app/shared/components/data-table/data-table.css @@ -0,0 +1,5 @@ +:host { + display: block; + /* Using a custom font for better aesthetics */ + font-family: 'Inter', sans-serif; +} diff --git a/src/app/shared/components/data-table/data-table.html b/src/app/shared/components/data-table/data-table.html new file mode 100644 index 0000000..50c33c1 --- /dev/null +++ b/src/app/shared/components/data-table/data-table.html @@ -0,0 +1,245 @@ + + +@if (enableColumnChooser()) { + +
    +
    +
    + +
    + + @if (showChooser()) { + + + +
    +
    +
    + @for (c of columns(); track c.key) { + + } +
    + +
    + + +
    +
    + } +
    +} + + + +
    + + + + @if (rowActionsTpl && actionsPosition() === 'left') { + + } @for (c of effectiveColumns(); track c.key) { + + + } @if (rowActionsTpl && actionsPosition() === 'right') { + + } + + + + + @for (row of data(); track $index; let i = $index) { + + @if (rowActionsTpl && actionsPosition() === 'left') { + + } @for (c of effectiveColumns(); track c.key) { + + + } @if (rowActionsTpl && actionsPosition() === 'right') { + + } + + } @empty { + + + + } + +
    + {{ actionsHeader() }} + + @if (c.sortable) { + + } @else { + {{ c.label }} + } + + {{ actionsHeader() }} +
    + + + @if (c.cell) { + + } @else if (c.cellTemplate) { + + } @else { + {{ getNestedValue(row, c.key) }} + } + + +
    + @if (loading()) { +
    + + + + + Chargement en cours… +
    + } @else { +
    + + + + + Aucune donnée disponible + + + Essayez de modifier vos filtres ou ajoutez un nouvel enregistrement + +
    + } +
    +
    diff --git a/src/app/shared/components/data-table/data-table.spec.ts b/src/app/shared/components/data-table/data-table.spec.ts new file mode 100644 index 0000000..bdc1674 --- /dev/null +++ b/src/app/shared/components/data-table/data-table.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DataTable } from './data-table'; + +describe('DataTable', () => { + let component: DataTable; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DataTable] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DataTable); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/data-table/data-table.ts b/src/app/shared/components/data-table/data-table.ts new file mode 100644 index 0000000..005974b --- /dev/null +++ b/src/app/shared/components/data-table/data-table.ts @@ -0,0 +1,265 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ContentChild, + TemplateRef, + input, + output, + signal, + effect, + computed, +} from '@angular/core'; + +// --- Interfaces (kept outside the component class) --- +export interface TableColumn { + key: string; + label: string; + sortable?: boolean; + width?: string; + cellTemplate?: TemplateRef<{ $implicit: T }>; + cell?: (row: T, index?: number) => string | number | null | undefined; + defaultVisible?: boolean; +} + +export interface SortState { + key: string; + dir: 'asc' | 'desc' | ''; +} +// ----------------------------------------------------- + +@Component({ + selector: 'app-data-table', + standalone: true, // Assuming standalone component structure + imports: [CommonModule], + templateUrl: './data-table.html', + styleUrls: ['./data-table.css'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DataTable> { + // --- Inputs --- + columns = input[]>([]); + data = input([]); + loading = input(false); + sort = input({ key: '', dir: '' }); + enableColumnChooser = input(true); + persistenceKey = input(null); + minVisible = input(1); + visibleKeys = input(null); + actionsPosition = input<'left' | 'right' | 'none'>('right'); + actionsSticky = input(true); + actionsHeader = input('Actions'); + + // --- Outputs --- + sortChange = output(); + visibleKeysChange = output(); + + // --- Content Projection --- + @ContentChild('rowActions', { read: TemplateRef }) rowActionsTpl?: TemplateRef; + + // --- Internal State --- + protected _visible = signal([]); + protected showChooser = signal(false); + + // --- Core Reactivity: The Fix --- + /** Computed signal that filters the columns based on the current internal visible keys. */ + readonly effectiveColumns = computed[]>(() => { + const cols = this.columns() ?? []; + const vk = this._visible(); // Dependency: Updates when visible keys change + + // If no columns are defined or no keys are marked visible, return empty. + if (!cols.length || !vk || vk.length === 0) return []; + + const set = new Set(vk); + // Filter the original column list to maintain the correct order + return cols.filter((c) => set.has(c.key)); + }); + + // Computed Signal for Colspan (used for empty/loading row) + readonly colSpan = computed(() => { + const hasActions = this.rowActionsTpl && this.actionsPosition() !== 'none'; + return this.effectiveColumns().length + (hasActions ? 1 : 0); + }); + + private get isControlled(): boolean { + return this.visibleKeys() !== null && this.visibleKeys() !== undefined; + } + + constructor() { + // 1. Initialize visibility (loads defaults or persisted state) + effect( + () => { + this.columns(); // Re-run when column definition changes + this.loadInitialVisible(); + }, + { allowSignalWrites: true } + ); + + // 2. Handle controlled mode changes + effect(() => { + if (!this.isControlled) return; + const external = this.visibleKeys() ?? []; + const cols = this.columns() ?? []; + const allowed = new Set(cols.map((c) => c.key)); + const sanitized = external.filter((k) => allowed.has(k)); + + // ⬇️ Only mirror if it actually differs from our current optimistic state + if (!this.arraysEqual(this._visible(), sanitized)) { + this._visible.set(sanitized); + } + }); + + // 3. Persist and reset sort when internal state changes (in uncontrolled mode) + effect(() => { + const current = this._visible(); // dep + if (!this.isControlled) { + this.persistVisible(current); + } + this.resetSortIfHidden(); + }); + } + + private arraysEqual(a: string[], b: string[]) { + if (a === b) return true; + if (!a || !b || a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; + return true; + } + + // --- Methods --- + toggleSort(c: TableColumn) { + // ... (Sorting logic remains the same) + if (!c.sortable) return; + const currentSort = this.sort(); + let nextDir: 'asc' | 'desc' | '' = 'asc'; + if (currentSort.key === c.key) { + if (currentSort.dir === 'asc') nextDir = 'desc'; + else if (currentSort.dir === 'desc') nextDir = ''; + } + const newSortState: SortState = { key: nextDir ? c.key : '', dir: nextDir }; + this.sortChange.emit(newSortState); + } + + protected loadInitialVisible() { + const cols = this.columns() ?? []; + if (!cols.length) { + this._visible.set([]); + return; + } + + if (this.isControlled) { + const external = this.visibleKeys() ?? []; + const allowed = new Set(cols.map((c) => c.key)); + const sanitized = external.filter((k) => allowed.has(k)); + this._visible.set(sanitized); + return; + } + + const key = this.persistenceKey(); + if (key) { + try { + const raw = localStorage.getItem(key); + if (raw) { + const parsed = JSON.parse(raw) as { visibleKeys?: string[] }; + const list = (parsed?.visibleKeys ?? []).filter((k) => cols.some((c) => c.key === k)); + if (list.length) { + this._visible.set(list); + return; + } + } + } catch {} + } + + const defaults = cols.filter((c) => c.defaultVisible).map((c) => c.key); + if (defaults.length) { + this._visible.set(defaults); + return; + } + + this._visible.set(cols.map((c) => c.key)); + } + + protected persistVisible(current: string[]) { + const key = this.persistenceKey(); + if (!key) return; + try { + localStorage.setItem(key, JSON.stringify({ visibleKeys: current })); + } catch {} + } + + toggleColumn(key: string) { + const cols = this.columns() ?? []; + const allowed = new Set(cols.map((c) => c.key)); + if (!allowed.has(key)) return; + + const current = this._visible(); + const isOn = current.includes(key); + + let next: string[]; + if (isOn) { + if (current.length <= this.minVisible()) return; // keep at least minVisible + next = current.filter((k) => k !== key); + } else { + next = [...current, key]; + } + + // ⬇️ Optimistic update so the UI updates immediately + this._visible.set(next); + + if (this.isControlled) { + // Inform parent; if parent reflects it back, our effect won't stomp since arraysEqual + this.visibleKeysChange.emit(next); + } else { + // Uncontrolled: persistence handled by the effect that watches _visible() + // (nothing else to do) + } + } + + showAllColumns() { + const next = (this.columns() ?? []).map((c) => c.key); + this._visible.set(next); // optimistic + if (this.isControlled) this.visibleKeysChange.emit(next); + } + + hideAllColumnsExceptFirst() { + const cols = this.columns() ?? []; + if (!cols.length) return; + const next = [cols[0].key]; + this._visible.set(next); + if (this.isControlled) this.visibleKeysChange.emit(next); + } + + protected resetSortIfHidden() { + const s = this.sort(); + if (!s?.key) return; + const visibleSet = new Set(this._visible()); + if (!visibleSet.has(s.key)) { + this.sortChange.emit({ key: '', dir: '' }); + } + } + + getNestedValue(obj: any, path: string): any { + if (!obj || !path) return ''; + return path + .split('.') + .reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : ''), obj); + } + + actionsThClasses(): string { + const sticky = this.actionsSticky() + ? 'sticky z-10 bg-white dark:bg-gray-800/80 backdrop-blur-sm transition-all duration-300' + : ''; + return this.actionsPosition() === 'right' + ? `${sticky} right-0 px-5 py-3` + : `${sticky} left-0 px-5 py-3`; + } + + actionsTdClasses(): string { + const sticky = this.actionsSticky() + ? 'sticky z-10 bg-white dark:bg-gray-900 border-l border-gray-100 dark:border-gray-800/50' + : ''; + return this.actionsPosition() === 'right' + ? `${sticky} right-0 p-4 text-right align-middle` + : `${sticky} left-0 p-4 align-middle`; + } +} diff --git a/src/app/shared/components/date-picker/date-picker.component.ts b/src/app/shared/components/date-picker/date-picker.component.ts new file mode 100644 index 0000000..d32e7bb --- /dev/null +++ b/src/app/shared/components/date-picker/date-picker.component.ts @@ -0,0 +1,132 @@ +import { DatePipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, computed, inject, input, output, TemplateRef, viewChild, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardButtonComponent } from '../button/button.component'; +import { ZardCalendarComponent } from '../calendar/calendar.component'; +import { ZardPopoverComponent, ZardPopoverDirective } from '../popover/popover.component'; +import { datePickerVariants, ZardDatePickerVariants } from './date-picker.variants'; + +import type { ClassValue } from '@shared/utils/merge-classes'; + +export type { ZardDatePickerVariants }; + +@Component({ + selector: 'z-date-picker, [z-date-picker]', + exportAs: 'zDatePicker', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZardButtonComponent, ZardCalendarComponent, ZardPopoverComponent, ZardPopoverDirective], + host: {}, + template: ` + + + + + + + + `, + providers: [DatePipe], +}) +export class ZardDatePickerComponent { + private readonly datePipe = inject(DatePipe); + + readonly calendarTemplate = viewChild.required>('calendarTemplate'); + readonly popoverDirective = viewChild.required('popoverDirective'); + readonly calendar = viewChild.required('calendar'); + + readonly class = input(''); + readonly zType = input('outline'); + readonly zSize = input('default'); + readonly value = input(null); + readonly placeholder = input('Pick a date'); + readonly zFormat = input('MMMM d, yyyy'); + readonly minDate = input(null); + readonly maxDate = input(null); + readonly disabled = input(false); + + readonly dateChange = output(); + + protected readonly classes = computed(() => + mergeClasses( + datePickerVariants({ + zSize: this.zSize(), + }), + this.class(), + ), + ); + + protected readonly buttonClasses = computed(() => { + const hasValue = !!this.value(); + return mergeClasses( + 'justify-start text-left font-normal', + !hasValue && 'text-muted-foreground', + this.zSize() === 'sm' ? 'h-8' : this.zSize() === 'lg' ? 'h-12' : 'h-10', + 'min-w-[240px]', + ); + }); + + protected readonly textClasses = computed(() => { + const hasValue = !!this.value(); + return mergeClasses(!hasValue && 'text-muted-foreground'); + }); + + protected readonly popoverClasses = computed(() => mergeClasses('w-auto p-0')); + + protected readonly calendarSize = computed(() => { + const size = this.zSize(); + if (size === 'sm') return 'sm'; + if (size === 'lg') return 'lg'; + return 'default'; + }); + + protected readonly displayText = computed(() => { + const date = this.value(); + if (!date) { + return this.placeholder(); + } + return this.formatDate(date, this.zFormat()); + }); + + protected onDateChange(date: Date): void { + this.dateChange.emit(date); + // Close popover after selection using direct method call + this.popoverDirective().hide(); + } + + protected onPopoverVisibilityChange(visible: boolean): void { + if (visible) { + // Reset calendar navigation when opening to show correct month/year + setTimeout(() => { + if (this.calendar()) { + this.calendar().resetNavigation(); + } + }); + } + } + + private formatDate(date: Date, format: string): string { + return this.datePipe.transform(date, format) || ''; + } +} \ No newline at end of file diff --git a/src/app/shared/components/date-picker/date-picker.variants.ts b/src/app/shared/components/date-picker/date-picker.variants.ts new file mode 100644 index 0000000..a28f1b8 --- /dev/null +++ b/src/app/shared/components/date-picker/date-picker.variants.ts @@ -0,0 +1,23 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +const datePickerVariants = cva('', { + variants: { + zSize: { + sm: '', + default: '', + lg: '', + }, + zType: { + default: '', + outline: '', + ghost: '', + }, + }, + defaultVariants: { + zSize: 'default', + zType: 'outline', + }, +}); + +export { datePickerVariants }; +export type ZardDatePickerVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/dialog/dialog-ref.ts b/src/app/shared/components/dialog/dialog-ref.ts new file mode 100644 index 0000000..732946c --- /dev/null +++ b/src/app/shared/components/dialog/dialog-ref.ts @@ -0,0 +1,89 @@ +import { filter, fromEvent, Subject, takeUntil } from 'rxjs'; + +import { OverlayRef } from '@angular/cdk/overlay'; +import { isPlatformBrowser } from '@angular/common'; +import { EventEmitter, Inject, PLATFORM_ID } from '@angular/core'; + +import { ZardDialogComponent, ZardDialogOptions } from './dialog.component'; + +const enum eTriggerAction { + CANCEL = 'cancel', + OK = 'ok', +} + +export class ZardDialogRef { + private destroy$ = new Subject(); + private isClosing = false; + protected result?: R; + componentInstance: T | null = null; + + constructor( + private overlayRef: OverlayRef, + private config: ZardDialogOptions, + private containerInstance: ZardDialogComponent, + @Inject(PLATFORM_ID) private platformId: object, + ) { + this.containerInstance.cancelTriggered.subscribe(() => this.trigger(eTriggerAction.CANCEL)); + this.containerInstance.okTriggered.subscribe(() => this.trigger(eTriggerAction.OK)); + + if ((this.config.zMaskClosable ?? true) && isPlatformBrowser(this.platformId)) { + this.overlayRef + .outsidePointerEvents() + .pipe(takeUntil(this.destroy$)) + .subscribe(() => this.close()); + } + + if (isPlatformBrowser(this.platformId)) { + fromEvent(document, 'keydown') + .pipe( + filter(event => event.key === 'Escape'), + takeUntil(this.destroy$), + ) + .subscribe(() => this.close()); + } + } + + close(result?: R) { + if (this.isClosing) { + return; + } + + this.isClosing = true; + this.result = result; + + this.containerInstance.state.set('close'); + + setTimeout(() => { + if (this.overlayRef) { + if (this.overlayRef.hasAttached()) { + this.overlayRef.detachBackdrop(); + } + this.overlayRef.dispose(); + } + + if (!this.destroy$.closed) { + this.destroy$.next(); + this.destroy$.complete(); + } + }, 150); + } + + private trigger(action: eTriggerAction) { + const trigger = { ok: this.config.zOnOk, cancel: this.config.zOnCancel }[action]; + + if (trigger instanceof EventEmitter) { + trigger.emit(this.getContentComponent()); + } else if (typeof trigger === 'function') { + const result = trigger(this.getContentComponent()) as R; + this.closeWithResult(result); + } else this.close(); + } + + private getContentComponent(): T { + return this.componentInstance as T; + } + + private closeWithResult(result: R): void { + if (result !== false) this.close(result); + } +} 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..ac3032c --- /dev/null +++ b/src/app/shared/components/dialog/dialog.component.ts @@ -0,0 +1,172 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { OverlayModule } from '@angular/cdk/overlay'; +import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, PortalModule, TemplatePortal } from '@angular/cdk/portal'; +import { + ChangeDetectionStrategy, + Component, + ComponentRef, + computed, + ElementRef, + EmbeddedViewRef, + EventEmitter, + inject, + NgModule, + output, + signal, + TemplateRef, + Type, + viewChild, + ViewContainerRef, +} from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardButtonComponent } from '../button/button.component'; +import { ZardDialogRef } from './dialog-ref'; +import { ZardDialogService } from './dialog.service'; +import { dialogVariants } from './dialog.variants'; + +const noopFun = () => void 0; +export type OnClickCallback = (instance: T) => false | void | object; +export class ZardDialogOptions { + zCancelIcon?: string; + zCancelText?: string | null; + zClosable?: boolean; + zContent?: string | TemplateRef | Type; + zCustomClasses?: string; + zData?: U; + zDescription?: string; + zHideFooter?: boolean; + zMaskClosable?: boolean; + zOkDestructive?: boolean; + zOkDisabled?: boolean; + zOkIcon?: string; + zOkText?: string | null; + zOnCancel?: EventEmitter | OnClickCallback = noopFun; + zOnOk?: EventEmitter | OnClickCallback = noopFun; + zTitle?: string | TemplateRef; + zViewContainerRef?: ViewContainerRef; + zWidth?: string; +} + +@Component({ + selector: 'z-dialog', + exportAs: 'zDialog', + imports: [OverlayModule, PortalModule, ZardButtonComponent], + template: ` + @if (config.zClosable || config.zClosable === undefined) { + + } + + @if (config.zTitle || config.zDescription) { +
    + @if (config.zTitle) { +

    {{ config.zTitle }}

    + + @if (config.zDescription) { +

    {{ config.zDescription }}

    + } + } +
    + } + +
    + + + @if (isStringContent) { +
    + } +
    + + @if (!config.zHideFooter) { +
    + @if (config.zCancelText !== null) { + + } + + @if (config.zOkText !== null) { + + } +
    + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + '[class]': 'classes()', + '[@dialogAnimation]': 'state()', + '[style.width]': 'config.zWidth ? config.zWidth : null', + }, + animations: [ + trigger('dialogAnimation', [ + state('close', style({ opacity: 0, transform: 'scale(0.9)' })), + state('open', style({ opacity: 1, transform: 'scale(1)' })), + transition('close => open', animate('150ms ease-out')), + transition('open => close', animate('150ms ease-in')), + ]), + ], +}) +export class ZardDialogComponent extends BasePortalOutlet { + private readonly host = inject(ElementRef); + protected readonly config = inject(ZardDialogOptions); + + protected readonly classes = computed(() => mergeClasses(dialogVariants(), this.config.zCustomClasses)); + public dialogRef?: ZardDialogRef; + + protected readonly isStringContent = typeof this.config.zContent === 'string'; + + readonly portalOutlet = viewChild.required(CdkPortalOutlet); + + okTriggered = output(); + cancelTriggered = output(); + state = signal<'close' | 'open'>('close'); + + constructor() { + super(); + } + + getNativeElement(): HTMLElement { + return this.host.nativeElement; + } + + attachComponentPortal(portal: ComponentPortal): ComponentRef { + if (this.portalOutlet()?.hasAttached()) { + throw Error('Attempting to attach modal content after content is already attached'); + } + return this.portalOutlet()?.attachComponentPortal(portal); + } + + attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { + if (this.portalOutlet()?.hasAttached()) { + throw Error('Attempting to attach modal content after content is already attached'); + } + + return this.portalOutlet()?.attachTemplatePortal(portal); + } + + onOkClick() { + this.okTriggered.emit(); + } + + onCloseClick() { + this.cancelTriggered.emit(); + } +} + +@NgModule({ + imports: [ZardButtonComponent, ZardDialogComponent, OverlayModule, PortalModule], + providers: [ZardDialogService], +}) +export class ZardDialogModule {} diff --git a/src/app/shared/components/dialog/dialog.service.ts b/src/app/shared/components/dialog/dialog.service.ts new file mode 100644 index 0000000..a2a428f --- /dev/null +++ b/src/app/shared/components/dialog/dialog.service.ts @@ -0,0 +1,99 @@ +import { ComponentType, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal'; +import { isPlatformBrowser } from '@angular/common'; +import { inject, Injectable, InjectionToken, Injector, PLATFORM_ID, TemplateRef } from '@angular/core'; + +import { ZardDialogRef } from './dialog-ref'; +import { ZardDialogComponent, ZardDialogOptions } from './dialog.component'; + +type ContentType = ComponentType | TemplateRef | string; +export const Z_MODAL_DATA = new InjectionToken('Z_MODAL_DATA'); + +@Injectable({ + providedIn: 'root', +}) +export class ZardDialogService { + private overlay = inject(Overlay); + private injector = inject(Injector); + private platformId = inject(PLATFORM_ID); + + create(config: ZardDialogOptions): ZardDialogRef { + return this.open(config.zContent as ComponentType, config); + } + + private open(componentOrTemplateRef: ContentType, config: ZardDialogOptions) { + const overlayRef = this.createOverlay(); + + if (!overlayRef) { + // Return a mock dialog ref for SSR environments + return new ZardDialogRef(undefined as any, config, undefined as any, this.platformId); + } + + const dialogContainer = this.attachDialogContainer(overlayRef, config); + + const dialogRef = this.attachDialogContent(componentOrTemplateRef, dialogContainer, overlayRef, config); + dialogContainer.dialogRef = dialogRef; + + return dialogRef; + } + + private createOverlay(): OverlayRef | undefined { + if (isPlatformBrowser(this.platformId)) { + const overlayConfig = new OverlayConfig({ + hasBackdrop: true, + positionStrategy: this.overlay.position().global(), + }); + + return this.overlay.create(overlayConfig); + } + return undefined; + } + + private attachDialogContainer(overlayRef: OverlayRef, config: ZardDialogOptions) { + const injector = Injector.create({ + parent: this.injector, + providers: [ + { provide: OverlayRef, useValue: overlayRef }, + { provide: ZardDialogOptions, useValue: config }, + ], + }); + + const containerPortal = new ComponentPortal>(ZardDialogComponent, config.zViewContainerRef, injector); + const containerRef = overlayRef.attach>(containerPortal); + + setTimeout(() => { + containerRef.instance.state.set('open'); + }, 0); + + return containerRef.instance; + } + + private attachDialogContent(componentOrTemplateRef: ContentType, dialogContainer: ZardDialogComponent, overlayRef: OverlayRef, config: ZardDialogOptions) { + const dialogRef = new ZardDialogRef(overlayRef, config, dialogContainer, this.platformId); + + if (componentOrTemplateRef instanceof TemplateRef) { + dialogContainer.attachTemplatePortal( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + new TemplatePortal(componentOrTemplateRef, null!, { + dialogRef: dialogRef, + } as any), + ); + } else if (typeof componentOrTemplateRef !== 'string') { + const injector = this.createInjector(dialogRef, config); + const contentRef = dialogContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, config.zViewContainerRef, injector)); + dialogRef.componentInstance = contentRef.instance; + } + + return dialogRef; + } + + private createInjector(dialogRef: ZardDialogRef, config: ZardDialogOptions) { + return Injector.create({ + parent: this.injector, + providers: [ + { provide: ZardDialogRef, useValue: dialogRef }, + { provide: Z_MODAL_DATA, useValue: config.zData }, + ], + }); + } +} diff --git a/src/app/shared/components/dialog/dialog.variants.ts b/src/app/shared/components/dialog/dialog.variants.ts new file mode 100644 index 0000000..011b01c --- /dev/null +++ b/src/app/shared/components/dialog/dialog.variants.ts @@ -0,0 +1,6 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const dialogVariants = cva( + 'fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg rounded-lg max-w-[calc(100%-2rem)] sm:max-w-[425px]', +); +export type ZardDialogVariants = VariantProps; diff --git a/src/app/shared/components/divider/divider.component.ts b/src/app/shared/components/divider/divider.component.ts new file mode 100644 index 0000000..5138c8b --- /dev/null +++ b/src/app/shared/components/divider/divider.component.ts @@ -0,0 +1,34 @@ +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; +import type { ClassValue } from 'clsx'; + +import { dividerVariants, ZardDividerVariants } from './divider.variants'; +import { mergeClasses } from '@shared/utils/merge-classes'; + +@Component({ + selector: 'z-divider', + standalone: true, + exportAs: 'zDivider', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: '', + host: { + '[attr.role]': `'separator'`, + '[attr.aria-orientation]': 'zOrientation()', + '[class]': 'classes()', + }, +}) +export class ZardDividerComponent { + readonly zOrientation = input('horizontal'); + readonly zSpacing = input('default'); + readonly class = input(''); + + protected readonly classes = computed(() => + mergeClasses( + dividerVariants({ + zOrientation: this.zOrientation(), + zSpacing: this.zSpacing(), + }), + this.class(), + ), + ); +} \ No newline at end of file diff --git a/src/app/shared/components/divider/divider.variants.ts b/src/app/shared/components/divider/divider.variants.ts new file mode 100644 index 0000000..695b38e --- /dev/null +++ b/src/app/shared/components/divider/divider.variants.ts @@ -0,0 +1,54 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const dividerVariants = cva('bg-border block', { + variants: { + zOrientation: { + horizontal: 'h-px w-full', + vertical: 'w-px h-full inline-block', + }, + zSpacing: { + none: '', + sm: '', + default: '', + lg: '', + }, + }, + defaultVariants: { + zOrientation: 'horizontal', + zSpacing: 'default', + }, + compoundVariants: [ + { + zOrientation: 'horizontal', + zSpacing: 'sm', + class: 'my-2', + }, + { + zOrientation: 'horizontal', + zSpacing: 'default', + class: 'my-4', + }, + { + zOrientation: 'horizontal', + zSpacing: 'lg', + class: 'my-8', + }, + { + zOrientation: 'vertical', + zSpacing: 'sm', + class: 'mx-2', + }, + { + zOrientation: 'vertical', + zSpacing: 'default', + class: 'mx-4', + }, + { + zOrientation: 'vertical', + zSpacing: 'lg', + class: 'mx-8', + }, + ], +}); + +export type ZardDividerVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/dropdown/dropdown-item.component.ts b/src/app/shared/components/dropdown/dropdown-item.component.ts new file mode 100644 index 0000000..c5305e4 --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown-item.component.ts @@ -0,0 +1,56 @@ +import type { ClassValue } from 'clsx'; + +import { Component, computed, HostListener, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { ZardDropdownService } from './dropdown.service'; +import { dropdownItemVariants, ZardDropdownItemVariants } from './dropdown.variants'; + +@Component({ + selector: 'z-dropdown-menu-item, [z-dropdown-menu-item]', + exportAs: 'zDropdownMenuItem', + standalone: true, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + '[attr.data-disabled]': 'disabled() || null', + '[attr.data-variant]': 'variant()', + '[attr.data-inset]': 'inset() || null', + '[attr.aria-disabled]': 'disabled()', + role: 'menuitem', + tabindex: '-1', + }, +}) +export class ZardDropdownMenuItemComponent { + private dropdownService = inject(ZardDropdownService); + + readonly variant = input('default'); + readonly inset = input(false, { transform }); + readonly disabled = input(false, { transform }); + readonly class = input(''); + + @HostListener('click', ['$event']) + onClick(event: Event) { + if (this.disabled()) { + event.preventDefault(); + event.stopPropagation(); + return; + } + + // Fechar dropdown após click + setTimeout(() => { + this.dropdownService.close(); + }, 0); + } + + protected readonly classes = computed(() => + mergeClasses( + dropdownItemVariants({ + variant: this.variant(), + inset: this.inset(), + }), + this.class(), + ), + ); +} diff --git a/src/app/shared/components/dropdown/dropdown-label.component.ts b/src/app/shared/components/dropdown/dropdown-label.component.ts new file mode 100644 index 0000000..c0682e6 --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown-label.component.ts @@ -0,0 +1,31 @@ +import type { ClassValue } from 'clsx'; + +import { Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { dropdownLabelVariants } from './dropdown.variants'; + +@Component({ + selector: 'z-dropdown-menu-label, [z-dropdown-menu-label]', + exportAs: 'zDropdownMenuLabel', + standalone: true, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + '[attr.data-inset]': 'inset() || null', + }, +}) +export class ZardDropdownMenuLabelComponent { + readonly inset = input(false, { transform }); + readonly class = input(''); + + protected readonly classes = computed(() => + mergeClasses( + dropdownLabelVariants({ + inset: this.inset(), + }), + this.class(), + ), + ); +} diff --git a/src/app/shared/components/dropdown/dropdown-menu-content.component.ts b/src/app/shared/components/dropdown/dropdown-menu-content.component.ts new file mode 100644 index 0000000..76abf6d --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown-menu-content.component.ts @@ -0,0 +1,27 @@ +import type { ClassValue } from 'clsx'; + +import { Component, computed, input, TemplateRef, viewChild, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { dropdownContentVariants } from './dropdown.variants'; + +@Component({ + selector: 'z-dropdown-menu-content', + exportAs: 'zDropdownMenuContent', + standalone: true, + encapsulation: ViewEncapsulation.None, + template: ` + +
    + +
    +
    + `, +}) +export class ZardDropdownMenuContentComponent { + readonly contentTemplate = viewChild.required>('contentTemplate'); + + readonly class = input(''); + + protected readonly contentClasses = computed(() => mergeClasses(dropdownContentVariants(), this.class())); +} diff --git a/src/app/shared/components/dropdown/dropdown-shortcut.component.ts b/src/app/shared/components/dropdown/dropdown-shortcut.component.ts new file mode 100644 index 0000000..51735ad --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown-shortcut.component.ts @@ -0,0 +1,22 @@ +import type { ClassValue } from 'clsx'; + +import { Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { dropdownShortcutVariants } from './dropdown.variants'; + +@Component({ + selector: 'z-dropdown-menu-shortcut, [z-dropdown-menu-shortcut]', + exportAs: 'zDropdownMenuShortcut', + standalone: true, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardDropdownMenuShortcutComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(dropdownShortcutVariants(), this.class())); +} diff --git a/src/app/shared/components/dropdown/dropdown-trigger.directive.ts b/src/app/shared/components/dropdown/dropdown-trigger.directive.ts new file mode 100644 index 0000000..29d0f84 --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown-trigger.directive.ts @@ -0,0 +1,101 @@ +import { Directive, ElementRef, HostListener, inject, input, OnInit, ViewContainerRef } from '@angular/core'; + +import { ZardDropdownMenuContentComponent } from './dropdown-menu-content.component'; +import { ZardDropdownService } from './dropdown.service'; + +@Directive({ + selector: '[z-dropdown], [zDropdown]', + exportAs: 'zDropdown', + standalone: true, + host: { + '[attr.tabindex]': '0', + '[attr.role]': '"button"', + '[attr.aria-haspopup]': '"menu"', + '[attr.aria-expanded]': 'dropdownService.isOpen()', + '[attr.aria-disabled]': 'zDisabled()', + }, +}) +export class ZardDropdownDirective implements OnInit { + private elementRef = inject(ElementRef); + private viewContainerRef = inject(ViewContainerRef); + protected dropdownService = inject(ZardDropdownService); + + readonly zDropdownMenu = input(); + readonly zTrigger = input<'click' | 'hover'>('click'); + readonly zDisabled = input(false); + + ngOnInit() { + // Ensure button has proper accessibility attributes + const element = this.elementRef.nativeElement; + if (!element.hasAttribute('aria-label') && !element.hasAttribute('aria-labelledby')) { + element.setAttribute('aria-label', element.textContent?.trim() || 'Open menu'); + } + } + + @HostListener('click', ['$event']) + onClick(event: Event) { + if (this.zDisabled() || this.zTrigger() !== 'click') return; + + event.preventDefault(); + event.stopPropagation(); + + const menuContent = this.zDropdownMenu(); + if (menuContent) { + this.dropdownService.toggle(this.elementRef, menuContent?.contentTemplate?.(), this.viewContainerRef); + } + } + + @HostListener('mouseenter') + onMouseEnter() { + if (this.zDisabled() || this.zTrigger() !== 'hover') return; + + const menuContent = this.zDropdownMenu(); + if (menuContent) { + this.dropdownService.open(this.elementRef, menuContent?.contentTemplate?.(), this.viewContainerRef); + } + } + + @HostListener('mouseleave') + onMouseLeave() { + if (this.zDisabled() || this.zTrigger() !== 'hover') return; + + this.dropdownService.close(); + } + + @HostListener('keydown', ['$event']) + onKeydown(event: KeyboardEvent) { + if (this.zDisabled()) return; + + switch (event.key) { + case 'Enter': + case ' ': + event.preventDefault(); + event.stopPropagation(); + this.toggleDropdown(); + break; + case 'ArrowDown': + event.preventDefault(); + this.openDropdown(); + break; + case 'Escape': + event.preventDefault(); + this.dropdownService.close(); + this.elementRef.nativeElement.focus(); + break; + } + } + + private toggleDropdown() { + const menuContent = this.zDropdownMenu(); + if (menuContent) { + this.dropdownService.toggle(this.elementRef, menuContent?.contentTemplate?.(), this.viewContainerRef); + } + } + + private openDropdown() { + const menuContent = this.zDropdownMenu(); + if (menuContent && !this.dropdownService.isOpen()) { + this.dropdownService.open(this.elementRef, menuContent?.contentTemplate?.(), this.viewContainerRef); + } + } +} diff --git a/src/app/shared/components/dropdown/dropdown.component.ts b/src/app/shared/components/dropdown/dropdown.component.ts new file mode 100644 index 0000000..3d8cf6a --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown.component.ts @@ -0,0 +1,279 @@ +import { Overlay, OverlayModule, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { + ChangeDetectionStrategy, + Component, + computed, + ElementRef, + HostListener, + inject, + input, + OnDestroy, + OnInit, + output, + PLATFORM_ID, + signal, + TemplateRef, + viewChild, + ViewContainerRef, + ViewEncapsulation, +} from '@angular/core'; +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { dropdownContentVariants } from './dropdown.variants'; + +import type { ClassValue } from 'clsx'; +import { isPlatformBrowser } from '@angular/common'; +@Component({ + selector: 'z-dropdown-menu', + exportAs: 'zDropdownMenu', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [OverlayModule], + host: { + '[attr.data-state]': 'isOpen() ? "open" : "closed"', + class: 'relative inline-block text-left', + }, + template: ` + +
    + +
    + + + +
    + +
    +
    + `, +}) +export class ZardDropdownMenuComponent implements OnInit, OnDestroy { + private elementRef = inject(ElementRef); + private overlay = inject(Overlay); + private overlayPositionBuilder = inject(OverlayPositionBuilder); + private viewContainerRef = inject(ViewContainerRef); + private platformId = inject(PLATFORM_ID); + + readonly dropdownTemplate = viewChild.required>('dropdownTemplate'); + + private overlayRef?: OverlayRef; + private portal?: TemplatePortal; + + readonly class = input(''); + readonly disabled = input(false, { transform }); + + readonly openChange = output(); + + readonly isOpen = signal(false); + readonly focusedIndex = signal(-1); + + protected readonly contentClasses = computed(() => mergeClasses(dropdownContentVariants(), this.class())); + + ngOnInit() { + setTimeout(() => { + this.createOverlay(); + }); + } + + ngOnDestroy() { + this.destroyOverlay(); + } + + @HostListener('document:click', ['$event']) + onDocumentClick(event: Event) { + if (!this.elementRef.nativeElement.contains(event.target as Node)) { + this.close(); + } + } + + onDropdownKeydown(event: KeyboardEvent) { + const items = this.getDropdownItems(); + + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + this.navigateItems(1, items); + break; + case 'ArrowUp': + event.preventDefault(); + this.navigateItems(-1, items); + break; + case 'Enter': + case ' ': + event.preventDefault(); + this.selectFocusedItem(items); + break; + case 'Escape': + event.preventDefault(); + this.close(); + this.focusTrigger(); + break; + case 'Home': + event.preventDefault(); + this.focusFirstItem(items); + break; + case 'End': + event.preventDefault(); + this.focusLastItem(items); + break; + } + } + + toggle() { + if (this.disabled()) return; + if (this.isOpen()) { + this.close(); + } else { + this.open(); + } + } + + open() { + if (this.isOpen()) return; + + if (!this.overlayRef) { + this.createOverlay(); + } + + if (!this.overlayRef) return; + + this.portal = new TemplatePortal(this.dropdownTemplate(), this.viewContainerRef); + this.overlayRef.attach(this.portal); + this.isOpen.set(true); + this.openChange.emit(true); + + setTimeout(() => { + this.focusDropdown(); + this.focusFirstItem(this.getDropdownItems()); + }, 0); + } + + close() { + if (this.overlayRef?.hasAttached()) { + this.overlayRef.detach(); + } + this.isOpen.set(false); + this.focusedIndex.set(-1); + this.openChange.emit(false); + } + + private createOverlay() { + if (this.overlayRef) return; + + if (isPlatformBrowser(this.platformId)) { + try { + const positionStrategy = this.overlayPositionBuilder + .flexibleConnectedTo(this.elementRef) + .withPositions([ + { + originX: 'start', + originY: 'bottom', + overlayX: 'start', + overlayY: 'top', + offsetY: 4, + }, + { + originX: 'start', + originY: 'top', + overlayX: 'start', + overlayY: 'bottom', + offsetY: -4, + }, + ]) + .withPush(false); + + this.overlayRef = this.overlay.create({ + positionStrategy, + hasBackdrop: false, + scrollStrategy: this.overlay.scrollStrategies.reposition(), + minWidth: 200, + maxHeight: 400, + }); + } catch (error) { + console.error('Error creating overlay:', error); + } + } + } + + private destroyOverlay() { + if (this.overlayRef) { + this.overlayRef.dispose(); + this.overlayRef = undefined; + } + } + + private getDropdownItems(): HTMLElement[] { + if (!this.overlayRef?.hasAttached()) return []; + const dropdownElement = this.overlayRef.overlayElement; + return Array.from(dropdownElement.querySelectorAll('z-dropdown-menu-item, [z-dropdown-menu-item]')).filter( + (item: Element) => !item.hasAttribute('data-disabled'), + ) as HTMLElement[]; + } + + private navigateItems(direction: number, items: HTMLElement[]) { + if (items.length === 0) return; + + const currentIndex = this.focusedIndex(); + let nextIndex = currentIndex + direction; + + if (nextIndex < 0) { + nextIndex = items.length - 1; + } else if (nextIndex >= items.length) { + nextIndex = 0; + } + + this.focusedIndex.set(nextIndex); + this.updateItemFocus(items, nextIndex); + } + + private selectFocusedItem(items: HTMLElement[]) { + const currentIndex = this.focusedIndex(); + if (currentIndex >= 0 && currentIndex < items.length) { + const item = items[currentIndex]; + item.click(); + } + } + + private focusFirstItem(items: HTMLElement[]) { + if (items.length > 0) { + this.focusedIndex.set(0); + this.updateItemFocus(items, 0); + } + } + + private focusLastItem(items: HTMLElement[]) { + if (items.length > 0) { + const lastIndex = items.length - 1; + this.focusedIndex.set(lastIndex); + this.updateItemFocus(items, lastIndex); + } + } + + private updateItemFocus(items: HTMLElement[], focusedIndex: number) { + items.forEach((item, index) => { + if (index === focusedIndex) { + item.focus(); + item.setAttribute('data-highlighted', ''); + } else { + item.removeAttribute('data-highlighted'); + } + }); + } + + private focusDropdown() { + if (this.overlayRef?.hasAttached()) { + const dropdownElement = this.overlayRef.overlayElement.querySelector('[role="menu"]') as HTMLElement; + if (dropdownElement) { + dropdownElement.focus(); + } + } + } + + private focusTrigger() { + const trigger = this.elementRef.nativeElement.querySelector('.trigger-container'); + if (trigger) { + trigger.focus(); + } + } +} diff --git a/src/app/shared/components/dropdown/dropdown.module.ts b/src/app/shared/components/dropdown/dropdown.module.ts new file mode 100644 index 0000000..26742c8 --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown.module.ts @@ -0,0 +1,24 @@ +import { OverlayModule } from '@angular/cdk/overlay'; +import { NgModule } from '@angular/core'; + +import { ZardDropdownMenuContentComponent } from './dropdown-menu-content.component'; +import { ZardDropdownMenuShortcutComponent } from './dropdown-shortcut.component'; +import { ZardDropdownMenuLabelComponent } from './dropdown-label.component'; +import { ZardDropdownMenuItemComponent } from './dropdown-item.component'; +import { ZardDropdownDirective } from './dropdown-trigger.directive'; +import { ZardDropdownMenuComponent } from './dropdown.component'; + +const DROPDOWN_COMPONENTS = [ + ZardDropdownMenuComponent, + ZardDropdownMenuItemComponent, + ZardDropdownMenuLabelComponent, + ZardDropdownMenuShortcutComponent, + ZardDropdownMenuContentComponent, + ZardDropdownDirective, +]; + +@NgModule({ + imports: [OverlayModule, ...DROPDOWN_COMPONENTS], + exports: [...DROPDOWN_COMPONENTS], +}) +export class ZardDropdownModule {} diff --git a/src/app/shared/components/dropdown/dropdown.service.ts b/src/app/shared/components/dropdown/dropdown.service.ts new file mode 100644 index 0000000..8945692 --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown.service.ts @@ -0,0 +1,203 @@ +import { Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { ElementRef, inject, Injectable, PLATFORM_ID, signal, TemplateRef, ViewContainerRef } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; + +@Injectable({ + providedIn: 'root', +}) +export class ZardDropdownService { + private overlay = inject(Overlay); + private overlayPositionBuilder = inject(OverlayPositionBuilder); + private platformId = inject(PLATFORM_ID); + + private overlayRef?: OverlayRef; + private portal?: TemplatePortal; + private triggerElement?: ElementRef; + private focusedIndex = signal(-1); + + readonly isOpen = signal(false); + + toggle(triggerElement: ElementRef, template: TemplateRef, viewContainerRef: ViewContainerRef) { + if (this.isOpen()) { + this.close(); + } else { + this.open(triggerElement, template, viewContainerRef); + } + } + + open(triggerElement: ElementRef, template: TemplateRef, viewContainerRef: ViewContainerRef) { + if (this.isOpen()) { + this.close(); + } + + this.triggerElement = triggerElement; + this.createOverlay(triggerElement); + + if (!this.overlayRef) return; + + this.portal = new TemplatePortal(template, viewContainerRef); + this.overlayRef.attach(this.portal); + this.isOpen.set(true); + + // Setup keyboard navigation + setTimeout(() => { + this.setupKeyboardNavigation(); + this.focusFirstItem(); + }, 0); + + // Close on outside click + this.overlayRef.outsidePointerEvents().subscribe(() => { + this.close(); + }); + } + + close() { + if (this.overlayRef?.hasAttached()) { + this.overlayRef.detach(); + } + this.isOpen.set(false); + this.focusedIndex.set(-1); + this.destroyOverlay(); + } + + private createOverlay(triggerElement: ElementRef) { + if (this.overlayRef) { + this.destroyOverlay(); + } + + const positionStrategy = this.overlayPositionBuilder + .flexibleConnectedTo(triggerElement) + .withPositions([ + { + originX: 'start', + originY: 'bottom', + overlayX: 'start', + overlayY: 'top', + offsetY: 4, + }, + { + originX: 'start', + originY: 'top', + overlayX: 'start', + overlayY: 'bottom', + offsetY: -4, + }, + ]) + .withPush(false); + + this.overlayRef = this.overlay.create({ + positionStrategy, + hasBackdrop: false, + scrollStrategy: this.overlay.scrollStrategies.reposition(), + minWidth: 200, + maxHeight: 400, + }); + } + + private destroyOverlay() { + if (this.overlayRef) { + this.overlayRef.dispose(); + this.overlayRef = undefined; + } + } + + private setupKeyboardNavigation() { + if (!this.overlayRef?.hasAttached() || !isPlatformBrowser(this.platformId)) return; + + const dropdownElement = this.overlayRef.overlayElement.querySelector('[role="menu"]') as HTMLElement; + if (!dropdownElement) return; + + dropdownElement.addEventListener('keydown', (event: KeyboardEvent) => { + const items = this.getDropdownItems(); + + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + this.navigateItems(1, items); + break; + case 'ArrowUp': + event.preventDefault(); + this.navigateItems(-1, items); + break; + case 'Enter': + case ' ': + event.preventDefault(); + this.selectFocusedItem(items); + break; + case 'Escape': + event.preventDefault(); + this.close(); + this.triggerElement?.nativeElement.focus(); + break; + case 'Home': + event.preventDefault(); + this.focusItemAtIndex(items, 0); + break; + case 'End': + event.preventDefault(); + this.focusItemAtIndex(items, items.length - 1); + break; + } + }); + + // Focus dropdown container + dropdownElement.focus(); + } + + private getDropdownItems(): HTMLElement[] { + if (!this.overlayRef?.hasAttached()) return []; + const dropdownElement = this.overlayRef.overlayElement; + return Array.from(dropdownElement.querySelectorAll('z-dropdown-menu-item, [z-dropdown-menu-item]')).filter( + (item: Element) => !item.hasAttribute('data-disabled'), + ) as HTMLElement[]; + } + + private navigateItems(direction: number, items: HTMLElement[]) { + if (items.length === 0) return; + + const currentIndex = this.focusedIndex(); + let nextIndex = currentIndex + direction; + + if (nextIndex < 0) { + nextIndex = items.length - 1; + } else if (nextIndex >= items.length) { + nextIndex = 0; + } + + this.focusItemAtIndex(items, nextIndex); + } + + private focusItemAtIndex(items: HTMLElement[], index: number) { + if (index >= 0 && index < items.length) { + this.focusedIndex.set(index); + this.updateItemFocus(items, index); + } + } + + private focusFirstItem() { + const items = this.getDropdownItems(); + if (items.length > 0) { + this.focusItemAtIndex(items, 0); + } + } + + private selectFocusedItem(items: HTMLElement[]) { + const currentIndex = this.focusedIndex(); + if (currentIndex >= 0 && currentIndex < items.length) { + const item = items[currentIndex]; + item.click(); + } + } + + private updateItemFocus(items: HTMLElement[], focusedIndex: number) { + items.forEach((item, index) => { + if (index === focusedIndex) { + item.focus(); + item.setAttribute('data-highlighted', ''); + } else { + item.removeAttribute('data-highlighted'); + } + }); + } +} diff --git a/src/app/shared/components/dropdown/dropdown.variants.ts b/src/app/shared/components/dropdown/dropdown.variants.ts new file mode 100644 index 0000000..41107a9 --- /dev/null +++ b/src/app/shared/components/dropdown/dropdown.variants.ts @@ -0,0 +1,40 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const dropdownContentVariants = cva('bg-popover text-popover-foreground z-50 min-w-[200px] overflow-y-auto rounded-md border py-1 px-1 shadow-md'); + +export const dropdownItemVariants = cva( + 'relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', + { + variants: { + variant: { + default: '', + destructive: 'text-destructive hover:bg-destructive/10 focus:bg-destructive/10 dark:hover:bg-destructive/20 dark:focus:bg-destructive/20 focus:text-destructive', + }, + inset: { + true: 'pl-8', + false: '', + }, + }, + defaultVariants: { + variant: 'default', + inset: false, + }, + }, +); + +export const dropdownLabelVariants = cva('relative flex items-center px-2 py-1.5 text-sm font-medium text-muted-foreground', { + variants: { + inset: { + true: 'pl-8', + false: '', + }, + }, + defaultVariants: { + inset: false, + }, +}); + +export const dropdownShortcutVariants = cva('ml-auto text-xs tracking-widest text-muted-foreground'); + +export type ZardDropdownItemVariants = VariantProps; +export type ZardDropdownLabelVariants = VariantProps; diff --git a/src/app/shared/components/empty/empty.component.ts b/src/app/shared/components/empty/empty.component.ts new file mode 100644 index 0000000..b29709f --- /dev/null +++ b/src/app/shared/components/empty/empty.component.ts @@ -0,0 +1,66 @@ +import { ChangeDetectionStrategy, Component, computed, input, TemplateRef, ViewEncapsulation } from '@angular/core'; +import type { ClassValue } from 'clsx'; + +import { ZardStringTemplateOutletDirective } from '../core/directives/string-template-outlet/string-template-outlet.directive'; +import { emptyVariants, ZardEmptyVariants } from './empty.variants'; +import { mergeClasses } from '@shared/utils/merge-classes'; + +@Component({ + selector: 'z-empty', + exportAs: 'zEmpty', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZardStringTemplateOutletDirective], + host: { + '[class]': 'classes()', + }, + template: ` + @if (zImage()) { + @if (isTemplate(zImage())) { +
    + +
    + } @else { + Empty + } + } @else { + +
    + + + + + + + + + +
    + } + @if (zDescription()) { +
    + @if (isTemplate(zDescription())) { + + } @else { +

    {{ zDescription() }}

    + } +
    + } + `, +}) +export class ZardEmptyComponent { + readonly zImage = input>(); + readonly zDescription = input>('No data'); + readonly zSize = input('default'); + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(emptyVariants({ zSize: this.zSize() }), this.class())); + + isTemplate(value: string | TemplateRef | undefined): value is TemplateRef { + return value instanceof TemplateRef; + } +} diff --git a/src/app/shared/components/empty/empty.variants.ts b/src/app/shared/components/empty/empty.variants.ts new file mode 100644 index 0000000..16259c8 --- /dev/null +++ b/src/app/shared/components/empty/empty.variants.ts @@ -0,0 +1,16 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const emptyVariants = cva('flex flex-col items-center justify-center text-center', { + variants: { + zSize: { + default: 'text-sm [&_img]:w-40 [&_svg]:w-16 [&_svg]:h-10', + sm: 'text-xs [&_img]:w-28 [&_svg]:w-12 [&_svg]:h-8', + lg: 'text-base [&_img]:w-52 [&_svg]:w-20 [&_svg]:h-12', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export type ZardEmptyVariants = VariantProps; diff --git a/src/app/shared/components/form/form.component.ts b/src/app/shared/components/form/form.component.ts new file mode 100644 index 0000000..b8192b0 --- /dev/null +++ b/src/app/shared/components/form/form.component.ts @@ -0,0 +1,92 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { formFieldVariants, formControlVariants, formLabelVariants, formMessageVariants, ZardFormMessageVariants } from './form.variants'; + +@Component({ + selector: 'z-form-field, [z-form-field]', + exportAs: 'zFormField', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: '', + host: { + '[class]': 'classes()', + }, +}) +export class ZardFormFieldComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(formFieldVariants(), this.class())); +} + +@Component({ + selector: 'z-form-control, [z-form-control]', + exportAs: 'zFormControl', + standalone: true, + imports: [], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + @if (errorMessage() || helpText()) { +
    + @if (errorMessage()) { +

    {{ errorMessage() }}

    + } @else if (helpText()) { +

    {{ helpText() }}

    + } +
    + } + `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardFormControlComponent { + readonly class = input(''); + readonly errorMessage = input(''); + readonly helpText = input(''); + + protected readonly classes = computed(() => mergeClasses(formControlVariants(), this.class())); +} + +@Component({ + selector: 'z-form-label, label[z-form-label]', + exportAs: 'zFormLabel', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: '', + host: { + '[class]': 'classes()', + }, +}) +export class ZardFormLabelComponent { + readonly class = input(''); + readonly zRequired = input(false, { transform }); + + protected readonly classes = computed(() => mergeClasses(formLabelVariants({ zRequired: this.zRequired() }), this.class())); +} + +@Component({ + selector: 'z-form-message, [z-form-message]', + exportAs: 'zFormMessage', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: '', + host: { + '[class]': 'classes()', + }, +}) +export class ZardFormMessageComponent { + readonly class = input(''); + readonly zType = input('default'); + + protected readonly classes = computed(() => mergeClasses(formMessageVariants({ zType: this.zType() }), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/form/form.module.ts b/src/app/shared/components/form/form.module.ts new file mode 100644 index 0000000..9ca3ed9 --- /dev/null +++ b/src/app/shared/components/form/form.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; + +import { ZardFormControlComponent, ZardFormFieldComponent, ZardFormLabelComponent, ZardFormMessageComponent } from './form.component'; + +const FORM_COMPONENTS = [ZardFormFieldComponent, ZardFormLabelComponent, ZardFormControlComponent, ZardFormMessageComponent]; + +@NgModule({ + imports: [...FORM_COMPONENTS], + exports: [...FORM_COMPONENTS], +}) +export class ZardFormModule {} \ No newline at end of file diff --git a/src/app/shared/components/form/form.variants.ts b/src/app/shared/components/form/form.variants.ts new file mode 100644 index 0000000..cde2dcd --- /dev/null +++ b/src/app/shared/components/form/form.variants.ts @@ -0,0 +1,32 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const formFieldVariants = cva('grid gap-2'); + +export const formLabelVariants = cva('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', { + variants: { + zRequired: { + true: "after:content-['*'] after:ml-0.5 after:text-red-500", + }, + }, +}); + +export const formControlVariants = cva(''); + +export const formMessageVariants = cva('text-sm', { + variants: { + zType: { + default: 'text-muted-foreground', + error: 'text-red-500', + success: 'text-green-500', + warning: 'text-yellow-500', + }, + }, + defaultVariants: { + zType: 'default', + }, +}); + +export type ZardFormFieldVariants = VariantProps; +export type ZardFormLabelVariants = VariantProps; +export type ZardFormControlVariants = VariantProps; +export type ZardFormMessageVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/input-group/input-group.component.ts b/src/app/shared/components/input-group/input-group.component.ts new file mode 100644 index 0000000..a92b81d --- /dev/null +++ b/src/app/shared/components/input-group/input-group.component.ts @@ -0,0 +1,136 @@ +import { booleanAttribute, ChangeDetectionStrategy, Component, computed, input, TemplateRef, ViewEncapsulation } from '@angular/core'; +import type { ClassValue } from 'clsx'; + +import { generateId, mergeClasses } from '@shared/utils/merge-classes'; +import { ZardStringTemplateOutletDirective } from '../core/directives/string-template-outlet/string-template-outlet.directive'; +import { inputGroupAddonVariants, inputGroupAffixVariants, inputGroupInputVariants, inputGroupVariants, ZardInputGroupVariants } from './input-group.variants'; + +@Component({ + selector: 'z-input-group', + exportAs: 'zInputGroup', + standalone: true, + imports: [ZardStringTemplateOutletDirective], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + @if (zAddOnBefore()) { +
    + {{ zAddOnBefore() }} +
    + } + +
    + @if (zPrefix()) { +
    + {{ zPrefix() }} +
    + } + + + + @if (zSuffix()) { +
    + {{ zSuffix() }} +
    + } +
    + + @if (zAddOnAfter()) { +
    + {{ zAddOnAfter() }} +
    + } +
    + `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardInputGroupComponent { + readonly zSize = input('default'); + readonly zAddOnBefore = input>(); + readonly zAddOnAfter = input>(); + readonly zPrefix = input>(); + readonly zSuffix = input>(); + readonly zDisabled = input(false, { transform: booleanAttribute }); + readonly zBorderless = input(false, { transform: booleanAttribute }); + readonly zAriaLabel = input(); + readonly zAriaLabelledBy = input(); + readonly zAriaDescribedBy = input(); + readonly zAddOnBeforeAriaLabel = input(); + readonly zAddOnAfterAriaLabel = input(); + readonly zPrefixAriaLabel = input(); + readonly zSuffixAriaLabel = input(); + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses('w-full', this.class())); + + private readonly uniqueId = generateId('input-group'); + protected readonly addonBeforeId = computed(() => `${this.uniqueId}-addon-before`); + protected readonly addonAfterId = computed(() => `${this.uniqueId}-addon-after`); + protected readonly prefixId = computed(() => `${this.uniqueId}-prefix`); + protected readonly suffixId = computed(() => `${this.uniqueId}-suffix`); + + protected readonly wrapperClasses = computed(() => + inputGroupVariants({ + zSize: this.zSize(), + zDisabled: this.zDisabled(), + }), + ); + + protected readonly addonBeforeClasses = computed(() => + inputGroupAddonVariants({ + zSize: this.zSize(), + zPosition: 'before', + zDisabled: this.zDisabled(), + zBorderless: this.zBorderless(), + }), + ); + + protected readonly addonAfterClasses = computed(() => + inputGroupAddonVariants({ + zSize: this.zSize(), + zPosition: 'after', + zDisabled: this.zDisabled(), + zBorderless: this.zBorderless(), + }), + ); + + protected readonly prefixClasses = computed(() => + inputGroupAffixVariants({ + zSize: this.zSize(), + zPosition: 'prefix', + }), + ); + + protected readonly suffixClasses = computed(() => + inputGroupAffixVariants({ + zSize: this.zSize(), + zPosition: 'suffix', + }), + ); + + protected readonly inputWrapperClasses = computed(() => { + return mergeClasses( + inputGroupInputVariants({ + zSize: this.zSize(), + zHasPrefix: Boolean(this.zPrefix()), + zHasSuffix: Boolean(this.zSuffix()), + zHasAddonBefore: Boolean(this.zAddOnBefore()), + zHasAddonAfter: Boolean(this.zAddOnAfter()), + zDisabled: this.zDisabled(), + zBorderless: this.zBorderless(), + }), + 'relative', + ); + }); +} diff --git a/src/app/shared/components/input-group/input-group.variants.ts b/src/app/shared/components/input-group/input-group.variants.ts new file mode 100644 index 0000000..8e7f245 --- /dev/null +++ b/src/app/shared/components/input-group/input-group.variants.ts @@ -0,0 +1,151 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const inputGroupVariants = cva( + 'flex items-stretch w-full [&_input[z-input]]:!border-0 [&_input[z-input]]:!bg-transparent [&_input[z-input]]:!outline-none [&_input[z-input]]:!ring-0 [&_input[z-input]]:!ring-offset-0 [&_input[z-input]]:!px-0 [&_input[z-input]]:!py-0 [&_input[z-input]]:!h-full [&_input[z-input]]:flex-1 [&_textarea[z-input]]:!border-0 [&_textarea[z-input]]:!bg-transparent [&_textarea[z-input]]:!outline-none [&_textarea[z-input]]:!ring-0 [&_textarea[z-input]]:!ring-offset-0 [&_textarea[z-input]]:!px-0 [&_textarea[z-input]]:!py-0', + { + variants: { + zSize: { + sm: 'h-9', + default: 'h-10', + lg: 'h-11', + }, + zDisabled: { + true: 'opacity-50 cursor-not-allowed', + false: '', + }, + }, + defaultVariants: { + zSize: 'default', + zDisabled: false, + }, + }, +); + +export const inputGroupAddonVariants = cva( + 'addon inline-flex items-center justify-center whitespace-nowrap text-sm font-medium border border-input bg-muted text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', + { + variants: { + zSize: { + sm: 'h-9 px-3 text-xs', + default: 'h-10 px-3 text-sm', + lg: 'h-11 px-4 text-base', + }, + zPosition: { + before: 'rounded-l-md border-r-0', + after: 'rounded-r-md border-l-0', + }, + zDisabled: { + true: 'cursor-not-allowed opacity-50 pointer-events-none', + false: '', + }, + zBorderless: { + true: 'border-0 shadow-none', + false: '', + }, + }, + defaultVariants: { + zSize: 'default', + zPosition: 'before', + zDisabled: false, + zBorderless: false, + }, + }, +); + +export const inputGroupAffixVariants = cva('absolute inset-y-0 flex items-center text-muted-foreground pointer-events-none z-10', { + variants: { + zSize: { + sm: 'text-xs', + default: 'text-sm', + lg: 'text-base', + }, + zPosition: { + prefix: 'left-0 pl-3', + suffix: 'right-0 pr-3', + }, + }, + defaultVariants: { + zSize: 'default', + zPosition: 'prefix', + }, +}); + +export const inputGroupInputVariants = cva( + 'input-wrapper flex h-full w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 transition-colors', + { + variants: { + zSize: { + sm: 'h-9 px-3 py-1 text-sm', + default: 'h-10 px-3 py-2 text-sm', + lg: 'h-11 px-4 py-2 text-base', + }, + zHasPrefix: { + true: '', + false: '', + }, + zHasSuffix: { + true: '', + false: '', + }, + zHasAddonBefore: { + true: 'border-l-0 rounded-l-none', + false: '', + }, + zHasAddonAfter: { + true: 'border-r-0 rounded-r-none', + false: '', + }, + zDisabled: { + true: 'cursor-not-allowed opacity-50', + false: '', + }, + zBorderless: { + true: 'border-0 bg-transparent shadow-none', + false: '', + }, + }, + compoundVariants: [ + { + zHasPrefix: true, + zSize: 'sm', + class: 'pl-7', + }, + { + zHasPrefix: true, + zSize: 'default', + class: 'pl-8', + }, + { + zHasPrefix: true, + zSize: 'lg', + class: 'pl-9', + }, + { + zHasSuffix: true, + zSize: 'sm', + class: 'pr-12', + }, + { + zHasSuffix: true, + zSize: 'default', + class: 'pr-14', + }, + { + zHasSuffix: true, + zSize: 'lg', + class: 'pr-16', + }, + ], + defaultVariants: { + zSize: 'default', + zHasPrefix: false, + zHasSuffix: false, + zHasAddonBefore: false, + zHasAddonAfter: false, + zDisabled: false, + zBorderless: false, + }, + }, +); + +export type ZardInputGroupVariants = VariantProps; diff --git a/src/app/shared/components/input/input.directive.ts b/src/app/shared/components/input/input.directive.ts new file mode 100644 index 0000000..6717794 --- /dev/null +++ b/src/app/shared/components/input/input.directive.ts @@ -0,0 +1,29 @@ +import type { ClassValue } from 'clsx'; + +import { computed, Directive, ElementRef, inject, input } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { inputVariants, ZardInputVariants } from './input.variants'; + +@Directive({ + selector: 'input[z-input], textarea[z-input]', + exportAs: 'zInput', + standalone: true, + host: { + '[class]': 'classes()', + }, +}) +export class ZardInputDirective { + readonly elementRef = inject(ElementRef); + private readonly isTextarea = this.elementRef.nativeElement.tagName.toLowerCase() === 'textarea'; + + readonly zBorderless = input(false, { transform }); + readonly zSize = input('default'); + readonly zStatus = input(); + + readonly class = input(''); + + protected readonly classes = computed(() => + mergeClasses(inputVariants({ zType: !this.isTextarea ? 'default' : 'textarea', zSize: this.zSize(), zStatus: this.zStatus(), zBorderless: this.zBorderless() }), this.class()), + ); +} diff --git a/src/app/shared/components/input/input.variants.ts b/src/app/shared/components/input/input.variants.ts new file mode 100644 index 0000000..6e1d579 --- /dev/null +++ b/src/app/shared/components/input/input.variants.ts @@ -0,0 +1,33 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export type zInputIcon = 'email' | 'password' | 'text'; + +export const inputVariants = cva('w-full', { + variants: { + zType: { + default: + 'flex rounded-md border px-4 font-normal border-input bg-transparent text-base md:text-sm ring-offset-background file:border-0 file:text-foreground file:bg-transparent file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', + textarea: + 'flex min-h-[80px] rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', + }, + zSize: { + default: 'h-10 py-2 file:max-md:py-0', + sm: 'h-9 file:md:py-2 file:max-md:py-1.5', + lg: 'h-11 py-1 file:md:py-3 file:max-md:py-2.5', + }, + zStatus: { + error: 'border-destructive focus-visible:ring-destructive', + warning: 'border-yellow-500 focus-visible:ring-yellow-500', + success: 'border-green-500 focus-visible:ring-green-500', + }, + zBorderless: { + true: 'flex-1 bg-transparent border-0 outline-none focus-visible:ring-0 focus-visible:ring-offset-0 px-0 py-0', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + }, +}); + +export type ZardInputVariants = VariantProps; diff --git a/src/app/shared/components/layout/content.component.ts b/src/app/shared/components/layout/content.component.ts new file mode 100644 index 0000000..57cb96a --- /dev/null +++ b/src/app/shared/components/layout/content.component.ts @@ -0,0 +1,27 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { contentVariants } from './layout.variants'; + +@Component({ + selector: 'z-content', + exportAs: 'zContent', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, + host: { + '[class]': 'classes()', + }, +}) +export class ContentComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(contentVariants(), this.class())); +} diff --git a/src/app/shared/components/layout/footer.component.ts b/src/app/shared/components/layout/footer.component.ts new file mode 100644 index 0000000..8a1f756 --- /dev/null +++ b/src/app/shared/components/layout/footer.component.ts @@ -0,0 +1,25 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { footerVariants } from './layout.variants'; + +@Component({ + selector: 'z-footer', + exportAs: 'zFooter', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class FooterComponent { + readonly class = input(''); + readonly zHeight = input(64); + + protected readonly classes = computed(() => mergeClasses(footerVariants(), this.class())); +} diff --git a/src/app/shared/components/layout/header.component.ts b/src/app/shared/components/layout/header.component.ts new file mode 100644 index 0000000..8925449 --- /dev/null +++ b/src/app/shared/components/layout/header.component.ts @@ -0,0 +1,25 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { headerVariants } from './layout.variants'; + +@Component({ + selector: 'z-header', + exportAs: 'zHeader', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class HeaderComponent { + readonly class = input(''); + readonly zHeight = input(64); + + protected readonly classes = computed(() => mergeClasses(headerVariants(), this.class())); +} diff --git a/src/app/shared/components/layout/layout.component.ts b/src/app/shared/components/layout/layout.component.ts new file mode 100644 index 0000000..6f92aa6 --- /dev/null +++ b/src/app/shared/components/layout/layout.component.ts @@ -0,0 +1,45 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, contentChildren, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { layoutVariants, LayoutVariants } from './layout.variants'; +import { SidebarComponent } from './sidebar.component'; + +@Component({ + selector: 'z-layout', + exportAs: 'zLayout', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + host: { + '[class]': 'classes()', + }, + template: ``, +}) +export class LayoutComponent { + readonly class = input(''); + readonly zDirection = input('auto'); + + // Query for direct sidebar children to auto-detect layout direction + private readonly sidebars = contentChildren(SidebarComponent, { descendants: false }); + + private readonly detectedDirection = computed(() => { + if (this.zDirection() !== 'auto') { + return this.zDirection(); + } + + // Auto-detection: Check if there are any sidebar children + const hasSidebar = this.sidebars().length > 0; + return hasSidebar ? 'horizontal' : 'vertical'; + }); + + protected readonly classes = computed(() => + mergeClasses( + layoutVariants({ + zDirection: this.detectedDirection() as LayoutVariants['zDirection'], + }), + this.class(), + ), + ); +} diff --git a/src/app/shared/components/layout/layout.module.ts b/src/app/shared/components/layout/layout.module.ts new file mode 100644 index 0000000..ea5d798 --- /dev/null +++ b/src/app/shared/components/layout/layout.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; + +import { ContentComponent } from './content.component'; +import { FooterComponent } from './footer.component'; +import { HeaderComponent } from './header.component'; +import { LayoutComponent } from './layout.component'; +import { SidebarGroupLabelComponent, SidebarGroupComponent, SidebarComponent } from './sidebar.component'; + +const LAYOUT_COMPONENTS = [LayoutComponent, HeaderComponent, FooterComponent, ContentComponent, SidebarComponent, SidebarGroupComponent, SidebarGroupLabelComponent]; + +@NgModule({ + imports: [LAYOUT_COMPONENTS], + exports: [LAYOUT_COMPONENTS], +}) +export class LayoutModule {} diff --git a/src/app/shared/components/layout/layout.variants.ts b/src/app/shared/components/layout/layout.variants.ts new file mode 100644 index 0000000..6df9e37 --- /dev/null +++ b/src/app/shared/components/layout/layout.variants.ts @@ -0,0 +1,48 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +// Layout Variants +export const layoutVariants = cva('flex w-full min-h-0', { + variants: { + zDirection: { + horizontal: 'flex-row', + vertical: 'flex-col', + auto: 'flex-col', + }, + }, + defaultVariants: { + zDirection: 'auto', + }, +}); +export type LayoutVariants = VariantProps; + +// Header Variants +export const headerVariants = cva('flex items-center px-4 bg-background border-b border-border shrink-0', { + variants: {}, +}); +export type HeaderVariants = VariantProps; + +// Footer Variants +export const footerVariants = cva('flex items-center px-6 bg-background border-t border-border shrink-0', { + variants: {}, +}); +export type FooterVariants = VariantProps; + +// Content Variants +export const contentVariants = cva('flex-1 flex flex-col overflow-auto bg-background p-6 min-h-dvh'); +export type ContentVariants = VariantProps; + +// Sidebar Variants +export const sidebarVariants = cva( + 'relative flex flex-col h-full transition-all duration-300 ease-in-out border-r shrink-0 p-6 bg-sidebar text-sidebar-foreground border-sidebar-border', +); + +export const sidebarTriggerVariants = cva( + 'absolute bottom-4 z-10 flex items-center justify-center cursor-pointer rounded-sm border border-sidebar-border bg-sidebar hover:bg-sidebar-accent transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring focus-visible:ring-offset-2 w-6 h-6 -right-3', +); + +// Sidebar Group Variants +export const sidebarGroupVariants = cva('flex flex-col gap-1'); + +export const sidebarGroupLabelVariants = cva( + 'flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 focus-visible:ring-sidebar-ring [&>svg]:size-4 [&>svg]:shrink-0', +); diff --git a/src/app/shared/components/layout/sidebar.component.ts b/src/app/shared/components/layout/sidebar.component.ts new file mode 100644 index 0000000..723aa1e --- /dev/null +++ b/src/app/shared/components/layout/sidebar.component.ts @@ -0,0 +1,127 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, effect, input, output, signal, TemplateRef, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { ZardStringTemplateOutletDirective } from '../core/directives/string-template-outlet/string-template-outlet.directive'; +import { sidebarGroupLabelVariants, sidebarGroupVariants, sidebarTriggerVariants, sidebarVariants } from './layout.variants'; + +@Component({ + selector: 'z-sidebar', + exportAs: 'zSidebar', + standalone: true, + imports: [ZardStringTemplateOutletDirective], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class SidebarComponent { + readonly zWidth = input(200); + readonly zCollapsedWidth = input(64); + readonly zCollapsible = input(false, { transform }); + readonly zCollapsed = input(false, { transform }); + readonly zReverseArrow = input(false, { transform }); + readonly zTrigger = input | null>(null); + readonly class = input(''); + + readonly zCollapsedChange = output(); + + private readonly internalCollapsed = signal(false); + + constructor() { + effect(() => { + this.internalCollapsed.set(this.zCollapsed()); + }); + } + + protected readonly currentWidth = computed(() => { + const collapsed = this.zCollapsed(); + if (collapsed) { + return this.zCollapsedWidth(); + } + + const width = this.zWidth(); + return typeof width === 'number' ? width : parseInt(width, 10); + }); + + protected readonly chevronIcon = computed(() => { + const collapsed = this.zCollapsed(); + const reverse = this.zReverseArrow(); + + if (reverse) { + return collapsed ? 'icon-chevron-left text-base' : 'icon-chevron-right text-base'; + } + return collapsed ? 'icon-chevron-right text-base' : 'icon-chevron-left text-base'; + }); + + protected readonly classes = computed(() => mergeClasses(sidebarVariants(), this.class())); + + protected readonly triggerClasses = computed(() => mergeClasses(sidebarTriggerVariants())); + + toggleCollapsed(): void { + const newState = !this.zCollapsed(); + this.internalCollapsed.set(newState); + this.zCollapsedChange.emit(newState); + } +} + +@Component({ + selector: 'z-sidebar-group', + exportAs: 'zSidebarGroup', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class SidebarGroupComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(sidebarGroupVariants(), this.class())); +} + +@Component({ + selector: 'z-sidebar-group-label', + exportAs: 'zSidebarGroupLabel', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class SidebarGroupLabelComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(sidebarGroupLabelVariants(), this.class())); +} diff --git a/src/app/shared/components/loader/loader.component.ts b/src/app/shared/components/loader/loader.component.ts new file mode 100644 index 0000000..5bbdcfd --- /dev/null +++ b/src/app/shared/components/loader/loader.component.ts @@ -0,0 +1,55 @@ +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; +import type { ClassValue } from 'clsx'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { loaderVariants, ZardLoaderVariants } from './loader.variants'; + +@Component({ + selector: 'z-loader', + exportAs: 'zLoader', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + @for (_ of bars; track $index) { +
    + } +
    + `, + styles: ` + @layer utilities { + @keyframes spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0.15; + } + } + + .animate-spinner { + animation: spinner 1.2s linear infinite; + } + } + `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardLoaderComponent { + readonly class = input(''); + readonly zSize = input('default'); + + protected readonly bars = Array.from({ length: 12 }); + protected readonly animationDelay = (index: number) => `-${1.3 - index * 0.1}s`; + protected readonly transform = (index: number) => `rotate(${30 * index}deg) translate(146%)`; + + protected readonly classes = computed(() => mergeClasses(loaderVariants({ zSize: this.zSize() }), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/loader/loader.variants.ts b/src/app/shared/components/loader/loader.variants.ts new file mode 100644 index 0000000..07770b9 --- /dev/null +++ b/src/app/shared/components/loader/loader.variants.ts @@ -0,0 +1,15 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const loaderVariants = cva('', { + variants: { + zSize: { + default: 'size-6', + sm: 'size-4', + lg: 'size-8', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); +export type ZardLoaderVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/menu/menu-content.directive.ts b/src/app/shared/components/menu/menu-content.directive.ts new file mode 100644 index 0000000..e91cdba --- /dev/null +++ b/src/app/shared/components/menu/menu-content.directive.ts @@ -0,0 +1,21 @@ +import type { ClassValue } from 'clsx'; + +import { CdkMenu } from '@angular/cdk/menu'; +import { computed, Directive, input } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { menuContentVariants } from './menu.variants'; + +@Directive({ + selector: '[z-menu-content]', + standalone: true, + hostDirectives: [CdkMenu], + host: { + '[class]': 'classes()', + }, +}) +export class ZardMenuContentDirective { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(menuContentVariants(), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/menu/menu-item.directive.ts b/src/app/shared/components/menu/menu-item.directive.ts new file mode 100644 index 0000000..589be95 --- /dev/null +++ b/src/app/shared/components/menu/menu-item.directive.ts @@ -0,0 +1,85 @@ +import type { ClassValue } from 'clsx'; + +import { BooleanInput } from '@angular/cdk/coercion'; +import { CdkMenuItem } from '@angular/cdk/menu'; +import { booleanAttribute, computed, Directive, effect, inject, input, signal, untracked } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { menuItemVariants, ZardMenuItemVariants } from './menu.variants'; + +@Directive({ + selector: 'button[z-menu-item], [z-menu-item]', + standalone: true, + hostDirectives: [ + { + directive: CdkMenuItem, + outputs: ['cdkMenuItemTriggered: menuItemTriggered'], + }, + ], + host: { + '[class]': 'classes()', + '[attr.data-orientation]': "'horizontal'", + '[attr.data-state]': 'isOpenState()', + '[attr.aria-disabled]': "disabledState() ? '' : undefined", + '[attr.data-disabled]': "disabledState() ? '' : undefined", + '[attr.data-highlighted]': "highlightedState() ? '' : undefined", + + '(focus)': 'onFocus()', + '(blur)': 'onBlur()', + '(pointermove)': 'onPointerMove($event)', + }, +}) +export class ZardMenuItemDirective { + private readonly cdkMenuItem = inject(CdkMenuItem, { host: true }); + + readonly zDisabled = input(false, { transform: booleanAttribute }); + readonly zInset = input(false); + readonly class = input(''); + + private readonly isFocused = signal(false); + + protected readonly disabledState = computed(() => this.zDisabled()); + + protected readonly isOpenState = computed(() => this.cdkMenuItem.isMenuOpen()); + + protected readonly highlightedState = computed(() => this.isFocused()); + + protected readonly classes = computed(() => + mergeClasses( + menuItemVariants({ + inset: this.zInset(), + }), + this.class(), + ), + ); + + constructor() { + effect(() => { + const disabled = this.zDisabled(); + untracked(() => { + this.cdkMenuItem.disabled = disabled; + }); + }); + } + + onFocus(): void { + if (!this.zDisabled()) { + this.isFocused.set(true); + } + } + + onBlur(): void { + this.isFocused.set(false); + } + + onPointerMove(event: PointerEvent) { + if (event.defaultPrevented) return; + + if (!(event.pointerType === 'mouse')) return; + + if (!this.zDisabled()) { + const item = event.currentTarget; + (item as HTMLElement)?.focus({ preventScroll: true }); + } + } +} \ No newline at end of file diff --git a/src/app/shared/components/menu/menu-manager.service.ts b/src/app/shared/components/menu/menu-manager.service.ts new file mode 100644 index 0000000..780b981 --- /dev/null +++ b/src/app/shared/components/menu/menu-manager.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; + +import { ZardMenuDirective } from './menu.directive'; + +@Injectable({ + providedIn: 'root', +}) +export class ZardMenuManagerService { + private activeHoverMenu: ZardMenuDirective | null = null; + + registerHoverMenu(menu: ZardMenuDirective): void { + if (this.activeHoverMenu && this.activeHoverMenu !== menu) { + this.activeHoverMenu.close(); + } + this.activeHoverMenu = menu; + } + + unregisterHoverMenu(menu: ZardMenuDirective): void { + if (this.activeHoverMenu === menu) { + this.activeHoverMenu = null; + } + } + + closeActiveMenu(): void { + if (this.activeHoverMenu) { + this.activeHoverMenu.close(); + this.activeHoverMenu = null; + } + } +} \ No newline at end of file diff --git a/src/app/shared/components/menu/menu-positions.ts b/src/app/shared/components/menu/menu-positions.ts new file mode 100644 index 0000000..4bcf9ba --- /dev/null +++ b/src/app/shared/components/menu/menu-positions.ts @@ -0,0 +1,210 @@ +import { ConnectedPosition } from '@angular/cdk/overlay'; + +export const MENU_POSITIONS_MAP: { [key: string]: ConnectedPosition[] } = { + bottomLeft: [ + { + originX: 'start', + originY: 'bottom', + overlayX: 'start', + overlayY: 'top', + offsetY: 8, + }, + { + originX: 'start', + originY: 'top', + overlayX: 'start', + overlayY: 'bottom', + offsetY: -8, + }, + ], + bottomCenter: [ + { + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetY: 8, + }, + { + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetY: -8, + }, + ], + bottomRight: [ + { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top', + offsetY: 8, + }, + { + originX: 'end', + originY: 'top', + overlayX: 'end', + overlayY: 'bottom', + offsetY: -8, + }, + ], + topLeft: [ + { + originX: 'start', + originY: 'top', + overlayX: 'start', + overlayY: 'bottom', + offsetY: -8, + }, + { + originX: 'start', + originY: 'bottom', + overlayX: 'start', + overlayY: 'top', + offsetY: 8, + }, + ], + topCenter: [ + { + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetY: -8, + }, + { + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetY: 8, + }, + ], + topRight: [ + { + originX: 'end', + originY: 'top', + overlayX: 'end', + overlayY: 'bottom', + offsetY: -8, + }, + { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top', + offsetY: 8, + }, + ], + leftTop: [ + { + originX: 'start', + originY: 'top', + overlayX: 'end', + overlayY: 'top', + offsetX: -8, + }, + { + originX: 'end', + originY: 'top', + overlayX: 'start', + overlayY: 'top', + offsetX: 8, + }, + ], + leftCenter: [ + { + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + }, + { + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + }, + ], + leftBottom: [ + { + originX: 'start', + originY: 'bottom', + overlayX: 'end', + overlayY: 'bottom', + offsetX: -8, + }, + { + originX: 'end', + originY: 'bottom', + overlayX: 'start', + overlayY: 'bottom', + offsetX: 8, + }, + ], + rightTop: [ + { + originX: 'end', + originY: 'top', + overlayX: 'start', + overlayY: 'top', + offsetX: 8, + }, + { + originX: 'start', + originY: 'top', + overlayX: 'end', + overlayY: 'top', + offsetX: -8, + }, + ], + rightCenter: [ + { + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + }, + { + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + }, + ], + rightBottom: [ + { + originX: 'end', + originY: 'bottom', + overlayX: 'start', + overlayY: 'bottom', + offsetX: 8, + }, + { + originX: 'start', + originY: 'bottom', + overlayX: 'end', + overlayY: 'bottom', + offsetX: -8, + }, + ], +}; + +export type ZardMenuPlacement = + | 'bottomLeft' + | 'bottomCenter' + | 'bottomRight' + | 'topLeft' + | 'topCenter' + | 'topRight' + | 'leftTop' + | 'leftCenter' + | 'leftBottom' + | 'rightTop' + | 'rightCenter' + | 'rightBottom'; \ No newline at end of file diff --git a/src/app/shared/components/menu/menu.directive.ts b/src/app/shared/components/menu/menu.directive.ts new file mode 100644 index 0000000..9d6feba --- /dev/null +++ b/src/app/shared/components/menu/menu.directive.ts @@ -0,0 +1,186 @@ +import { BooleanInput } from '@angular/cdk/coercion'; +import { CdkMenuTrigger } from '@angular/cdk/menu'; +import { ConnectedPosition } from '@angular/cdk/overlay'; +import { booleanAttribute, computed, Directive, effect, ElementRef, inject, input, OnDestroy, OnInit, PLATFORM_ID, untracked } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; + +import { ZardMenuManagerService } from './menu-manager.service'; +import { MENU_POSITIONS_MAP, ZardMenuPlacement } from './menu-positions'; + +export type ZardMenuTrigger = 'click' | 'hover'; + +@Directive({ + selector: '[z-menu]', + standalone: true, + hostDirectives: [ + { + directive: CdkMenuTrigger, + inputs: ['cdkMenuTriggerFor: zMenuTriggerFor'], + }, + ], + host: { + role: 'button', + '[attr.aria-haspopup]': "'menu'", + '[attr.aria-expanded]': 'cdkTrigger.isOpen()', + '[attr.data-state]': "cdkTrigger.isOpen() ? 'open': 'closed'", + '[attr.data-disabled]': "zDisabled() ? '' : undefined", + '[style.cursor]': "'pointer'", + }, +}) +export class ZardMenuDirective implements OnInit, OnDestroy { + private static readonly MENU_OVERLAY_SELECTOR = '.cdk-overlay-container .cdk-overlay-pane:last-child'; + private static readonly MENU_CONTENT_SELECTOR = '.cdk-overlay-pane [z-menu-content]'; + + protected readonly cdkTrigger = inject(CdkMenuTrigger, { host: true }); + private readonly elementRef = inject(ElementRef); + private readonly menuManager = inject(ZardMenuManagerService); + private readonly platformId = inject(PLATFORM_ID); + + private closeTimeout: ReturnType | null = null; + private readonly cleanupFunctions: Array<() => void> = []; + + readonly zMenuTriggerFor = input.required(); + readonly zDisabled = input(false, { transform: booleanAttribute }); + readonly zTrigger = input('click'); + readonly zHoverDelay = input(100); + readonly zPlacement = input('bottomLeft'); + + private readonly menuPositions = computed(() => this.getPositionsByPlacement(this.zPlacement())); + + constructor() { + effect(() => { + const positions = this.menuPositions(); + untracked(() => { + this.cdkTrigger.menuPosition = positions; + }); + }); + } + + private getPositionsByPlacement(placement: ZardMenuPlacement): ConnectedPosition[] { + return MENU_POSITIONS_MAP[placement] || MENU_POSITIONS_MAP['bottomLeft']; + } + + ngOnInit(): void { + const isMobile = this.isMobileDevice(); + + // If trigger is hover but device is mobile, skip hover behavior + // The CDK MenuTrigger will handle click by default + if (this.zTrigger() === 'hover' && !isMobile) { + this.initializeHoverBehavior(); + } + } + + ngOnDestroy(): void { + this.cancelScheduledClose(); + this.menuManager.unregisterHoverMenu(this); + this.cleanupFunctions.forEach(cleanup => cleanup()); + this.cleanupFunctions.length = 0; + } + + close(): void { + this.cancelScheduledClose(); + this.cdkTrigger.close(); + } + + private initializeHoverBehavior(): void { + this.setupTriggerListeners(); + this.setupMenuOpenListener(); + } + + private setupTriggerListeners(): void { + const element = this.elementRef.nativeElement; + + this.addEventListenerWithCleanup(element, 'mouseenter', () => { + if (this.zDisabled()) return; + + this.cancelScheduledClose(); + this.menuManager.registerHoverMenu(this); + this.cdkTrigger.open(); + }); + + this.addEventListenerWithCleanup(element, 'mouseleave', event => this.scheduleCloseIfNeeded(event as MouseEvent)); + } + + private setupMenuOpenListener(): void { + const openSubscription = this.cdkTrigger.opened.subscribe(() => { + setTimeout(() => this.setupMenuContentListeners(), 0); + }); + + const closeSubscription = this.cdkTrigger.closed.subscribe(() => { + this.menuManager.unregisterHoverMenu(this); + }); + + this.cleanupFunctions.push( + () => openSubscription.unsubscribe(), + () => closeSubscription.unsubscribe(), + ); + } + + private setupMenuContentListeners(): void { + const overlay = document.querySelector(ZardMenuDirective.MENU_OVERLAY_SELECTOR); + if (!overlay) return; + + this.addEventListenerWithCleanup(overlay, 'mouseenter', () => this.cancelScheduledClose()); + this.addEventListenerWithCleanup(overlay, 'mouseleave', event => this.scheduleCloseIfNeeded(event as MouseEvent)); + } + + private cancelScheduledClose(): void { + if (this.closeTimeout) { + clearTimeout(this.closeTimeout); + this.closeTimeout = null; + } + } + + private scheduleCloseIfNeeded(event: MouseEvent): void { + if (this.shouldKeepMenuOpen(event.relatedTarget as Element)) { + return; + } + + this.scheduleMenuClose(); + } + + private shouldKeepMenuOpen(relatedTarget: Element | null): boolean { + if (!relatedTarget) return false; + + const isMovingToTrigger = this.elementRef.nativeElement.contains(relatedTarget); + const isMovingToMenu = relatedTarget.closest(ZardMenuDirective.MENU_CONTENT_SELECTOR); + const isMovingToOtherTrigger = relatedTarget.matches('[z-menu]') && !this.elementRef.nativeElement.contains(relatedTarget); + + if (isMovingToOtherTrigger) { + return false; + } + + return isMovingToTrigger || !!isMovingToMenu; + } + + private scheduleMenuClose(): void { + this.closeTimeout = setTimeout(() => { + this.cdkTrigger.close(); + }, this.zHoverDelay()); + } + + private addEventListenerWithCleanup(element: Element, eventType: string, handler: (event: MouseEvent | Event) => void, options?: AddEventListenerOptions): void { + if (isPlatformBrowser(this.platformId)) { + element.addEventListener(eventType, handler, options); + this.cleanupFunctions.push(() => element.removeEventListener(eventType, handler, options)); + } + } + + private isMobileDevice(): boolean { + if (!isPlatformBrowser(this.platformId)) { + return false; // Default to desktop behavior on server + } + + // Check for touch support + const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0; + + // Check for mobile user agent + const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i; + const isMobileUA = mobileRegex.test(navigator.userAgent); + + // Check viewport width for small screens + const isSmallScreen = window.innerWidth <= 768; + + return hasTouch && (isMobileUA || isSmallScreen); + } +} \ No newline at end of file diff --git a/src/app/shared/components/menu/menu.module.ts b/src/app/shared/components/menu/menu.module.ts new file mode 100644 index 0000000..ac2cf82 --- /dev/null +++ b/src/app/shared/components/menu/menu.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; + +import { ZardMenuContentDirective } from './menu-content.directive'; +import { ZardMenuItemDirective } from './menu-item.directive'; +import { ZardMenuDirective } from './menu.directive'; + +const MENU_COMPONENTS = [ZardMenuContentDirective, ZardMenuItemDirective, ZardMenuDirective]; + +@NgModule({ + imports: [MENU_COMPONENTS], + exports: [MENU_COMPONENTS], +}) +export class ZardMenuModule {} \ No newline at end of file diff --git a/src/app/shared/components/menu/menu.variants.ts b/src/app/shared/components/menu/menu.variants.ts new file mode 100644 index 0000000..bb56ef1 --- /dev/null +++ b/src/app/shared/components/menu/menu.variants.ts @@ -0,0 +1,23 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const menuContentVariants = cva( + 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-2 text-popover-foreground shadow-lg animate-in data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', +); + +export const menuItemVariants = cva( + 'relative flex w-full cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 text-left', + { + variants: { + inset: { + true: 'pl-8', + false: '', + }, + }, + defaultVariants: { + inset: false, + }, + }, +); + +export type ZardMenuContentVariants = VariantProps; +export type ZardMenuItemVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/modal/modal.css b/src/app/shared/components/modal/modal.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/modal/modal.html b/src/app/shared/components/modal/modal.html new file mode 100644 index 0000000..e0998cb --- /dev/null +++ b/src/app/shared/components/modal/modal.html @@ -0,0 +1,35 @@ +@if (open()) { +
    +
    +
    +
    +

    {{ title() }}

    + +
    + +
    + +
    + + @if (showFooter()) { +
    + +
    + } +
    +
    +
    +} diff --git a/src/app/shared/components/modal/modal.spec.ts b/src/app/shared/components/modal/modal.spec.ts new file mode 100644 index 0000000..45f7009 --- /dev/null +++ b/src/app/shared/components/modal/modal.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Modal } from './modal'; + +describe('Modal', () => { + let component: Modal; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Modal] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Modal); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/modal/modal.ts b/src/app/shared/components/modal/modal.ts new file mode 100644 index 0000000..9ba2cb4 --- /dev/null +++ b/src/app/shared/components/modal/modal.ts @@ -0,0 +1,56 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + HostListener, + input, + OnDestroy, + OnInit, + output, +} from '@angular/core'; + +@Component({ + selector: 'app-modal', + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './modal.html', + styleUrl: './modal.css', +}) +export class Modal implements OnInit, OnDestroy { + open = input(false); + title = input('Modifier'); + size = input<'sm' | 'md' | 'lg' | 'xl' | 'xxl'>('md'); + closeOnBackdrop = input(true); + showFooter = input(true); + + close = output(); + + sizeClass = computed( + () => + ({ + sm: 'max-w-sm', + md: 'max-w-lg', + lg: 'max-w-2xl', + xl: 'max-w-4xl', + xxl: 'max-w-6xl', + }[this.size()]) + ); + + onBackdrop() { + if (this.closeOnBackdrop()) this.close.emit(); + } + + ngOnInit() { + // Scroll lock quand la modale est ouverte + if (this.open()) document.documentElement.style.overflow = 'hidden'; + } + + ngOnDestroy() { + document.documentElement.style.overflow = ''; + } + + @HostListener('document:keydown.escape') onEsc() { + if (this.open()) this.close.emit(); + } +} diff --git a/src/app/shared/components/mode-toggle/mode-toggle.css b/src/app/shared/components/mode-toggle/mode-toggle.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/mode-toggle/mode-toggle.html b/src/app/shared/components/mode-toggle/mode-toggle.html new file mode 100644 index 0000000..634b2f6 --- /dev/null +++ b/src/app/shared/components/mode-toggle/mode-toggle.html @@ -0,0 +1,13 @@ +
    + + + +
    diff --git a/src/app/shared/components/mode-toggle/mode-toggle.spec.ts b/src/app/shared/components/mode-toggle/mode-toggle.spec.ts new file mode 100644 index 0000000..f58d241 --- /dev/null +++ b/src/app/shared/components/mode-toggle/mode-toggle.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModeToggle } from './mode-toggle'; + +describe('ModeToggle', () => { + let component: ModeToggle; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ModeToggle] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModeToggle); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/mode-toggle/mode-toggle.ts b/src/app/shared/components/mode-toggle/mode-toggle.ts new file mode 100644 index 0000000..1226476 --- /dev/null +++ b/src/app/shared/components/mode-toggle/mode-toggle.ts @@ -0,0 +1,25 @@ +import { Component, computed } from '@angular/core'; +import { LucideAngularModule, MoonIcon, SunIcon } from 'lucide-angular'; +import { Theme } from 'src/app/core/services/theme'; +import { ZardSwitchComponent } from '../switch/switch.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +@Component({ + selector: 'app-mode-toggle', + imports: [LucideAngularModule, ZardSwitchComponent, FormsModule, ReactiveFormsModule], + templateUrl: './mode-toggle.html', + styleUrl: './mode-toggle.css', + standalone: true, +}) +export class ModeToggle { + readonly SunIcon = SunIcon; + readonly MoonIcon = MoonIcon; + + isDark = computed(() => this.theme.mode() === 'dark'); + + constructor(private theme: Theme) {} + + toggle() { + this.theme.toggle(); + } +} diff --git a/src/app/shared/components/pagination/pagination.component.ts b/src/app/shared/components/pagination/pagination.component.ts new file mode 100644 index 0000000..92fbccc --- /dev/null +++ b/src/app/shared/components/pagination/pagination.component.ts @@ -0,0 +1,245 @@ +import { booleanAttribute, ChangeDetectionStrategy, Component, computed, forwardRef, input, linkedSignal, output, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ClassValue } from 'clsx'; + +import { + paginationContentVariants, + paginationEllipsisVariants, + paginationItemVariants, + paginationNextVariants, + paginationPreviousVariants, + paginationVariants, +} from './pagination.variants'; +import { buttonVariants, ZardButtonVariants } from '../button/button.variants'; +import { mergeClasses } from '@shared/utils/merge-classes'; + +@Component({ + selector: 'z-pagination-content', + exportAs: 'zPaginationContent', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class ZardPaginationContentComponent { + readonly ariaLabel = input('pagination-content'); + + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(paginationContentVariants(), this.class())); +} + +@Component({ + selector: 'z-pagination-item', + exportAs: 'zPaginationItem', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + +
    + `, +}) +export class ZardPaginationItemComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(paginationItemVariants(), this.class())); +} + +@Component({ + selector: 'z-pagination-button', + exportAs: 'zPaginationButton', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class ZardPaginationButtonComponent { + readonly zDisabled = input(false, { transform: booleanAttribute }); + readonly zActive = input(false, { transform: booleanAttribute }); + readonly zSize = input('icon'); + + readonly class = input(''); + readonly zClick = output(); + + protected readonly classes = computed(() => mergeClasses(buttonVariants({ zType: this.zType(), zSize: this.zSize() }), this.class())); + + private readonly zType = computed(() => (this.zActive() ? 'outline' : 'ghost')); + + handleClick() { + if (!this.zDisabled() && !this.zActive()) { + this.zClick.emit(); + } + } +} + +@Component({ + selector: 'z-pagination-previous', + exportAs: 'zPaginationPrevious', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZardPaginationButtonComponent], + template: ` + +
    + +
    + `, +}) +export class ZardPaginationPreviousComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(paginationPreviousVariants(), this.class())); +} + +@Component({ + selector: 'z-pagination-next', + exportAs: 'zPaginationNext', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZardPaginationButtonComponent], + template: ` + + +
    +
    + `, +}) +export class ZardPaginationNextComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(paginationNextVariants(), this.class())); +} + +@Component({ + selector: 'z-pagination-ellipsis', + exportAs: 'zPaginationEllipsis', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + More pages + `, + host: { + '[class]': 'classes()', + }, +}) +export class ZardPaginationEllipsisComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(paginationEllipsisVariants(), this.class())); +} + +@Component({ + selector: 'z-pagination', + exportAs: 'zPagination', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZardPaginationContentComponent, ZardPaginationItemComponent, ZardPaginationButtonComponent], + template: ` + + + +
    +
    +
    + + @for (page of pages(); track page) { + + + {{ page }} + + + } + + + +
    +
    +
    +
    + `, + host: { + '[class]': 'classes()', + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardPaginationComponent), + multi: true, + }, + ], +}) +export class ZardPaginationComponent implements ControlValueAccessor { + readonly zPageIndex = input(1); + readonly zTotal = input(1); + readonly zSize = input('icon'); + readonly zDisabled = input(false, { transform: booleanAttribute }); + + readonly class = input(''); + + readonly zPageIndexChange = output(); + + protected readonly classes = computed(() => mergeClasses(paginationVariants(), this.class())); + + protected readonly disabled = linkedSignal(() => { + return this.zDisabled(); + }); + + readonly currentPage = linkedSignal(this.zPageIndex); + + readonly pages = computed(() => Array.from({ length: Math.max(0, this.zTotal()) }, (_, i) => i + 1)); + + goToPage(page: number): void { + if (this.disabled()) return; + if (page !== this.currentPage() && page >= 1 && page <= this.zTotal()) { + this.currentPage.set(page); + this.zPageIndexChange.emit(page); + this.onChange(page); + this.onTouched(); + } + } + + goToPrevious() { + this.goToPage(this.currentPage() - 1); + } + + goToNext() { + this.goToPage(this.currentPage() + 1); + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onChange: (value: number) => void = () => {}; + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onTouched: () => void = () => {}; + + writeValue(value: number): void { + this.currentPage.set(value); + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled.set(isDisabled); + } +} \ No newline at end of file diff --git a/src/app/shared/components/pagination/pagination.module.ts b/src/app/shared/components/pagination/pagination.module.ts new file mode 100644 index 0000000..ba44ff6 --- /dev/null +++ b/src/app/shared/components/pagination/pagination.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; + +import { + ZardPaginationButtonComponent, + ZardPaginationComponent, + ZardPaginationContentComponent, + ZardPaginationEllipsisComponent, + ZardPaginationItemComponent, + ZardPaginationNextComponent, + ZardPaginationPreviousComponent, +} from './pagination.component'; + +const components = [ + ZardPaginationContentComponent, + ZardPaginationItemComponent, + ZardPaginationButtonComponent, + ZardPaginationPreviousComponent, + ZardPaginationNextComponent, + ZardPaginationEllipsisComponent, + ZardPaginationComponent, +]; + +@NgModule({ + imports: components, + exports: components, +}) +export class ZardPaginationModule {} \ No newline at end of file diff --git a/src/app/shared/components/pagination/pagination.variants.ts b/src/app/shared/components/pagination/pagination.variants.ts new file mode 100644 index 0000000..31f8dee --- /dev/null +++ b/src/app/shared/components/pagination/pagination.variants.ts @@ -0,0 +1,19 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const paginationContentVariants = cva('flex flex-row items-center gap-1'); +export type ZardPaginationContentVariants = VariantProps; + +export const paginationItemVariants = cva(''); +export type ZardPaginationItemVariants = VariantProps; + +export const paginationPreviousVariants = cva('gap-1 px-2.5 sm:pl-2.5'); +export type ZardPaginationPreviousVariants = VariantProps; + +export const paginationNextVariants = cva('gap-1 px-2.5 sm:pr-2.5'); +export type ZardPaginationNextVariants = VariantProps; + +export const paginationEllipsisVariants = cva('flex size-9 items-center justify-center'); +export type ZardPaginationEllipsisVariants = VariantProps; + +export const paginationVariants = cva('mx-auto flex w-full justify-center'); +export type ZardPaginationVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/paginator/paginator.css b/src/app/shared/components/paginator/paginator.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/paginator/paginator.html b/src/app/shared/components/paginator/paginator.html new file mode 100644 index 0000000..2c0d360 --- /dev/null +++ b/src/app/shared/components/paginator/paginator.html @@ -0,0 +1,84 @@ +
    + +
    + Affichage de {{ (page() - 1) * perPage() + 1 }} à + {{ page() * perPage() < total() ? page() * perPage() : total() }} + sur {{ total() }} résultat{{ total() > 1 ? 's' : '' }} +
    + + +
    + + + + + + + + +
    +
    diff --git a/src/app/shared/components/paginator/paginator.spec.ts b/src/app/shared/components/paginator/paginator.spec.ts new file mode 100644 index 0000000..37cea0e --- /dev/null +++ b/src/app/shared/components/paginator/paginator.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { Paginator } from './paginator'; + +describe('Paginator', () => { + let component: Paginator; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Paginator] + }) + .compileComponents(); + + fixture = TestBed.createComponent(Paginator); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/paginator/paginator.ts b/src/app/shared/components/paginator/paginator.ts new file mode 100644 index 0000000..3946d6b --- /dev/null +++ b/src/app/shared/components/paginator/paginator.ts @@ -0,0 +1,68 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core'; + +@Component({ + selector: 'app-paginator', + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './paginator.html', + styleUrl: './paginator.css', +}) +export class Paginator { + page = input(1); + perPage = input(10); + total = input(0); + pageSizes = input([10, 20, 50]); + + pageChange = output(); + perPageChange = output(); + + totalPages = computed(() => Math.max(1, Math.ceil(this.total() / this.perPage()))); + + /** Pages visibles (avec ellipses) */ + visiblePages = computed(() => { + const total = this.totalPages(); + const current = this.page(); + const delta = 2; + const range: (number | string)[] = []; + const left = Math.max(2, current - delta); + const right = Math.min(total - 1, current + delta); + + range.push(1); + if (left > 2) range.push('...'); + for (let i = left; i <= right; i++) range.push(i); + if (right < total - 1) range.push('...'); + if (total > 1) range.push(total); + return range; + }); + + /** Tableau complet des pages pour le sélecteur */ + totalPagesArray = computed(() => Array.from({ length: this.totalPages() }, (_, i) => i + 1)); + + prev() { + const newPage = this.page() - 1; + if (newPage >= 1) this.pageChange.emit(newPage); + } + + next() { + const newPage = this.page() + 1; + if (newPage <= this.totalPages()) this.pageChange.emit(newPage); + } + + goToPage(p: number) { + if (p >= 1 && p <= this.totalPages()) this.pageChange.emit(p); + } + + handlePerPageChange(event: Event) { + const value = (event.target as HTMLSelectElement).value; + this.perPageChange.emit(parseInt(value, 10)); + } + + /** Quand on sélectionne une page depuis le select */ + selectPage(event: Event) { + const pageNumber = parseInt((event.target as HTMLSelectElement).value, 10); + if (!isNaN(pageNumber) && pageNumber >= 1 && pageNumber <= this.totalPages()) { + this.pageChange.emit(pageNumber); + } + } +} diff --git a/src/app/shared/components/pmu-logo/pmu-logo.css b/src/app/shared/components/pmu-logo/pmu-logo.css new file mode 100644 index 0000000..a30e58e --- /dev/null +++ b/src/app/shared/components/pmu-logo/pmu-logo.css @@ -0,0 +1,3 @@ +:host { + @apply inline-block; +} diff --git a/src/app/shared/components/pmu-logo/pmu-logo.html b/src/app/shared/components/pmu-logo/pmu-logo.html new file mode 100644 index 0000000..6ab627a --- /dev/null +++ b/src/app/shared/components/pmu-logo/pmu-logo.html @@ -0,0 +1,9 @@ + diff --git a/src/app/shared/components/pmu-logo/pmu-logo.spec.ts b/src/app/shared/components/pmu-logo/pmu-logo.spec.ts new file mode 100644 index 0000000..60038f1 --- /dev/null +++ b/src/app/shared/components/pmu-logo/pmu-logo.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PmuLogo } from './pmu-logo'; + +describe('PmuLogo', () => { + let component: PmuLogo; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PmuLogo] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PmuLogo); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/pmu-logo/pmu-logo.ts b/src/app/shared/components/pmu-logo/pmu-logo.ts new file mode 100644 index 0000000..d7028f2 --- /dev/null +++ b/src/app/shared/components/pmu-logo/pmu-logo.ts @@ -0,0 +1,40 @@ +import { computed, Input } from '@angular/core'; +import { Component } from '@angular/core'; +import { Theme } from 'src/app/core/services/theme'; + +@Component({ + selector: 'app-pmu-logo', + imports: [], + templateUrl: './pmu-logo.html', + styleUrl: './pmu-logo.css', + standalone: true, +}) +export class PmuLogo { + /** Variants: default | small | footer | hero ... */ + @Input() variant: 'default' | 'small' | 'footer' | 'hero' = 'default'; + + isDark = computed(() => this.theme.mode() === 'dark'); + + constructor(private theme: Theme) {} + + get logoSrc() { + return this.isDark() ? '/assets/logos/pmu_logo_light.png' : '/assets/logos/pmu_logo_dark.png'; + } + + get logoAlt() { + return 'PMU MALI'; + } + + get logoClass() { + switch (this.variant) { + case 'small': + return 'h-10'; + case 'footer': + return 'h-12 opacity-80'; + case 'hero': + return 'h-24'; + default: + return 'h-16'; + } + } +} diff --git a/src/app/shared/components/popover/popover.component.ts b/src/app/shared/components/popover/popover.component.ts new file mode 100644 index 0000000..d57a9b5 --- /dev/null +++ b/src/app/shared/components/popover/popover.component.ts @@ -0,0 +1,368 @@ +import { merge, Subject, takeUntil } from 'rxjs'; + +import { ConnectedPosition, Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { isPlatformBrowser } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + computed, + Directive, + effect, + ElementRef, + inject, + input, + OnDestroy, + OnInit, + output, + PLATFORM_ID, + Renderer2, + signal, + TemplateRef, + ViewContainerRef, +} from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { popoverVariants } from './popover.variants'; + +export type ZardPopoverTrigger = 'click' | 'hover' | null; +export type ZardPopoverPlacement = 'top' | 'bottom' | 'left' | 'right'; + +const POPOVER_POSITIONS_MAP = { + top: { + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetX: 0, + offsetY: -8, + }, + bottom: { + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetX: 0, + offsetY: 8, + }, + left: { + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + offsetY: 0, + }, + right: { + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + offsetY: 0, + }, +} as const; + +@Directive({ + selector: '[zPopover]', + exportAs: 'zPopover', + standalone: true, +}) +export class ZardPopoverDirective implements OnInit, OnDestroy { + private readonly destroy$ = new Subject(); + private readonly hidePopover$ = new Subject(); + private readonly overlay = inject(Overlay); + private readonly overlayPositionBuilder = inject(OverlayPositionBuilder); + private readonly elementRef = inject(ElementRef); + private readonly renderer = inject(Renderer2); + private readonly viewContainerRef = inject(ViewContainerRef); + private readonly platformId = inject(PLATFORM_ID); + + private overlayRef?: OverlayRef; + + readonly zTrigger = input('click'); + readonly zContent = input.required>(); + readonly zPlacement = input('bottom'); + readonly zOrigin = input(); + readonly zVisible = input(false); + readonly zOverlayClickable = input(true); + readonly zVisibleChange = output(); + + private isVisible = signal(false); + + get nativeElement() { + return this.zOrigin()?.nativeElement || this.elementRef.nativeElement; + } + + constructor() { + // Watch for changes to zVisible input + // Using untracked for isVisible to avoid circular dependencies + effect(() => { + const visible = this.zVisible(); + + // Defer DOM manipulation to avoid change detection issues + setTimeout(() => { + const currentlyVisible = this.isVisible(); + if (visible && !currentlyVisible) { + this.show(); + } else if (!visible && currentlyVisible) { + this.hide(); + } + }); + }); + } + + ngOnInit() { + this.setupTriggers(); + this.createOverlay(); + } + + ngOnDestroy() { + this.hide(); + this.hidePopover$.complete(); + this.destroy$.next(); + this.destroy$.complete(); + } + + show() { + if (this.isVisible()) return; + + if (!this.overlayRef) { + this.createOverlay(); + } + + const templatePortal = new TemplatePortal(this.zContent(), this.viewContainerRef); + this.overlayRef?.attach(templatePortal); + this.isVisible.set(true); + this.zVisibleChange.emit(true); + + if (this.zOverlayClickable() && this.zTrigger() === 'click' && isPlatformBrowser(this.platformId)) { + this.setupOutsideClickListener(); + } + } + + hide() { + if (!this.isVisible()) return; + + this.hidePopover$.next(); + this.overlayRef?.detach(); + this.isVisible.set(false); + this.zVisibleChange.emit(false); + } + + toggle() { + if (this.isVisible()) { + this.hide(); + } else { + this.show(); + } + } + + private setupTriggers() { + const trigger = this.zTrigger(); + if (!trigger) return; + + if (trigger === 'click') { + this.renderer.listen(this.nativeElement, 'click', (event: Event) => { + event.stopPropagation(); + this.toggle(); + }); + } else if (trigger === 'hover') { + this.renderer.listen(this.nativeElement, 'mouseenter', () => { + this.show(); + }); + + this.renderer.listen(this.nativeElement, 'mouseleave', () => { + this.hide(); + }); + } + } + + private createOverlay() { + if (isPlatformBrowser(this.platformId)) { + const positionStrategy = this.overlayPositionBuilder + .flexibleConnectedTo(this.nativeElement) + .withPositions(this.getPositions()) + .withPush(false) + .withFlexibleDimensions(false) + .withViewportMargin(8); + + this.overlayRef = this.overlay.create({ + positionStrategy, + hasBackdrop: false, + scrollStrategy: this.overlay.scrollStrategies.reposition(), + }); + } + } + + private getPositions(): ConnectedPosition[] { + const placement = this.zPlacement(); + const positions: ConnectedPosition[] = []; + + // Primary position + const primaryConfig = POPOVER_POSITIONS_MAP[placement]; + positions.push({ + originX: primaryConfig.originX as any, + originY: primaryConfig.originY as any, + overlayX: primaryConfig.overlayX as any, + overlayY: primaryConfig.overlayY as any, + offsetX: primaryConfig.offsetX || 0, + offsetY: primaryConfig.offsetY || 0, + }); + + // Fallback positions for better positioning when primary doesn't fit + switch (placement) { + case 'bottom': + // Try top if bottom doesn't fit + positions.push({ + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetX: 0, + offsetY: -8, + }); + // If neither top nor bottom work, try right + positions.push({ + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + offsetY: 0, + }); + // Finally try left + positions.push({ + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + offsetY: 0, + }); + break; + case 'top': + // Try bottom if top doesn't fit + positions.push({ + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetX: 0, + offsetY: 8, + }); + // If neither top nor bottom work, try right + positions.push({ + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + offsetY: 0, + }); + // Finally try left + positions.push({ + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + offsetY: 0, + }); + break; + case 'right': + // Try left if right doesn't fit + positions.push({ + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + offsetY: 0, + }); + // If neither left nor right work, try bottom + positions.push({ + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetX: 0, + offsetY: 8, + }); + // Finally try top + positions.push({ + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetX: 0, + offsetY: -8, + }); + break; + case 'left': + // Try right if left doesn't fit + positions.push({ + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + offsetY: 0, + }); + // If neither left nor right work, try bottom + positions.push({ + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetX: 0, + offsetY: 8, + }); + // Finally try top + positions.push({ + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetX: 0, + offsetY: -8, + }); + break; + } + + return positions; + } + + private setupOutsideClickListener() { + if (!this.overlayRef) return; + + this.overlayRef + .outsidePointerEvents() + .pipe(takeUntil(merge(this.hidePopover$, this.destroy$))) + .subscribe(event => { + const clickTarget = event.target as HTMLElement; + + if (this.nativeElement.contains(clickTarget)) { + return; + } + + this.hide(); + }); + } +} + +@Component({ + selector: 'z-popover', + standalone: true, + imports: [], + template: ``, + host: { + '[class]': 'classes()', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ZardPopoverComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(popoverVariants(), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/popover/popover.variants.ts b/src/app/shared/components/popover/popover.variants.ts new file mode 100644 index 0000000..b50576f --- /dev/null +++ b/src/app/shared/components/popover/popover.variants.ts @@ -0,0 +1,7 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const popoverVariants = cva( + 'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', +); + +export type ZardPopoverVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/progress-bar/progress-bar.component.ts b/src/app/shared/components/progress-bar/progress-bar.component.ts new file mode 100644 index 0000000..b9eb086 --- /dev/null +++ b/src/app/shared/components/progress-bar/progress-bar.component.ts @@ -0,0 +1,66 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { containerProgressBarVariants, progressBarVariants, ZardContainerProgressBarVariants, ZardProgressBarVariants } from './progress-bar.variants'; + +@Component({ + selector: 'z-progress-bar', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + styles: ` + @keyframes indeterminate { + 0% { + left: -0%; + width: 30%; + } + 50% { + left: 50%; + width: 30%; + } + 100% { + left: 100%; + width: 0; + } + } + `, + template: ` + @if (zIndeterminate()) { +
    +
    +
    + } @else { +
    +
    +
    + } + `, + host: { + class: 'w-full', + }, +}) +export class ZardProgressBarComponent { + readonly zType = input('default'); + readonly zSize = input('default'); + readonly zShape = input('default'); + readonly zIndeterminate = input(undefined); + readonly class = input(''); + readonly barClass = input(''); + readonly progress = input(0); + + readonly correctedProgress = computed(() => { + if (this.progress() > 100) return 100; + if (this.progress() < 0) return 0; + return this.progress(); + }); + + protected readonly classes = computed(() => + mergeClasses(containerProgressBarVariants({ zIndeterminate: this.zIndeterminate(), zType: this.zType(), zSize: this.zSize(), zShape: this.zShape() }), this.class()), + ); + + protected readonly barClasses = computed(() => + mergeClasses(progressBarVariants({ zIndeterminate: this.zIndeterminate(), zType: this.zType(), zShape: this.zShape() }), this.barClass()), + ); +} \ No newline at end of file diff --git a/src/app/shared/components/progress-bar/progress-bar.variants.ts b/src/app/shared/components/progress-bar/progress-bar.variants.ts new file mode 100644 index 0000000..24b84cb --- /dev/null +++ b/src/app/shared/components/progress-bar/progress-bar.variants.ts @@ -0,0 +1,54 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const containerProgressBarVariants = cva('w-full transition-all', { + variants: { + zType: { + default: 'bg-primary-foreground hover:bg-primary/10', + destructive: 'bg-primary-foreground dark:text-secondary-foreground hover:bg-destructive/10', + accent: 'bg-primary-foreground hover:bg-primary/10', + }, + zSize: { + default: 'h-2', + sm: 'h-3', + lg: 'h-5', + }, + zShape: { + default: 'rounded-sm', + circle: 'rounded-full', + square: 'rounded-none', + }, + zIndeterminate: { + true: 'relative', + }, + }, + + defaultVariants: { + zType: 'default', + zSize: 'default', + zShape: 'default', + }, +}); +export type ZardContainerProgressBarVariants = VariantProps; + +export const progressBarVariants = cva('h-full transition-all', { + variants: { + zType: { + default: 'bg-primary', + destructive: 'bg-destructive', + accent: 'bg-chart-1', + }, + zShape: { + default: 'rounded-sm', + circle: 'rounded-full ', + square: 'rounded-none', + }, + zIndeterminate: { + true: 'absolute animate-[indeterminate_1.5s_infinite_ease-out]', + }, + }, + defaultVariants: { + zType: 'default', + zShape: 'default', + }, +}); +export type ZardProgressBarVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/radio/radio.component.ts b/src/app/shared/components/radio/radio.component.ts new file mode 100644 index 0000000..d3e3de0 --- /dev/null +++ b/src/app/shared/components/radio/radio.component.ts @@ -0,0 +1,108 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, forwardRef, inject, input, output, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import type { ClassValue } from 'clsx'; +import { NgClass } from '@angular/common'; + +import { radioLabelVariants, radioVariants, ZardRadioVariants } from './radio.variants'; +import { generateId, mergeClasses, transform } from '@shared/utils/merge-classes'; + +type OnTouchedType = () => unknown; +type OnChangeType = (value: unknown) => void; + +@Component({ + selector: 'z-radio, [z-radio]', + standalone: true, + imports: [NgClass], + exportAs: 'zRadio', + template: ` + +
    + + + + +
    + +
    + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardRadioComponent), + multi: true, + }, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, +}) +export class ZardRadioComponent implements ControlValueAccessor { + private cdr = inject(ChangeDetectorRef); + + readonly radioChange = output(); + readonly class = input(''); + readonly disabled = input(false, { transform }); + readonly zType = input('default'); + readonly zSize = input('default'); + readonly name = input('radio'); + readonly zId = input(generateId('radio')); + readonly value = input(null); + /* eslint-disable-next-line @typescript-eslint/no-empty-function */ + private onChange: OnChangeType = () => {}; + /* eslint-disable-next-line @typescript-eslint/no-empty-function */ + private onTouched: OnTouchedType = () => {}; + + protected readonly classes = computed(() => mergeClasses(radioVariants({ zType: this.zType(), zSize: this.zSize() }), this.class())); + protected readonly labelClasses = computed(() => mergeClasses(radioLabelVariants({ zSize: this.zSize() }))); + + protected readonly svgSizeClass = computed(() => { + const size = this.zSize(); + if (size === 'lg') { + return 'h-5 w-5'; + } + if (size === 'sm') { + return 'h-2.5 w-2.5'; + } + return 'h-3.5 w-3.5'; // default size + }); + + checked = false; + + writeValue(val: unknown): void { + this.checked = val === this.value(); + this.cdr.markForCheck(); + } + + registerOnChange(fn: OnChangeType): void { + this.onChange = fn; + } + + registerOnTouched(fn: OnTouchedType): void { + this.onTouched = fn; + } + + onRadioBlur(): void { + this.onTouched(); + this.cdr.markForCheck(); + } + + onRadioChange(): void { + if (this.disabled()) return; + + this.checked = true; + this.onChange(this.value()); + this.radioChange.emit(this.checked); + this.cdr.markForCheck(); + } +} \ No newline at end of file diff --git a/src/app/shared/components/radio/radio.variants.ts b/src/app/shared/components/radio/radio.variants.ts new file mode 100644 index 0000000..086c722 --- /dev/null +++ b/src/app/shared/components/radio/radio.variants.ts @@ -0,0 +1,45 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const radioVariants = cva( + 'cursor-[unset] peer appearance-none border transition shadow hover:shadow-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', + { + variants: { + zType: { + default: 'border-primary checked:bg-primary', + destructive: 'border-destructive checked:bg-destructive', + secondary: 'border-secondary checked:bg-secondary', + }, + zSize: { + default: 'h-4 w-4', + sm: 'h-3 w-3', + lg: 'h-6 w-6', + }, + zShape: { + default: 'rounded-full', + circle: 'rounded-full', + square: 'rounded-none', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + zShape: 'default', + }, + }, +); + +export const radioLabelVariants = cva('cursor-[unset] text-current empty:hidden', { + variants: { + zSize: { + default: 'text-base', + sm: 'text-sm', + lg: 'text-lg', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export type ZardRadioVariants = VariantProps; +export type ZardRadioLabelVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/resizable/resizable-handle.component.ts b/src/app/shared/components/resizable/resizable-handle.component.ts new file mode 100644 index 0000000..6b405ac --- /dev/null +++ b/src/app/shared/components/resizable/resizable-handle.component.ts @@ -0,0 +1,188 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { ZardResizableComponent } from './resizable.component'; +import { resizableHandleIndicatorVariants, resizableHandleVariants } from './resizable.variants'; + +@Component({ + selector: 'z-resizable-handle', + exportAs: 'zResizableHandle', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (zWithHandle()) { +
    + } + `, + host: { + '[class]': 'classes()', + '[attr.data-layout]': 'layout()', + '[attr.tabindex]': 'zDisabled() ? null : 0', + '[attr.role]': '"separator"', + '[attr.aria-orientation]': 'layout() === "vertical" ? "horizontal" : "vertical"', + '[attr.aria-disabled]': 'zDisabled()', + '(mousedown)': 'handleMouseDown($event)', + '(touchstart)': 'handleTouchStart($event)', + '(keydown)': 'handleKeyDown($event)', + }, +}) +export class ZardResizableHandleComponent { + private readonly resizable = inject(ZardResizableComponent, { optional: true }); + + readonly zWithHandle = input(false, { transform }); + readonly zDisabled = input(false, { transform }); + readonly zHandleIndex = input(0); + readonly class = input(''); + + protected readonly layout = computed(() => this.resizable?.zLayout() || 'horizontal'); + + protected readonly classes = computed(() => + mergeClasses( + resizableHandleVariants({ + zLayout: this.layout(), + zDisabled: this.zDisabled(), + }), + this.class(), + ), + ); + + protected readonly handleClasses = computed(() => resizableHandleIndicatorVariants({ zLayout: this.layout() })); + + handleMouseDown(event: MouseEvent): void { + if (this.zDisabled() || !this.resizable) return; + this.resizable.startResize(this.zHandleIndex(), event); + } + + handleTouchStart(event: TouchEvent): void { + if (this.zDisabled() || !this.resizable) return; + this.resizable.startResize(this.zHandleIndex(), event); + } + + handleKeyDown(event: KeyboardEvent): void { + if (this.zDisabled() || !this.resizable) return; + + const panels = this.resizable.panels(); + const handleIndex = this.zHandleIndex(); + const layout = this.layout(); + + let delta = 0; + const step = event.shiftKey ? 10 : 1; + + switch (event.key) { + case 'ArrowLeft': + if (layout === 'horizontal') delta = -step; + break; + case 'ArrowRight': + if (layout === 'horizontal') delta = step; + break; + case 'ArrowUp': + if (layout === 'vertical') delta = -step; + break; + case 'ArrowDown': + if (layout === 'vertical') delta = step; + break; + case 'Home': + event.preventDefault(); + this.moveToExtreme(true); + return; + case 'End': + event.preventDefault(); + this.moveToExtreme(false); + return; + case 'Enter': + case ' ': + event.preventDefault(); + if (panels[handleIndex]?.zCollapsible() || panels[handleIndex + 1]?.zCollapsible()) { + const collapsibleIndex = panels[handleIndex]?.zCollapsible() ? handleIndex : handleIndex + 1; + this.resizable.collapsePanel(collapsibleIndex); + } + return; + default: + return; + } + + if (delta !== 0) { + event.preventDefault(); + this.adjustSizes(delta); + } + } + + private adjustSizes(delta: number): void { + if (!this.resizable) return; + + const panels = this.resizable.panels(); + const handleIndex = this.zHandleIndex(); + const sizes = [...this.resizable.panelSizes()]; + + const leftPanel = panels[handleIndex]; + const rightPanel = panels[handleIndex + 1]; + + if (!leftPanel || !rightPanel) return; + + const containerSize = this.resizable.getContainerSize(); + const leftMin = this.resizable.convertToPercentage(leftPanel.zMin() || 0, containerSize); + const leftMax = this.resizable.convertToPercentage(leftPanel.zMax() || 100, containerSize); + const rightMin = this.resizable.convertToPercentage(rightPanel.zMin() || 0, containerSize); + const rightMax = this.resizable.convertToPercentage(rightPanel.zMax() || 100, containerSize); + + let newLeftSize = sizes[handleIndex] + delta; + let newRightSize = sizes[handleIndex + 1] - delta; + + newLeftSize = Math.max(leftMin, Math.min(leftMax, newLeftSize)); + newRightSize = Math.max(rightMin, Math.min(rightMax, newRightSize)); + + const totalSize = newLeftSize + newRightSize; + const originalTotal = sizes[handleIndex] + sizes[handleIndex + 1]; + + if (Math.abs(totalSize - originalTotal) < 0.01) { + sizes[handleIndex] = newLeftSize; + sizes[handleIndex + 1] = newRightSize; + + this.resizable.panelSizes.set(sizes); + this.resizable.updatePanelStyles(); + this.resizable.zResize.emit({ + sizes, + layout: this.resizable.zLayout() || 'horizontal', + }); + } + } + + private moveToExtreme(toMin: boolean): void { + if (!this.resizable) return; + + const panels = this.resizable.panels(); + const handleIndex = this.zHandleIndex(); + const sizes = [...this.resizable.panelSizes()]; + + const leftPanel = panels[handleIndex]; + const rightPanel = panels[handleIndex + 1]; + + if (!leftPanel || !rightPanel) return; + + const containerSize = this.resizable.getContainerSize(); + const leftMin = this.resizable.convertToPercentage(leftPanel.zMin() || 0, containerSize); + const leftMax = this.resizable.convertToPercentage(leftPanel.zMax() || 100, containerSize); + const rightMin = this.resizable.convertToPercentage(rightPanel.zMin() || 0, containerSize); + const rightMax = this.resizable.convertToPercentage(rightPanel.zMax() || 100, containerSize); + + const totalSize = sizes[handleIndex] + sizes[handleIndex + 1]; + + if (toMin) { + sizes[handleIndex] = leftMin; + sizes[handleIndex + 1] = Math.min(totalSize - leftMin, rightMax); + } else { + sizes[handleIndex] = Math.min(totalSize - rightMin, leftMax); + sizes[handleIndex + 1] = rightMin; + } + + this.resizable['panelSizes'].set(sizes); + this.resizable['updatePanelStyles'](); + this.resizable.zResize.emit({ + sizes, + layout: this.resizable.zLayout() || 'horizontal', + }); + } +} \ No newline at end of file diff --git a/src/app/shared/components/resizable/resizable-panel.component.ts b/src/app/shared/components/resizable/resizable-panel.component.ts new file mode 100644 index 0000000..699a3e7 --- /dev/null +++ b/src/app/shared/components/resizable/resizable-panel.component.ts @@ -0,0 +1,38 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, ElementRef, inject, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { resizablePanelVariants } from './resizable.variants'; + +@Component({ + selector: 'z-resizable-panel', + exportAs: 'zResizablePanel', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + '[attr.data-collapsed]': 'isCollapsed()', + }, +}) +export class ZardResizablePanelComponent { + readonly elementRef = inject(ElementRef); + + readonly zDefaultSize = input(undefined); + readonly zMin = input(0); + readonly zMax = input(100); + readonly zCollapsible = input(false, { transform }); + readonly zResizable = input(true, { transform }); + readonly class = input(''); + + protected readonly isCollapsed = computed(() => { + const element = this.elementRef.nativeElement as HTMLElement; + const width = parseFloat(element.style.width || '0'); + const height = parseFloat(element.style.height || '0'); + return width === 0 || height === 0; + }); + + protected readonly classes = computed(() => mergeClasses(resizablePanelVariants({ zCollapsed: this.isCollapsed() }), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/resizable/resizable.component.ts b/src/app/shared/components/resizable/resizable.component.ts new file mode 100644 index 0000000..ad087e1 --- /dev/null +++ b/src/app/shared/components/resizable/resizable.component.ts @@ -0,0 +1,277 @@ +import type { ClassValue } from 'clsx'; + +import { + AfterContentInit, + ChangeDetectionStrategy, + Component, + computed, + contentChildren, + ElementRef, + EventEmitter, + inject, + input, + OnDestroy, + Output, + PLATFORM_ID, + signal, + ViewEncapsulation, +} from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { ZardResizablePanelComponent } from './resizable-panel.component'; +import { resizableVariants, ZardResizableVariants } from './resizable.variants'; + +export interface ZardResizeEvent { + sizes: number[]; + layout: 'horizontal' | 'vertical'; +} + +@Component({ + selector: 'z-resizable', + exportAs: 'zResizable', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + '[attr.data-layout]': 'zLayout()', + }, +}) +export class ZardResizableComponent implements AfterContentInit, OnDestroy { + private readonly elementRef = inject(ElementRef); + private readonly platformId = inject(PLATFORM_ID); + private listeners: (() => void)[] = []; + + readonly zLayout = input('horizontal'); + readonly zLazy = input(false, { transform }); + readonly class = input(''); + + @Output() readonly zResizeStart = new EventEmitter(); + @Output() readonly zResize = new EventEmitter(); + @Output() readonly zResizeEnd = new EventEmitter(); + + readonly panels = contentChildren(ZardResizablePanelComponent); + readonly panelSizes = signal([]); + protected readonly isResizing = signal(false); + protected readonly activeHandleIndex = signal(null); + + protected readonly classes = computed(() => mergeClasses(resizableVariants({ zLayout: this.zLayout() }), this.class())); + + ngAfterContentInit(): void { + this.initializePanelSizes(); + } + + convertToPercentage(value: number | string, containerSize: number): number { + if (typeof value === 'number') { + return value; + } + + if (typeof value === 'string') { + if (value.endsWith('%')) { + return parseFloat(value); + } + if (value.endsWith('px')) { + const pixels = parseFloat(value); + return (pixels / containerSize) * 100; + } + } + + return parseFloat(value.toString()) || 0; + } + + private initializePanelSizes(): void { + const panels = this.panels(); + const totalPanels = panels.length; + + if (totalPanels === 0) return; + + const containerSize = this.getContainerSize(); + const sizes = panels.map(panel => { + const defaultSize = panel.zDefaultSize(); + if (defaultSize !== undefined) { + return this.convertToPercentage(defaultSize, containerSize); + } + return 100 / totalPanels; + }); + + this.panelSizes.set(sizes); + this.updatePanelStyles(); + } + + startResize(handleIndex: number, event: MouseEvent | TouchEvent): void { + event.preventDefault(); + this.isResizing.set(true); + this.activeHandleIndex.set(handleIndex); + + const sizes = [...this.panelSizes()]; + this.zResizeStart.emit({ sizes, layout: this.zLayout() || 'horizontal' }); + + const startPosition = this.getEventPosition(event); + const startSizes = [...sizes]; + + const handleMove = (moveEvent: MouseEvent | TouchEvent) => { + this.handleResize(moveEvent, handleIndex, startPosition, startSizes); + }; + + const handleEnd = () => { + this.endResize(); + if (isPlatformBrowser(this.platformId)) { + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('touchmove', handleMove); + document.removeEventListener('mouseup', handleEnd); + document.removeEventListener('touchend', handleEnd); + } + }; + + if (isPlatformBrowser(this.platformId)) { + document.addEventListener('mousemove', handleMove); + document.addEventListener('touchmove', handleMove); + document.addEventListener('mouseup', handleEnd); + document.addEventListener('touchend', handleEnd); + + this.listeners.push(() => { + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('touchmove', handleMove); + document.removeEventListener('mouseup', handleEnd); + document.removeEventListener('touchend', handleEnd); + }); + } + } + + private handleResize(event: MouseEvent | TouchEvent, handleIndex: number, startPosition: number, startSizes: number[]): void { + const currentPosition = this.getEventPosition(event); + const delta = currentPosition - startPosition; + const containerSize = this.getContainerSize(); + const deltaPercentage = (delta / containerSize) * 100; + + const newSizes = [...startSizes]; + const panels = this.panels(); + + const leftPanel = panels[handleIndex]; + const rightPanel = panels[handleIndex + 1]; + + if (!leftPanel || !rightPanel) return; + + const leftMin = this.convertToPercentage(leftPanel.zMin() || 0, containerSize); + const leftMax = this.convertToPercentage(leftPanel.zMax() || 100, containerSize); + const rightMin = this.convertToPercentage(rightPanel.zMin() || 0, containerSize); + const rightMax = this.convertToPercentage(rightPanel.zMax() || 100, containerSize); + + let newLeftSize = startSizes[handleIndex] + deltaPercentage; + let newRightSize = startSizes[handleIndex + 1] - deltaPercentage; + + newLeftSize = Math.max(leftMin, Math.min(leftMax, newLeftSize)); + newRightSize = Math.max(rightMin, Math.min(rightMax, newRightSize)); + + const totalSize = newLeftSize + newRightSize; + const originalTotal = startSizes[handleIndex] + startSizes[handleIndex + 1]; + + if (Math.abs(totalSize - originalTotal) < 0.01) { + newSizes[handleIndex] = newLeftSize; + newSizes[handleIndex + 1] = newRightSize; + + this.panelSizes.set(newSizes); + + if (!this.zLazy()) { + this.updatePanelStyles(); + } + + this.zResize.emit({ sizes: newSizes, layout: this.zLayout() || 'horizontal' }); + } + } + + private endResize(): void { + this.isResizing.set(false); + this.activeHandleIndex.set(null); + + if (this.zLazy()) { + this.updatePanelStyles(); + } + + const sizes = [...this.panelSizes()]; + this.zResizeEnd.emit({ sizes, layout: this.zLayout() || 'horizontal' }); + } + + updatePanelStyles(): void { + const panels = this.panels(); + const sizes = this.panelSizes(); + const layout = this.zLayout(); + + panels.forEach((panel, index) => { + const size = sizes[index]; + if (size !== undefined) { + const element = panel.elementRef.nativeElement as HTMLElement; + if (layout === 'vertical') { + element.style.height = `${size}%`; + element.style.width = '100%'; + } else { + element.style.width = `${size}%`; + element.style.height = '100%'; + } + } + }); + } + + private getEventPosition(event: MouseEvent | TouchEvent): number { + const layout = this.zLayout(); + if (event instanceof MouseEvent) { + return layout === 'vertical' ? event.clientY : event.clientX; + } else { + const touch = event.touches[0]; + return layout === 'vertical' ? touch.clientY : touch.clientX; + } + } + + getContainerSize(): number { + const element = this.elementRef.nativeElement as HTMLElement; + const layout = this.zLayout(); + return layout === 'vertical' ? element.offsetHeight : element.offsetWidth; + } + + collapsePanel(index: number): void { + const panels = this.panels(); + const panel = panels[index]; + + if (!panel || !panel.zCollapsible()) return; + + const sizes = [...this.panelSizes()]; + const isCollapsed = sizes[index] === 0; + + if (isCollapsed) { + const containerSize = this.getContainerSize(); + const defaultSize = this.convertToPercentage(panel.zDefaultSize() || 100 / panels.length, containerSize); + sizes[index] = defaultSize; + + const totalOthers = sizes.reduce((sum, size, i) => (i !== index ? sum + size : sum), 0); + const scale = (100 - defaultSize) / totalOthers; + + sizes.forEach((size, i) => { + if (i !== index) { + sizes[i] = size * scale; + } + }); + } else { + const collapsedSize = sizes[index]; + sizes[index] = 0; + + const totalOthers = sizes.reduce((sum, size, i) => (i !== index ? sum + size : sum), 0); + const scale = (totalOthers + collapsedSize) / totalOthers; + + sizes.forEach((size, i) => { + if (i !== index) { + sizes[i] = size * scale; + } + }); + } + + this.panelSizes.set(sizes); + this.updatePanelStyles(); + this.zResize.emit({ sizes, layout: this.zLayout() || 'horizontal' }); + } + + ngOnDestroy(): void { + this.listeners.forEach(cleanup => cleanup()); + } +} \ No newline at end of file diff --git a/src/app/shared/components/resizable/resizable.variants.ts b/src/app/shared/components/resizable/resizable.variants.ts new file mode 100644 index 0000000..d02e115 --- /dev/null +++ b/src/app/shared/components/resizable/resizable.variants.ts @@ -0,0 +1,61 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const resizableVariants = cva('flex h-full w-full data-[layout=vertical]:flex-col overflow-hidden', { + variants: { + zLayout: { + horizontal: '', + vertical: '', + }, + }, + defaultVariants: { + zLayout: 'horizontal', + }, +}); + +export const resizablePanelVariants = cva('relative overflow-hidden flex-shrink-0 h-full', { + variants: { + zCollapsed: { + true: 'hidden', + false: '', + }, + }, + defaultVariants: { + zCollapsed: false, + }, +}); + +export const resizableHandleVariants = cva( + 'group relative flex flex-shrink-0 items-center justify-center bg-border transition-colors hover:bg-border/80 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1', + { + variants: { + zLayout: { + horizontal: 'w-[1px] min-w-[1px] cursor-col-resize after:absolute after:inset-y-0 after:left-1/2 after:w-4 after:-translate-x-1/2', + vertical: 'h-[1px] min-h-[1px] w-full cursor-row-resize after:absolute after:inset-x-0 after:top-1/2 after:h-4 after:-translate-y-1/2', + }, + zDisabled: { + true: 'cursor-default pointer-events-none opacity-50', + false: '', + }, + }, + defaultVariants: { + zLayout: 'horizontal', + zDisabled: false, + }, + }, +); + +export const resizableHandleIndicatorVariants = cva('absolute z-10 bg-muted-foreground/30 transition-colors group-hover:bg-muted-foreground/50 rounded-full', { + variants: { + zLayout: { + vertical: 'w-8 h-px', + horizontal: 'w-px h-8', + }, + }, + defaultVariants: { + zLayout: 'horizontal', + }, +}); + +export type ZardResizableVariants = VariantProps; +export type ZardResizablePanelVariants = VariantProps; +export type ZardResizableHandleVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/search-bar/search-bar.css b/src/app/shared/components/search-bar/search-bar.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/search-bar/search-bar.html b/src/app/shared/components/search-bar/search-bar.html new file mode 100644 index 0000000..af493bd --- /dev/null +++ b/src/app/shared/components/search-bar/search-bar.html @@ -0,0 +1,52 @@ +
    +
    + + + + + + + + + + @if (searchTerm().length > 0) { + + } +
    +
    diff --git a/src/app/shared/components/search-bar/search-bar.spec.ts b/src/app/shared/components/search-bar/search-bar.spec.ts new file mode 100644 index 0000000..72f6a71 --- /dev/null +++ b/src/app/shared/components/search-bar/search-bar.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SearchBar } from './search-bar'; + +describe('SearchBar', () => { + let component: SearchBar; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SearchBar] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SearchBar); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/search-bar/search-bar.ts b/src/app/shared/components/search-bar/search-bar.ts new file mode 100644 index 0000000..9f4ebe8 --- /dev/null +++ b/src/app/shared/components/search-bar/search-bar.ts @@ -0,0 +1,62 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + input, + output, + signal, + OnDestroy, +} from '@angular/core'; + +@Component({ + selector: 'app-search-bar', + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './search-bar.html', + styleUrl: './search-bar.css', +}) +export class SearchBar implements OnDestroy { + placeholder = input('Rechercher…'); + debounceMs = input(300); // Default 300ms debounce + + // Local signal to hold the current query string in the input field + searchTerm = signal(''); + + // Output event to notify parent component of the search query + search = output(); + + private debounceTimer: ReturnType | null = null; + + // Handles input change, updates the signal, and emits the new search term with debounce + handleInput(value: string) { + this.searchTerm.set(value); + this.debounceEmit(); + } + + private debounceEmit() { + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + this.debounceTimer = setTimeout(() => { + this.emitSearch(); + }, this.debounceMs()); + } + + emitSearch() { + this.search.emit(this.searchTerm().trim()); + } + + reset() { + this.searchTerm.set(''); + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + this.emitSearch(); + } + + ngOnDestroy() { + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + } +} diff --git a/src/app/shared/components/segmented/segmented.component.ts b/src/app/shared/components/segmented/segmented.component.ts new file mode 100644 index 0000000..12adf6a --- /dev/null +++ b/src/app/shared/components/segmented/segmented.component.ts @@ -0,0 +1,156 @@ +import { ChangeDetectionStrategy, Component, computed, contentChildren, effect, forwardRef, input, OnInit, output, signal, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { segmentedItemVariants, segmentedVariants, ZardSegmentedVariants } from './segmented.variants'; + +import type { ClassValue } from 'clsx'; + +export interface SegmentedOption { + value: string; + label: string; + disabled?: boolean; +} +@Component({ + selector: 'z-segmented-item', + standalone: true, + template: ``, + encapsulation: ViewEncapsulation.None, +}) +export class ZardSegmentedItemComponent { + readonly value = input.required(); + readonly label = input.required(); + readonly disabled = input(false); +} + +@Component({ + selector: 'z-segmented', + exportAs: 'zSegmented', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + @if (zOptions().length > 0) { + @for (option of zOptions(); track option.value) { + + } + } @else { + @for (item of items(); track item.value()) { + + } + } +
    + `, + host: { + '[class]': 'wrapperClasses()', + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardSegmentedComponent), + multi: true, + }, + ], +}) +export class ZardSegmentedComponent implements ControlValueAccessor, OnInit { + private readonly itemComponents = contentChildren(ZardSegmentedItemComponent); + + readonly class = input(''); + readonly zSize = input('default'); + readonly zOptions = input([]); + readonly zDefaultValue = input(''); + readonly zDisabled = input(false); + readonly zAriaLabel = input('Segmented control'); + + readonly zChange = output(); + + protected readonly selectedValue = signal(''); + protected readonly items = signal([]); + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onChange: (value: string) => void = () => {}; + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onTouched = () => {}; + + constructor() { + effect(() => { + this.items.set(this.itemComponents()); + }); + } + + ngOnInit() { + // Initialize with default value + if (this.zDefaultValue()) { + this.selectedValue.set(this.zDefaultValue()); + } + } + + protected readonly classes = computed(() => mergeClasses(segmentedVariants({ zSize: this.zSize() }), this.class())); + + protected readonly wrapperClasses = computed(() => 'inline-block'); + + protected getItemClasses(value: string): string { + return segmentedItemVariants({ + zSize: this.zSize(), + isActive: this.isSelected(value), + }); + } + + protected isSelected(value: string): boolean { + return this.selectedValue() === value; + } + + protected selectOption(value: string) { + if (this.zDisabled()) return; + + const option = this.zOptions().find(opt => opt.value === value); + const item = this.items().find(item => item.value() === value); + + if ((option && option.disabled) || (item && item.disabled())) return; + + this.selectedValue.set(value); + this.onChange(value); + this.onTouched(); + this.zChange.emit(value); + } + + // ControlValueAccessor implementation + writeValue(value: string): void { + this.selectedValue.set(value || ''); + } + + registerOnChange(fn: (value: string) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(_isDisabled: boolean): void { + // Handled by zDisabled input + } +} \ No newline at end of file diff --git a/src/app/shared/components/segmented/segmented.variants.ts b/src/app/shared/components/segmented/segmented.variants.ts new file mode 100644 index 0000000..b3bc0db --- /dev/null +++ b/src/app/shared/components/segmented/segmented.variants.ts @@ -0,0 +1,38 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const segmentedVariants = cva('inline-flex items-center justify-center rounded-md bg-muted p-1 text-muted-foreground', { + variants: { + zSize: { + sm: 'h-9 text-xs', + default: 'h-10 text-sm', + lg: 'h-12 text-base', + }, + }, + defaultVariants: { + zSize: 'default', + }, +}); + +export const segmentedItemVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', + { + variants: { + zSize: { + sm: 'px-2 py-1 text-xs', + default: 'px-3 py-1.5 text-sm', + lg: 'px-4 py-2 text-base', + }, + isActive: { + true: 'bg-background text-foreground shadow-sm', + false: 'hover:bg-muted/50', + }, + }, + defaultVariants: { + zSize: 'default', + isActive: false, + }, + }, +); + +export type ZardSegmentedVariants = VariantProps; +export type ZardSegmentedItemVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/select/select-item.component.ts b/src/app/shared/components/select/select-item.component.ts new file mode 100644 index 0000000..27bc5a9 --- /dev/null +++ b/src/app/shared/components/select/select-item.component.ts @@ -0,0 +1,71 @@ +import { + ChangeDetectionStrategy, + Component, + computed, + ElementRef, + forwardRef, + inject, + input, + linkedSignal, +} from '@angular/core'; + +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { selectItemVariants } from './select.variants'; + +// Interface to avoid circular dependency +interface SelectHost { + selectedValue(): string; + selectItem(value: string, label: string): void; +} + +@Component({ + selector: 'z-select-item, [z-select-item]', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [], + host: { + '[class]': 'classes()', + '[attr.value]': 'zValue()', + role: 'option', + tabindex: '-1', + '[attr.data-disabled]': 'zDisabled() ? "" : null', + '[attr.data-selected]': 'isSelected() ? "" : null', + '[attr.aria-selected]': 'isSelected()', + '(click)': 'onClick()', + }, + template: ` + + @if (isSelected()) { + + } + + + `, +}) +export class ZardSelectItemComponent { + readonly zValue = input.required(); + readonly zDisabled = input(false, { transform }); + readonly class = input(''); + + private select: SelectHost | null = null; + readonly elementRef = inject(ElementRef); + readonly label = linkedSignal(() => { + const element = this.elementRef?.nativeElement; + return (element?.textContent || element?.innerText)?.trim() ?? ''; + }); + + protected readonly classes = computed(() => mergeClasses(selectItemVariants(), this.class())); + + protected readonly isSelected = computed(() => this.select?.selectedValue() === this.zValue()); + + setSelectHost(selectHost: SelectHost) { + this.select = selectHost; + } + + onClick() { + if (this.zDisabled() || !this.select) { + return; + } + this.select.selectItem(this.zValue(), this.label()); + } +} diff --git a/src/app/shared/components/select/select.component.ts b/src/app/shared/components/select/select.component.ts new file mode 100644 index 0000000..e2d11cf --- /dev/null +++ b/src/app/shared/components/select/select.component.ts @@ -0,0 +1,478 @@ +import { + AfterContentInit, + ChangeDetectionStrategy, + Component, + computed, + contentChildren, + ElementRef, + forwardRef, + HostListener, + inject, + input, + linkedSignal, + OnDestroy, + OnInit, + output, + PLATFORM_ID, + signal, + TemplateRef, + viewChild, + ViewContainerRef, +} from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; +import { Overlay, OverlayModule, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { TemplatePortal } from '@angular/cdk/portal'; + +import { + selectContentVariants, + selectTriggerVariants, + ZardSelectTriggerVariants, +} from './select.variants'; +import { mergeClasses, transform } from '@shared/utils/merge-classes'; +import { ZardSelectItemComponent } from './select-item.component'; + +type OnTouchedType = () => void; +type OnChangeType = (value: string) => void; + +@Component({ + selector: 'z-select, [z-select]', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [OverlayModule], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardSelectComponent), + multi: true, + }, + ], + host: { + '[attr.data-disabled]': 'zDisabled() ? "" : null', + '[attr.data-state]': 'isOpen() ? "open" : "closed"', + class: 'relative inline-block w-full', + }, + template: ` + + + +
    +
    + +
    +
    +
    + `, +}) +export class ZardSelectComponent + implements ControlValueAccessor, OnInit, AfterContentInit, OnDestroy +{ + private elementRef = inject(ElementRef); + private overlay = inject(Overlay); + private overlayPositionBuilder = inject(OverlayPositionBuilder); + private viewContainerRef = inject(ViewContainerRef); + private platformId = inject(PLATFORM_ID); + + readonly dropdownTemplate = viewChild.required>('dropdownTemplate'); + + readonly selectItems = contentChildren(ZardSelectItemComponent); + + private overlayRef?: OverlayRef; + private portal?: TemplatePortal; + + readonly zSize = input('default'); + readonly zDisabled = input(false, { transform }); + readonly zPlaceholder = input('Select an option...'); + readonly zValue = input(''); + readonly zLabel = input(''); + readonly class = input(''); + + readonly zSelectionChange = output(); + + readonly isOpen = signal(false); + private readonly _selectedValue = signal(''); + private readonly _selectedLabel = linkedSignal(() => { + const currentValue = this.selectedValue(); + if (!this.zLabel() && currentValue) { + const matchingItem = this.selectItems()?.find((item) => item.zValue() === currentValue); + if (matchingItem) { + return matchingItem.label(); + } + } + return ''; + }); + readonly focusedIndex = signal(-1); + + // Use computed to derive the effective selected value from input or internal state + readonly selectedValue = computed(() => this.zValue() || this._selectedValue()); + + // Compute the label based on selected value + readonly selectedLabel = computed(() => { + const manualLabel = this.zLabel(); + if (manualLabel) return manualLabel; + + return this._selectedLabel() || this.selectedValue(); + }); + + private onChange: OnChangeType = (_value: string) => { + // ControlValueAccessor onChange callback + }; + + private onTouched: OnTouchedType = () => { + // ControlValueAccessor onTouched callback + }; + + protected readonly triggerClasses = computed(() => + mergeClasses( + selectTriggerVariants({ + zSize: this.zSize(), + }), + this.class() + ) + ); + + protected readonly contentClasses = computed(() => mergeClasses(selectContentVariants())); + + ngOnInit() { + // Initialize selected value from input immediately + const inputValue = this.zValue(); + if (inputValue) { + this._selectedValue.set(inputValue); + } + } + + ngAfterContentInit() { + // Setup select host reference for each item + // Note: This might not catch all items if they're rendered dynamically in the overlay + // So we also call setupSelectItems() when opening the overlay + this.setupSelectItems(); + } + + ngOnDestroy() { + this.destroyOverlay(); + } + + @HostListener('document:click', ['$event']) + onDocumentClick(event: Event) { + if (!this.elementRef.nativeElement.contains(event.target as Node)) { + this.close(); + } + } + + onTriggerKeydown(event: KeyboardEvent) { + switch (event.key) { + case 'Enter': + case ' ': + case 'ArrowDown': + case 'ArrowUp': + event.preventDefault(); + if (!this.isOpen()) { + this.open(); + } + break; + case 'Escape': + if (this.isOpen()) { + event.preventDefault(); + this.close(); + } + break; + } + } + + onDropdownKeydown(event: KeyboardEvent) { + const items = this.getSelectItems(); + + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + this.navigateItems(1, items); + break; + case 'ArrowUp': + event.preventDefault(); + this.navigateItems(-1, items); + break; + case 'Enter': + case ' ': + event.preventDefault(); + this.selectFocusedItem(items); + break; + case 'Escape': + event.preventDefault(); + this.close(); + this.focusButton(); + break; + case 'Home': + event.preventDefault(); + this.focusFirstItem(items); + break; + case 'End': + event.preventDefault(); + this.focusLastItem(items); + break; + } + } + + toggle() { + if (this.zDisabled()) return; + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + this.isOpen() ? this.close() : this.open(); + } + + open() { + if (this.isOpen()) return; + + // Create overlay if it doesn't exist + if (!this.overlayRef) { + this.createOverlay(); + } + + if (!this.overlayRef) return; + + this.portal = new TemplatePortal(this.dropdownTemplate(), this.viewContainerRef); + this.overlayRef.attach(this.portal); + this.isOpen.set(true); + + // Focus dropdown after opening and position on selected item + // Also setup select host reference for items (they might be new instances in the overlay) + setTimeout(() => { + this.setupSelectItems(); + this.focusDropdown(); + this.focusSelectedItem(); + }, 0); + } + + private setupSelectItems() { + // Setup select host reference for each item + // This needs to be called when the overlay opens because items are projected into the overlay + this.selectItems().forEach((item) => { + item.setSelectHost({ + selectedValue: () => this.selectedValue(), + selectItem: (value: string, label: string) => this.selectItem(value, label), + }); + }); + } + + close() { + if (this.overlayRef?.hasAttached()) { + this.overlayRef.detach(); + } + this.isOpen.set(false); + this.focusedIndex.set(-1); + this.onTouched(); + } + + selectItem(value: string, label: string) { + if (value === undefined || value === null || value === '') { + console.warn('Attempted to select item with invalid value:', { value, label }); + return; + } + + this._selectedValue.set(value); + this._selectedLabel.set(label || value); // Fallback to value if label is empty + this.onChange(value); + this.zSelectionChange.emit(value); + this.close(); + + // Return focus to the button after selection + setTimeout(() => { + this.focusButton(); + }, 0); + } + + private createOverlay() { + if (this.overlayRef) return; // Already created + + if (isPlatformBrowser(this.platformId)) { + try { + const positionStrategy = this.overlayPositionBuilder + .flexibleConnectedTo(this.elementRef) + .withPositions([ + { + originX: 'start', + originY: 'bottom', + overlayX: 'start', + overlayY: 'top', + offsetY: 4, + }, + { + originX: 'start', + originY: 'top', + overlayX: 'start', + overlayY: 'bottom', + offsetY: -4, + }, + ]) + .withPush(false); + + const elementWidth = this.elementRef.nativeElement.offsetWidth || 200; + + this.overlayRef = this.overlay.create({ + positionStrategy, + hasBackdrop: false, + scrollStrategy: this.overlay.scrollStrategies.reposition(), + width: elementWidth, + minWidth: elementWidth, + maxHeight: 384, // max-h-96 equivalent + }); + } catch (error) { + console.error('Error creating overlay:', error); + } + } + } + + private destroyOverlay() { + if (this.overlayRef) { + this.overlayRef.dispose(); + this.overlayRef = undefined; + } + } + + private getSelectItems(): HTMLElement[] { + if (!this.overlayRef?.hasAttached()) return []; + const dropdownElement = this.overlayRef.overlayElement; + return Array.from(dropdownElement.querySelectorAll('z-select-item, [z-select-item]')).filter( + (item: Element) => !item.hasAttribute('data-disabled') + ) as HTMLElement[]; + } + + private navigateItems(direction: number, items: HTMLElement[]) { + if (items.length === 0) return; + + const currentIndex = this.focusedIndex(); + let nextIndex = currentIndex + direction; + + if (nextIndex < 0) { + nextIndex = items.length - 1; + } else if (nextIndex >= items.length) { + nextIndex = 0; + } + + this.focusedIndex.set(nextIndex); + this.updateItemFocus(items, nextIndex); + } + + private selectFocusedItem(items: HTMLElement[]) { + const currentIndex = this.focusedIndex(); + if (currentIndex >= 0 && currentIndex < items.length) { + const item = items[currentIndex]; + const value = item.getAttribute('value'); + const label = item.textContent?.trim() || ''; + + if (value === null || value === undefined) { + console.warn('No value attribute found on selected item:', item); + return; + } + + this.selectItem(value, label); + } + } + + private focusFirstItem(items: HTMLElement[]) { + if (items.length > 0) { + this.focusedIndex.set(0); + this.updateItemFocus(items, 0); + } + } + + private focusLastItem(items: HTMLElement[]) { + if (items.length > 0) { + const lastIndex = items.length - 1; + this.focusedIndex.set(lastIndex); + this.updateItemFocus(items, lastIndex); + } + } + + private updateItemFocus(items: HTMLElement[], focusedIndex: number) { + items.forEach((item, index) => { + if (index === focusedIndex) { + item.focus(); + item.setAttribute('aria-selected', 'true'); + } else { + item.removeAttribute('aria-selected'); + } + }); + } + + private focusDropdown() { + if (this.overlayRef?.hasAttached()) { + const dropdownElement = this.overlayRef.overlayElement.querySelector( + '[role="listbox"]' + ) as HTMLElement; + if (dropdownElement) { + dropdownElement.focus(); + } + } + } + + private focusButton() { + const button = this.elementRef.nativeElement.querySelector('button'); + if (button) { + button.focus(); + } + } + + private focusSelectedItem() { + const items = this.getSelectItems(); + if (items.length === 0) return; + + // Find the index of the currently selected item + const selectedValue = this.selectedValue(); + let selectedIndex = -1; + + if (selectedValue) { + selectedIndex = items.findIndex((item) => item.getAttribute('value') === selectedValue); + } + + // If no item is selected, focus the first item + if (selectedIndex === -1) { + selectedIndex = 0; + } + + this.focusedIndex.set(selectedIndex); + this.updateItemFocus(items, selectedIndex); + } + + // ControlValueAccessor implementation + writeValue(value: string | null): void { + // Convert null/undefined to empty string, but preserve empty string + const stringValue = value != null ? String(value) : ''; + this._selectedValue.set(stringValue); + } + + registerOnChange(fn: (value: string) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(): void { + // The disabled state is handled by the disabled input + } +} diff --git a/src/app/shared/components/select/select.variants.ts b/src/app/shared/components/select/select.variants.ts new file mode 100644 index 0000000..2ab5c15 --- /dev/null +++ b/src/app/shared/components/select/select.variants.ts @@ -0,0 +1,29 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const selectTriggerVariants = cva( + 'flex w-full items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none cursor-pointer focus-visible:ring-[3px] focus-visible:border-ring focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground [&_svg:not([class*="text-"])]:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*="size-"])]:size-4', + { + variants: { + zSize: { + sm: 'h-8 text-xs', + default: 'h-9 text-sm', + lg: 'h-10 text-base', + }, + }, + defaultVariants: { + zSize: 'default', + }, + }, +); + +export const selectContentVariants = cva( + 'z-[9999] max-h-96 min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95', +); + +export const selectItemVariants = cva( + 'relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed data-[disabled]:hover:bg-transparent data-[disabled]:hover:text-current [&_svg:not([class*="text-"])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*="size-"])]:size-4', +); + +export type ZardSelectTriggerVariants = VariantProps; +export type ZardSelectContentVariants = VariantProps; +export type ZardSelectItemVariants = VariantProps; diff --git a/src/app/shared/components/sheet/sheet-ref.ts b/src/app/shared/components/sheet/sheet-ref.ts new file mode 100644 index 0000000..b11f21b --- /dev/null +++ b/src/app/shared/components/sheet/sheet-ref.ts @@ -0,0 +1,97 @@ +import { filter, fromEvent, Subject, takeUntil } from 'rxjs'; + +import { OverlayRef } from '@angular/cdk/overlay'; +import { isPlatformBrowser } from '@angular/common'; +import { EventEmitter, Inject, PLATFORM_ID } from '@angular/core'; + +import { ZardSheetComponent, ZardSheetOptions } from './sheet.component'; + +const enum eTriggerAction { + CANCEL = 'cancel', + OK = 'ok', +} + +export class ZardSheetRef { + private destroy$ = new Subject(); + private isClosing = false; + protected result?: R; + componentInstance: T | null = null; + + constructor( + private overlayRef: OverlayRef, + private config: ZardSheetOptions, + private containerInstance: ZardSheetComponent, + @Inject(PLATFORM_ID) private platformId: object, + ) { + this.containerInstance.cancelTriggered.subscribe(() => this.trigger(eTriggerAction.CANCEL)); + this.containerInstance.okTriggered.subscribe(() => this.trigger(eTriggerAction.OK)); + + if ((this.config.zMaskClosable ?? true) && isPlatformBrowser(this.platformId)) { + this.overlayRef + .outsidePointerEvents() + .pipe(takeUntil(this.destroy$)) + .subscribe(() => this.close()); + } + + if (isPlatformBrowser(this.platformId)) { + fromEvent(document, 'keydown') + .pipe( + filter(event => event.key === 'Escape'), + takeUntil(this.destroy$), + ) + .subscribe(() => this.close()); + } + } + + close(result?: R) { + if (this.isClosing) { + return; + } + + this.isClosing = true; + this.result = result; + this.containerInstance.state.set('closed'); + + const element = this.containerInstance.getNativeElement(); + const onAnimationEnd = () => { + element.removeEventListener('animationend', onAnimationEnd); + + if (this.overlayRef) { + if (this.overlayRef.hasAttached()) { + this.overlayRef.detachBackdrop(); + } + this.overlayRef.dispose(); + } + + if (!this.destroy$.closed) { + this.destroy$.next(); + this.destroy$.complete(); + } + }; + + element.addEventListener('animationend', onAnimationEnd); + + setTimeout(() => { + onAnimationEnd(); + }, 300); + } + + private trigger(action: eTriggerAction) { + const trigger = { ok: this.config.zOnOk, cancel: this.config.zOnCancel }[action]; + + if (trigger instanceof EventEmitter) { + trigger.emit(this.getContentComponent()); + } else if (typeof trigger === 'function') { + const result = trigger(this.getContentComponent()) as R; + this.closeWithResult(result); + } else this.close(); + } + + private getContentComponent(): T { + return this.componentInstance as T; + } + + private closeWithResult(result: R): void { + if (result !== false) this.close(result); + } +} diff --git a/src/app/shared/components/sheet/sheet.component.ts b/src/app/shared/components/sheet/sheet.component.ts new file mode 100644 index 0000000..929da2e --- /dev/null +++ b/src/app/shared/components/sheet/sheet.component.ts @@ -0,0 +1,177 @@ +import { OverlayModule } from '@angular/cdk/overlay'; +import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, PortalModule, TemplatePortal } from '@angular/cdk/portal'; +import { + ChangeDetectionStrategy, + Component, + ComponentRef, + computed, + ElementRef, + EmbeddedViewRef, + EventEmitter, + inject, + output, + signal, + TemplateRef, + Type, + viewChild, + ViewContainerRef, +} from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { ZardButtonComponent } from '../button/button.component'; +import { ZardSheetRef } from './sheet-ref'; +import { sheetVariants, ZardSheetVariants } from './sheet.variants'; + +const noopFun = () => void 0; +export type OnClickCallback = (instance: T) => false | void | object; +export class ZardSheetOptions { + zCancelIcon?: string; + zCancelText?: string | null; + zClosable?: boolean; + zContent?: string | TemplateRef | Type; + zCustomClasses?: string; + zData?: U; + zDescription?: string; + zHeight?: string; + zHideFooter?: boolean; + zMaskClosable?: boolean; + zOkDestructive?: boolean; + zOkDisabled?: boolean; + zOkIcon?: string; + zOkText?: string | null; + zOnCancel?: EventEmitter | OnClickCallback = noopFun; + zOnOk?: EventEmitter | OnClickCallback = noopFun; + zSide?: ZardSheetVariants['zSide'] = 'left'; + zSize?: ZardSheetVariants['zSize'] = 'default'; + zTitle?: string | TemplateRef; + zViewContainerRef?: ViewContainerRef; + zWidth?: string; +} + +@Component({ + selector: 'z-sheet', + exportAs: 'zSheet', + imports: [OverlayModule, PortalModule, ZardButtonComponent], + template: ` + @if (config.zClosable || config.zClosable === undefined) { + + } + + @if (config.zTitle || config.zDescription) { +
    + @if (config.zTitle) { +

    {{ config.zTitle }}

    + + @if (config.zDescription) { +

    {{ config.zDescription }}

    + } + } +
    + } + +
    + + + @if (isStringContent) { +
    + } +
    + + @if (!config.zHideFooter) { +
    + @if (config.zOkText !== null) { + + } + + @if (config.zCancelText !== null) { + + } +
    + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + 'data-slot': 'sheet', + '[class]': 'classes()', + '[attr.data-state]': 'state()', + '[style.width]': 'config.zWidth ? config.zWidth + " !important" : null', + '[style.height]': 'config.zHeight ? config.zHeight + " !important" : null', + }, +}) +export class ZardSheetComponent extends BasePortalOutlet { + private readonly host = inject(ElementRef); + protected readonly config = inject(ZardSheetOptions); + + protected readonly classes = computed(() => { + const zSize = this.config.zWidth || this.config.zHeight ? 'custom' : this.config.zSize; + + return mergeClasses( + sheetVariants({ + zSide: this.config.zSide, + zSize, + }), + this.config.zCustomClasses, + ); + }); + public sheetRef?: ZardSheetRef; + + protected readonly isStringContent = typeof this.config.zContent === 'string'; + + readonly portalOutlet = viewChild.required(CdkPortalOutlet); + + readonly okTriggered = output(); + readonly cancelTriggered = output(); + readonly state = signal<'closed' | 'open'>('closed'); + + constructor() { + super(); + } + + getNativeElement(): HTMLElement { + return this.host.nativeElement; + } + + attachComponentPortal(portal: ComponentPortal): ComponentRef { + if (this.portalOutlet()?.hasAttached()) { + throw Error('Attempting to attach modal content after content is already attached'); + } + return this.portalOutlet()?.attachComponentPortal(portal); + } + + attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { + if (this.portalOutlet()?.hasAttached()) { + throw Error('Attempting to attach modal content after content is already attached'); + } + + return this.portalOutlet()?.attachTemplatePortal(portal); + } + + onOkClick() { + this.okTriggered.emit(); + } + + onCloseClick() { + this.cancelTriggered.emit(); + } +} diff --git a/src/app/shared/components/sheet/sheet.module.ts b/src/app/shared/components/sheet/sheet.module.ts new file mode 100644 index 0000000..4f5c003 --- /dev/null +++ b/src/app/shared/components/sheet/sheet.module.ts @@ -0,0 +1,22 @@ +import { OverlayModule } from '@angular/cdk/overlay'; +import { PortalModule } from '@angular/cdk/portal'; +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { ZardButtonComponent } from '../button/button.component'; +import { ZardSheetComponent } from './sheet.component'; +import { ZardSheetService } from './sheet.service'; + +const components = [CommonModule, ZardButtonComponent, ZardSheetComponent, OverlayModule, PortalModule]; + +@NgModule({ + imports: components, + exports: components, +}) +export class ZardBreadcrumbModule {} + +@NgModule({ + imports: [CommonModule, ZardButtonComponent, ZardSheetComponent, OverlayModule, PortalModule], + providers: [ZardSheetService], +}) +export class ZardSheetModule {} diff --git a/src/app/shared/components/sheet/sheet.service.ts b/src/app/shared/components/sheet/sheet.service.ts new file mode 100644 index 0000000..b42275f --- /dev/null +++ b/src/app/shared/components/sheet/sheet.service.ts @@ -0,0 +1,96 @@ +import { inject, Injectable, InjectionToken, Injector, PLATFORM_ID, TemplateRef } from '@angular/core'; +import { ComponentType, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal'; +import { isPlatformBrowser } from '@angular/common'; + +import { ZardSheetComponent, ZardSheetOptions } from './sheet.component'; +import { ZardSheetRef } from './sheet-ref'; + +type ContentType = ComponentType | TemplateRef | string; +export const Z_MODAL_DATA = new InjectionToken('Z_MODAL_DATA'); + +@Injectable({ + providedIn: 'root', +}) +export class ZardSheetService { + private overlay = inject(Overlay); + private injector = inject(Injector); + private platformId = inject(PLATFORM_ID); + + create(config: ZardSheetOptions): ZardSheetRef { + return this.open(config.zContent as ComponentType, config); + } + + private open(componentOrTemplateRef: ContentType, config: ZardSheetOptions) { + const overlayRef = this.createOverlay(); + + if (!overlayRef) { + // Return a mock sheet ref for SSR environments + return new ZardSheetRef(undefined as any, config, undefined as any, this.platformId); + } + + const sheetContainer = this.attachSheetContainer(overlayRef, config); + + const sheetRef = this.attachSheetContent(componentOrTemplateRef, sheetContainer, overlayRef, config); + sheetContainer.sheetRef = sheetRef; + + return sheetRef; + } + + private createOverlay(): OverlayRef | undefined { + if (isPlatformBrowser(this.platformId)) { + const overlayConfig = new OverlayConfig({ + hasBackdrop: true, + positionStrategy: this.overlay.position().global(), + }); + + return this.overlay.create(overlayConfig); + } + return undefined; + } + + private attachSheetContainer(overlayRef: OverlayRef, config: ZardSheetOptions) { + const injector = Injector.create({ + parent: this.injector, + providers: [ + { provide: OverlayRef, useValue: overlayRef }, + { provide: ZardSheetOptions, useValue: config }, + ], + }); + + const containerPortal = new ComponentPortal>(ZardSheetComponent, config.zViewContainerRef, injector); + const containerRef = overlayRef.attach>(containerPortal); + containerRef.instance.state.set('open'); + + return containerRef.instance; + } + + private attachSheetContent(componentOrTemplateRef: ContentType, sheetContainer: ZardSheetComponent, overlayRef: OverlayRef, config: ZardSheetOptions) { + const sheetRef = new ZardSheetRef(overlayRef, config, sheetContainer, this.platformId); + + if (componentOrTemplateRef instanceof TemplateRef) { + sheetContainer.attachTemplatePortal( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + new TemplatePortal(componentOrTemplateRef, null!, { + sheetRef: sheetRef, + } as any), + ); + } else if (typeof componentOrTemplateRef !== 'string') { + const injector = this.createInjector(sheetRef, config); + const contentRef = sheetContainer.attachComponentPortal(new ComponentPortal(componentOrTemplateRef, config.zViewContainerRef, injector)); + sheetRef.componentInstance = contentRef.instance; + } + + return sheetRef; + } + + private createInjector(sheetRef: ZardSheetRef, config: ZardSheetOptions) { + return Injector.create({ + parent: this.injector, + providers: [ + { provide: ZardSheetRef, useValue: sheetRef }, + { provide: Z_MODAL_DATA, useValue: config.zData }, + ], + }); + } +} diff --git a/src/app/shared/components/sheet/sheet.variants.ts b/src/app/shared/components/sheet/sheet.variants.ts new file mode 100644 index 0000000..4bd1577 --- /dev/null +++ b/src/app/shared/components/sheet/sheet.variants.ts @@ -0,0 +1,58 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const sheetVariants = cva( + 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500', + { + variants: { + zSide: { + right: 'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 border-l', + left: 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 border-r', + top: 'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 border-b', + bottom: 'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 border-t', + }, + zSize: { + default: '', + sm: '', + lg: '', + custom: '', + }, + }, + compoundVariants: [ + { + zSide: ['left', 'right'], + zSize: 'default', + class: 'w-3/4 sm:max-w-sm h-full', + }, + { + zSide: ['left', 'right'], + zSize: 'sm', + class: 'w-1/2 sm:max-w-xs h-full', + }, + { + zSide: ['left', 'right'], + zSize: 'lg', + class: 'w-full sm:max-w-lg h-full', + }, + { + zSide: ['top', 'bottom'], + zSize: 'default', + class: 'h-auto', + }, + { + zSide: ['top', 'bottom'], + zSize: 'sm', + class: 'h-1/3', + }, + { + zSide: ['top', 'bottom'], + zSize: 'lg', + class: 'h-3/4', + }, + ], + defaultVariants: { + zSide: 'right', + zSize: 'default', + }, + }, +); +export type ZardSheetVariants = VariantProps; diff --git a/src/app/shared/components/skeleton/skeleton.component.ts b/src/app/shared/components/skeleton/skeleton.component.ts new file mode 100644 index 0000000..dd20936 --- /dev/null +++ b/src/app/shared/components/skeleton/skeleton.component.ts @@ -0,0 +1,21 @@ +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; +import type { ClassValue } from 'clsx'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { skeletonVariants } from './skeleton.variants'; + +@Component({ + selector: 'z-skeleton', + exportAs: 'zSkeleton', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: `
    `, + host: { + class: 'block', + }, +}) +export class ZardSkeletonComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(skeletonVariants(), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/skeleton/skeleton.variants.ts b/src/app/shared/components/skeleton/skeleton.variants.ts new file mode 100644 index 0000000..1e69a08 --- /dev/null +++ b/src/app/shared/components/skeleton/skeleton.variants.ts @@ -0,0 +1,4 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const skeletonVariants = cva('bg-accent animate-pulse rounded-md'); +export type ZardSkeletonVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/slider/slider.component.ts b/src/app/shared/components/slider/slider.component.ts new file mode 100644 index 0000000..0dcd9a8 --- /dev/null +++ b/src/app/shared/components/slider/slider.component.ts @@ -0,0 +1,389 @@ +import { fromEvent, map, Subject, switchMap, takeUntil, tap } from 'rxjs'; + +import { DOCUMENT } from '@angular/common'; +import { + AfterViewInit, + booleanAttribute, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + computed, + ElementRef, + forwardRef, + inject, + input, + linkedSignal, + numberAttribute, + OnChanges, + OnDestroy, + output, + signal, + SimpleChanges, + viewChild, + ViewEncapsulation, +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { clamp, convertValueToPercentage, roundToStep } from '@shared/utils/number'; +import { mergeClasses } from '@shared/utils/merge-classes'; +import { sliderOrientationVariants, sliderRangeVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants } from './slider.variants'; + +import type { ClassValue } from 'clsx'; + +type OnTouchedType = () => void; +type OnChangeType = (value: number) => void; + +@Component({ + selector: 'z-slider-track', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [], + template: ` + + + + `, + host: { + '[class]': '"data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full"', + '[attr.data-orientation]': 'orientation()', + }, +}) +export class ZSliderTrackComponent { + readonly orientation = input<'horizontal' | 'vertical'>('horizontal'); + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(sliderTrackVariants({ zOrientation: this.orientation() }), this.class())); + + private readonly trackEl = viewChild.required>('track'); + + get nativeElement(): HTMLElement { + return this.trackEl().nativeElement; + } +} + +@Component({ + selector: 'z-slider-range', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [], + template: ` + + `, +}) +export class ZSliderRangeComponent { + readonly percent = input(0); + + readonly orientation = input<'horizontal' | 'vertical'>('horizontal'); + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(sliderRangeVariants({ zOrientation: this.orientation() }), this.class())); +} + +@Component({ + selector: 'z-slider-thumb', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [], + template: ` + + `, + host: { + '[class]': 'orientationClasses()', + '[style.left]': 'orientation() === "horizontal" ? "calc(" + percent() + "% + " + offset() + "px)" : null', + '[style.bottom]': 'orientation() === "vertical" ? "calc(" + percent() + "% + " + offset() + "px)" : null', + }, +}) +export class ZSliderThumbComponent { + readonly value = input(0); + readonly min = input(0); + readonly max = input(100); + readonly disabled = input(false); + readonly percent = input(0); + readonly offset = input(0); + + readonly orientation = input<'horizontal' | 'vertical'>('horizontal'); + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(sliderThumbVariants(), this.class())); + protected readonly orientationClasses = computed(() => mergeClasses(sliderOrientationVariants({ zOrientation: this.orientation() }))); + + private readonly thumbEl = viewChild.required>('thumb'); + + get nativeElement(): HTMLElement { + return this.thumbEl().nativeElement; + } +} + +@Component({ + selector: 'z-slider', + exportAs: 'zSlider', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [ZSliderTrackComponent, ZSliderRangeComponent, ZSliderThumbComponent], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardSliderComponent), + multi: true, + }, + ], + template: ` + + + + + + + + `, + host: { + '[class]': 'classes()', + '[attr.data-orientation]': 'zOrientation()', + '[attr.aria-disabled]': 'disabled() ? true : null', + '[attr.data-disabled]': 'disabled() ? true : null', + }, +}) +export class ZardSliderComponent implements ControlValueAccessor, AfterViewInit, OnChanges, OnDestroy { + readonly zMin = input(0, { transform: numberAttribute }); + readonly zMax = input(100, { transform: numberAttribute }); + readonly zDefault = input(0, { transform: numberAttribute }); + readonly zValue = input(null, { transform: numberAttribute }); + readonly zStep = input(1, { transform: numberAttribute }); + readonly zDisabled = input(false, { transform: booleanAttribute }); + + readonly zOrientation = input<'horizontal' | 'vertical'>('horizontal'); + readonly class = input(''); + + readonly onSlide = output(); + + readonly thumbRef = viewChild.required(ZSliderThumbComponent); + readonly trackRef = viewChild.required(ZSliderTrackComponent); + + private elementRef = inject>(ElementRef); + private cdr = inject(ChangeDetectorRef); + private document = inject(DOCUMENT); + + protected readonly classes = computed(() => mergeClasses(sliderVariants({ orientation: this.zOrientation() }), this.class())); + protected disabled = linkedSignal(() => this.zDisabled()); + + readonly percentValue = signal(50); + readonly lastEmittedValue = signal(0); + + readonly thumbOffset = signal(0); + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onTouched: OnTouchedType = () => {}; + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onChange: OnChangeType = (value: number) => {}; + + private destroy$ = new Subject(); + + ngOnChanges(changes: SimpleChanges): void { + if ('zValue' in changes && !changes['zValue'].firstChange) { + const value = this.zValue(); + if (value !== this.lastEmittedValue()) { + this.setInitialValue(); + } + } + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit() { + const pointerDown$ = fromEvent(this.elementRef.nativeElement, 'pointerdown').pipe( + tap(event => { + if (this.disabled()) return; + + const target = event.target as HTMLElement; + const isThumb = this.thumbRef().nativeElement.contains(target); + const isTrack = this.trackRef().nativeElement.contains(target); + + if (isTrack && !isThumb) { + const coord = this.zOrientation() === 'vertical' ? event.clientY : event.clientX; + const clickPercentage = this.calculatePercentage(coord); + this.updateSliderFromPercentage(clickPercentage); + this.onTouched(); + requestAnimationFrame(() => { + this.thumbRef().nativeElement.focus(); + }); + } + }), + ); + + const pointerMove$ = fromEvent(this.document, 'pointermove'); + const pointerUp$ = fromEvent(this.document, 'pointerup'); + + pointerDown$ + .pipe( + switchMap(() => + pointerMove$.pipe( + takeUntil(pointerUp$), + takeUntil(this.destroy$), + map(event => { + const coord = this.zOrientation() === 'vertical' ? event.clientY : event.clientX; + return this.calculatePercentage(coord); + }), + ), + ), + takeUntil(this.destroy$), + ) + .subscribe(percentage => { + if (this.disabled()) return; + this.updateSliderFromPercentage(percentage); + this.onTouched(); + }); + + this.setInitialValue(); + } + + writeValue(value: number): void { + if (value == null) { + this.setInitialValue(); + return; + } + + const min = this.zMin(); + const max = this.zMax(); + const step = this.zStep(); + + const clampedValue = clamp(value, [min, max]); + const roundedValue = roundToStep(clampedValue, min, step); + + if (roundedValue === this.lastEmittedValue()) return; + + this.percentValue.set(convertValueToPercentage(roundedValue, min, max)); + this.lastEmittedValue.set(roundedValue); + this.cdr.markForCheck(); + } + + registerOnChange(fn: (value: number) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled.set(isDisabled); + this.cdr.markForCheck(); + } + + handleKeydown(event: KeyboardEvent): void { + if (this.disabled()) return; + + const percent = this.percentValue(); + const rawValue = this.zMin() + ((this.zMax() - this.zMin()) * percent) / 100; + const currentValue = roundToStep(rawValue, this.zMin(), this.zStep()); + + let newValue = currentValue; + + switch (event.key) { + case 'Home': + newValue = this.zMin(); + break; + case 'End': + newValue = this.zMax(); + break; + case 'ArrowLeft': + newValue = Math.max(currentValue - this.zStep(), this.zMin()); + break; + case 'ArrowRight': + newValue = Math.min(currentValue + this.zStep(), this.zMax()); + break; + case 'ArrowDown': + newValue = Math.max(currentValue - this.zStep(), this.zMin()); + break; + case 'ArrowUp': + newValue = Math.min(currentValue + this.zStep(), this.zMax()); + break; + default: + return; + } + + this.percentValue.set(convertValueToPercentage(newValue, this.zMin(), this.zMax())); + this.onSlide.emit(newValue); + this.lastEmittedValue.set(newValue); + this.onChange(newValue); + this.cdr.markForCheck(); + event.preventDefault(); + } + + private updateSliderFromPercentage(percentage: number): void { + const clamped = clamp(percentage, [0, 1]); + const rawValue = this.zMin() + (this.zMax() - this.zMin()) * clamped; + const value = roundToStep(rawValue, this.zMin(), this.zStep()); + + if (value !== this.lastEmittedValue()) { + this.percentValue.set(convertValueToPercentage(value, this.zMin(), this.zMax())); + this.onSlide.emit(value); + this.lastEmittedValue.set(value); + this.onChange(value); + this.cdr.markForCheck(); + } + } + + private calculatePercentage(clientCoord: number): number { + const rect = this.elementRef.nativeElement.getBoundingClientRect(); + if (this.zOrientation() === 'vertical') { + const relativeY = (clientCoord - rect.top) / rect.height; + return clamp(1 - relativeY, [0, 1]); + } + const relativeX = (clientCoord - rect.left) / rect.width; + return clamp(relativeX, [0, 1]); + } + + private setInitialValue(): void { + const min = this.zMin(); + const max = this.zMax(); + const step = this.zStep(); + + const def = clamp(this.zDefault(), [min, max]); + const raw = this.zValue(); + const value = raw != null && raw >= min && raw <= max ? raw : def; + + const initial = roundToStep(value, min, step); + this.percentValue.set(convertValueToPercentage(initial, min, max)); + this.lastEmittedValue.set(initial); + this.thumbOffset.set(0); + } +} \ No newline at end of file diff --git a/src/app/shared/components/slider/slider.variants.ts b/src/app/shared/components/slider/slider.variants.ts new file mode 100644 index 0000000..3a8f294 --- /dev/null +++ b/src/app/shared/components/slider/slider.variants.ts @@ -0,0 +1,74 @@ +import { cva, type VariantProps } from 'class-variance-authority'; + +export const sliderVariants = cva( + 'relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col', + { + variants: { + orientation: { + horizontal: 'items-center', + vertical: 'flex-col h-full min-h-44 w-auto', + }, + disabled: { + true: 'opacity-50 pointer-events-none', + false: '', + }, + }, + defaultVariants: { + orientation: 'horizontal', + disabled: false, + }, + }, +); + +export type SliderVariants = VariantProps; + +export const sliderTrackVariants = cva( + 'flex bg-muted relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5', + { + variants: { + zOrientation: { + horizontal: 'h-1.5 w-full', + vertical: 'w-1.5 h-full min-h-44', + }, + }, + defaultVariants: { + zOrientation: 'horizontal', + }, + }, +); + +export type SliderTrackVariants = VariantProps; + +export const sliderRangeVariants = cva('bg-primary absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full', { + variants: { + zOrientation: { + horizontal: 'h-full', + vertical: 'w-full', + }, + }, + defaultVariants: { + zOrientation: 'horizontal', + }, +}); + +export type SliderRangeVariants = VariantProps; + +export const sliderThumbVariants = cva( + 'border-primary bg-background ring-ring/50 block size-4 shrink-0 rounded-full border shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50', +); + +export type SliderThumbVariants = VariantProps; + +export const sliderOrientationVariants = cva('absolute', { + variants: { + zOrientation: { + horizontal: 'translate-x-[-50%]', + vertical: 'translate-y-[50%]', + }, + }, + defaultVariants: { + zOrientation: 'horizontal', + }, +}); + +export type SliderOrientationVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/switch/switch.component.ts b/src/app/shared/components/switch/switch.component.ts new file mode 100644 index 0000000..d32a373 --- /dev/null +++ b/src/app/shared/components/switch/switch.component.ts @@ -0,0 +1,109 @@ +import { + ChangeDetectionStrategy, + Component, + computed, + forwardRef, + input, + output, + signal, + ViewEncapsulation, +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import type { ClassValue } from 'clsx'; + +import { switchVariants, ZardSwitchVariants } from './switch.variants'; +import { mergeClasses, generateId } from '@shared/utils/merge-classes'; + +type OnTouchedType = () => any; +type OnChangeType = (value: any) => void; + +@Component({ + selector: 'z-switch, [z-switch]', + standalone: true, + exportAs: 'zSwitch', + template: ` + + + + + + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardSwitchComponent), + multi: true, + }, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, +}) +export class ZardSwitchComponent implements ControlValueAccessor { + readonly checkChange = output(); + readonly class = input(''); + + readonly zType = input('default'); + readonly zSize = input('default'); + readonly zId = input(''); + + /* eslint-disable-next-line @typescript-eslint/no-empty-function */ + private onChange: OnChangeType = () => {}; + /* eslint-disable-next-line @typescript-eslint/no-empty-function */ + private onTouched: OnTouchedType = () => {}; + + protected readonly classes = computed(() => + mergeClasses(switchVariants({ zType: this.zType(), zSize: this.zSize() }), this.class()) + ); + + protected readonly uniqueId = signal(generateId('switch')); + protected checked = signal(true); + protected status = computed(() => (this.checked() ? 'checked' : 'unchecked')); + protected disabled = signal(false); + + writeValue(val: boolean): void { + this.checked.set(val); + } + + registerOnChange(fn: OnChangeType): void { + this.onChange = fn; + } + + registerOnTouched(fn: OnTouchedType): void { + this.onTouched = fn; + } + + onSwitchChange(): void { + if (this.disabled()) return; + + this.checked.update((checked) => !checked); + this.onTouched(); + this.onChange(this.checked()); + this.checkChange.emit(this.checked()); + } + + setDisabledState(isDisabled: boolean): void { + this.disabled.set(isDisabled); + } +} diff --git a/src/app/shared/components/switch/switch.variants.ts b/src/app/shared/components/switch/switch.variants.ts new file mode 100644 index 0000000..e6ec94b --- /dev/null +++ b/src/app/shared/components/switch/switch.variants.ts @@ -0,0 +1,27 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const switchVariants = cva( + 'peer inline-flex shrink-0 cursor-pointer items-center rounded-full border border-slate-300 transition-colors ' + + 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ' + + 'disabled:cursor-not-allowed disabled:opacity-50 ' + + 'data-[state=unchecked]:bg-pmu-jaune data-[state=checked]:bg-pmu-vert', + { + variants: { + zType: { + default: 'bg-pmu-green', + destructive: 'data-[state=checked]:bg-destructive', + }, + zSize: { + default: 'h-6 w-11', + sm: 'h-5 w-9', + lg: 'h-7 w-14', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + }, + } +); + +export type ZardSwitchVariants = VariantProps; diff --git a/src/app/shared/components/table/table.component.ts b/src/app/shared/components/table/table.component.ts new file mode 100644 index 0000000..4c22cd5 --- /dev/null +++ b/src/app/shared/components/table/table.component.ts @@ -0,0 +1,144 @@ +import type { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { + tableVariants, + tableHeaderVariants, + tableBodyVariants, + tableRowVariants, + tableHeadVariants, + tableCellVariants, + tableCaptionVariants, + ZardTableVariants, +} from './table.variants'; + +@Component({ + selector: 'table[z-table]', + exportAs: 'zTable', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableComponent { + readonly zType = input('default'); + readonly zSize = input('default'); + readonly class = input(''); + + protected readonly classes = computed(() => + mergeClasses( + tableVariants({ + zType: this.zType(), + zSize: this.zSize(), + }), + this.class(), + ), + ); +} + +@Component({ + selector: 'thead[z-table-header]', + exportAs: 'zTableHeader', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableHeaderComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(tableHeaderVariants(), this.class())); +} + +@Component({ + selector: 'tbody[z-table-body]', + exportAs: 'zTableBody', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableBodyComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(tableBodyVariants(), this.class())); +} + +@Component({ + selector: 'tr[z-table-row]', + exportAs: 'zTableRow', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableRowComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(tableRowVariants(), this.class())); +} + +@Component({ + selector: 'th[z-table-head]', + exportAs: 'zTableHead', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableHeadComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(tableHeadVariants(), this.class())); +} + +@Component({ + selector: 'td[z-table-cell]', + exportAs: 'zTableCell', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableCellComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(tableCellVariants(), this.class())); +} + +@Component({ + selector: 'caption[z-table-caption]', + exportAs: 'zTableCaption', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ``, + host: { + '[class]': 'classes()', + }, +}) +export class ZardTableCaptionComponent { + readonly class = input(''); + + protected readonly classes = computed(() => mergeClasses(tableCaptionVariants(), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/table/table.module.ts b/src/app/shared/components/table/table.module.ts new file mode 100644 index 0000000..100c246 --- /dev/null +++ b/src/app/shared/components/table/table.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; + +import { + ZardTableComponent, + ZardTableHeaderComponent, + ZardTableBodyComponent, + ZardTableRowComponent, + ZardTableHeadComponent, + ZardTableCellComponent, + ZardTableCaptionComponent, +} from './table.component'; + +const TABLE_COMPONENTS = [ + ZardTableComponent, + ZardTableHeaderComponent, + ZardTableBodyComponent, + ZardTableRowComponent, + ZardTableHeadComponent, + ZardTableCellComponent, + ZardTableCaptionComponent, +]; + +@NgModule({ + imports: [...TABLE_COMPONENTS], + exports: [...TABLE_COMPONENTS], +}) +export class ZardTableModule {} \ No newline at end of file diff --git a/src/app/shared/components/table/table.variants.ts b/src/app/shared/components/table/table.variants.ts new file mode 100644 index 0000000..02035ba --- /dev/null +++ b/src/app/shared/components/table/table.variants.ts @@ -0,0 +1,61 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const tableVariants = cva( + 'w-full caption-bottom text-sm [&_thead_tr]:border-b [&_tbody]:border-0 [&_tbody_tr:last-child]:border-0 [&_tbody_tr]:border-b [&_tbody_tr]:transition-colors [&_tbody_tr]:hover:bg-muted/50 [&_tbody_tr]:data-[state=selected]:bg-muted [&_th]:h-10 [&_th]:px-2 [&_th]:text-left [&_th]:align-middle [&_th]:font-medium [&_th]:text-muted-foreground [&_th:has([role=checkbox])]:pr-0 [&_th>[role=checkbox]]:translate-y-[2px] [&_td]:p-2 [&_td]:align-middle [&_td:has([role=checkbox])]:pr-0 [&_td>[role=checkbox]]:translate-y-[2px] [&_caption]:mt-4 [&_caption]:text-sm [&_caption]:text-muted-foreground', + { + variants: { + zType: { + default: '', + striped: '[&_tbody_tr:nth-child(odd)]:bg-muted/50', + bordered: 'border border-border', + }, + zSize: { + default: '', + compact: '[&_td]:py-2 [&_th]:py-2', + comfortable: '[&_td]:py-4 [&_th]:py-4', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'default', + }, + }, +); + +export const tableHeaderVariants = cva('[&_tr]:border-b', { + variants: {}, + defaultVariants: {}, +}); + +export const tableBodyVariants = cva('[&_tr:last-child]:border-0', { + variants: {}, + defaultVariants: {}, +}); + +export const tableRowVariants = cva('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', { + variants: {}, + defaultVariants: {}, +}); + +export const tableHeadVariants = cva('h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', { + variants: {}, + defaultVariants: {}, +}); + +export const tableCellVariants = cva('p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', { + variants: {}, + defaultVariants: {}, +}); + +export const tableCaptionVariants = cva('mt-4 text-sm text-muted-foreground', { + variants: {}, + defaultVariants: {}, +}); + +export type ZardTableVariants = VariantProps; +export type ZardTableHeaderVariants = VariantProps; +export type ZardTableBodyVariants = VariantProps; +export type ZardTableRowVariants = VariantProps; +export type ZardTableHeadVariants = VariantProps; +export type ZardTableCellVariants = VariantProps; +export type ZardTableCaptionVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/tabs/tabs.component.ts b/src/app/shared/components/tabs/tabs.component.ts new file mode 100644 index 0000000..d630c99 --- /dev/null +++ b/src/app/shared/components/tabs/tabs.component.ts @@ -0,0 +1,272 @@ +import { + afterNextRender, + ChangeDetectionStrategy, + Component, + computed, + contentChildren, + ElementRef, + input, + output, + signal, + TemplateRef, + viewChild, + ViewEncapsulation, +} from '@angular/core'; +import { combineLatest, interval, startWith } from 'rxjs'; +import { CommonModule } from '@angular/common'; + +import { tabButtonVariants, tabContainerVariants, tabNavVariants, ZardTabVariants } from './tabs.variants'; +import { ZardButtonComponent } from '../button/button.component'; + +export type zPosition = 'top' | 'bottom' | 'left' | 'right'; +export type zAlign = 'center' | 'start' | 'end'; + +@Component({ + selector: 'z-tab', + standalone: true, + imports: [], + template: ` + + + + `, + encapsulation: ViewEncapsulation.None, +}) +export class ZardTabComponent { + label = input.required(); + readonly contentTemplate = viewChild.required>('content'); +} + +@Component({ + selector: 'z-tab-group', + standalone: true, + imports: [CommonModule, ZardButtonComponent], + template: ` +
    + @if (navBeforeContent()) { +
    + @if (showArrow()) { + @if (zTabsPosition() === 'top' || zTabsPosition() === 'bottom') { + + } @else { + + } + } + + + + @if (showArrow()) { + @if (zTabsPosition() === 'top' || zTabsPosition() === 'bottom') { + + } @else { + + } + } +
    + } + +
    + @for (tab of tabs(); track $index; let index = $index) { + @if (activeTabIndex() === index) { + + } + } +
    + + @if (!navBeforeContent()) { +
    + @if (showArrow()) { + @if (zTabsPosition() === 'top' || zTabsPosition() === 'bottom') { + + } @else { + + } + } + + + + @if (showArrow()) { + @if (zTabsPosition() === 'top' || zTabsPosition() === 'bottom') { + + } @else { + + } + } +
    + } +
    + `, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + styles: [ + ` + .nav-tab-scroll { + -webkit-overflow-scrolling: touch; + scroll-behavior: smooth; + &::-webkit-scrollbar-thumb { + background-color: rgba(209, 209, 209, 0.2); + border-radius: 2px; + } + &::-webkit-scrollbar { + height: 4px; + width: 4px; + } + &::-webkit-scrollbar-button { + display: none; + } + } + `, + ], +}) +export class ZardTabGroupComponent { + private readonly tabComponents = contentChildren(ZardTabComponent, { descendants: true }); + private readonly tabsContainer = viewChild.required('tabNav'); + + protected readonly tabs = computed(() => this.tabComponents()); + protected readonly activeTabIndex = signal(0); + protected readonly hasScrollSignal = signal(false); + + protected readonly showArrow = computed(() => { + const _tabs = this.tabs(); + const _position = this.zTabsPosition(); + const scrollStatus = this.hasScrollSignal(); + return scrollStatus; + }); + + protected readonly zOnTabChange = output<{ + index: number; + label: string; + tab: ZardTabComponent; + }>(); + protected readonly zDeselect = output<{ + index: number; + label: string; + tab: ZardTabComponent; + }>(); + + public readonly zTabsPosition = input('top'); + public readonly zActivePosition = input('bottom'); + public readonly zShowArrow = input(true); + public readonly zScrollAmount = input(100); + public readonly zAlignTabs = input('start'); + + constructor() { + afterNextRender(() => { + if (this.tabs().length > 0) { + this.setActiveTab(0); + } + + combineLatest([interval(100).pipe(startWith(0))]).subscribe(() => { + this.hasScrollSignal.set(this.hasScroll()); + }); + }); + } + + private hasScroll(): boolean { + if (this.tabsContainer && this.tabsContainer().nativeElement && this.zShowArrow()) { + const navElement: HTMLElement = this.tabsContainer().nativeElement; + return navElement.scrollWidth > navElement.clientWidth || navElement.scrollHeight > navElement.clientHeight; + } + return false; + } + + protected setActiveTab(index: number) { + const currentTab = this.tabs()[this.activeTabIndex()]; + if (index !== this.activeTabIndex()) { + this.zDeselect.emit({ + index: this.activeTabIndex(), + label: currentTab.label(), + tab: currentTab, + }); + } + + this.activeTabIndex.set(index); + const activeTabComponent = this.tabs()[index]; + if (activeTabComponent) { + this.zOnTabChange.emit({ + index, + label: activeTabComponent.label(), + tab: activeTabComponent, + }); + } + } + + protected readonly navBeforeContent = computed(() => { + const position = this.zTabsPosition(); + return position === 'top' || position === 'left'; + }); + + protected readonly isHorizontal = computed(() => { + const position = this.zTabsPosition(); + return position === 'top' || position === 'bottom'; + }); + + protected readonly navGridClasses = computed(() => { + const position = this.zTabsPosition(); + const hasArrows = this.showArrow(); + return position === 'left' || position === 'right' ? `grid${hasArrows ? ' grid-rows-[25px_1fr_25px]' : ''}` : `grid${hasArrows ? ' grid-cols-[25px_1fr_25px]' : ''}`; + }); + + protected readonly containerClasses = computed(() => tabContainerVariants({ zPosition: this.zTabsPosition() })); + + protected readonly navClasses = computed(() => tabNavVariants({ zPosition: this.zTabsPosition(), zAlignTabs: this.showArrow() ? 'start' : this.zAlignTabs() })); + + protected readonly buttonClassesSignal = computed(() => { + const activeIndex = this.activeTabIndex(); + const position = this.zActivePosition(); + return this.tabs().map((_, index) => { + const isActive = activeIndex === index; + return tabButtonVariants({ zActivePosition: position, isActive }); + }); + }); + + protected scrollNav(direction: 'left' | 'right' | 'up' | 'down') { + const container = this.tabsContainer().nativeElement; + const scrollAmount = this.zScrollAmount(); + if (direction === 'left') { + container.scrollLeft -= scrollAmount; + } else if (direction === 'right') { + container.scrollLeft += scrollAmount; + } else if (direction === 'up') { + container.scrollTop -= scrollAmount; + } else if (direction === 'down') { + container.scrollTop += scrollAmount; + } + } + + public selectTabByIndex(index: number): void { + if (index >= 0 && index < this.tabs().length) { + this.setActiveTab(index); + } else { + console.warn(`Index ${index} outside the range of available tabs.`); + } + } +} diff --git a/src/app/shared/components/tabs/tabs.variants.ts b/src/app/shared/components/tabs/tabs.variants.ts new file mode 100644 index 0000000..eac86d6 --- /dev/null +++ b/src/app/shared/components/tabs/tabs.variants.ts @@ -0,0 +1,80 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +import { zAlign } from './tabs.component'; + +export const tabContainerVariants = cva('flex w-full h-full', { + variants: { + zPosition: { + top: 'flex-col', + bottom: 'flex-col', + left: 'flex-row', + right: 'flex-row', + }, + }, + defaultVariants: { + zPosition: 'top', + }, +}); + +export const tabNavVariants = cva('flex gap-4 overflow-auto scroll nav-tab-scroll', { + variants: { + zPosition: { + top: 'flex-row border-b mb-4 w-full', + bottom: 'flex-row border-t mt-4 w-full', + left: 'flex-col border-r mr-4 h-full min-h-0', + right: 'flex-col border-l ml-4 h-full min-h-0', + }, + zAlignTabs: { + start: 'justify-start', + center: 'justify-center', + end: 'justify-end', + }, + }, + defaultVariants: { + zPosition: 'top', + zAlignTabs: 'start', + }, +}); + +export const tabButtonVariants = cva('hover:bg-transparent rounded-none flex-shrink-0', { + variants: { + zActivePosition: { + top: '', + bottom: '', + left: '', + right: '', + }, + isActive: { + true: '', + false: '', + }, + }, + compoundVariants: [ + { + zActivePosition: 'top', + isActive: true, + class: 'border-t-2 border-t-primary', + }, + { + zActivePosition: 'bottom', + isActive: true, + class: 'border-b-2 border-b-primary', + }, + { + zActivePosition: 'left', + isActive: true, + class: 'border-l-2 border-l-primary', + }, + { + zActivePosition: 'right', + isActive: true, + class: 'border-r-2 border-r-primary', + }, + ], + defaultVariants: { + zActivePosition: 'bottom', + isActive: false, + }, +}); + +export type ZardTabVariants = VariantProps & VariantProps & VariantProps & { zAlignTabs: zAlign }; diff --git a/src/app/shared/components/toast/toast.component.ts b/src/app/shared/components/toast/toast.component.ts new file mode 100644 index 0000000..28a3182 --- /dev/null +++ b/src/app/shared/components/toast/toast.component.ts @@ -0,0 +1,44 @@ +import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core'; +import { NgxSonnerToaster } from 'ngx-sonner'; +import type { ClassValue } from 'clsx'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { toastVariants, ZardToastVariants } from './toast.variants'; + +@Component({ + selector: 'z-toast, z-toaster', + standalone: true, + exportAs: 'zToast', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + imports: [NgxSonnerToaster], + template: ` + + `, +}) +export class ZardToastComponent { + readonly class = input(''); + readonly variant = input('default'); + readonly theme = input<'light' | 'dark' | 'system'>('system'); + readonly position = input<'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'>('bottom-right'); + readonly richColors = input(false); + readonly expand = input(false); + readonly duration = input(4000); + readonly visibleToasts = input(3); + readonly closeButton = input(false); + readonly toastOptions = input>({}); + readonly dir = input<'ltr' | 'rtl' | 'auto'>('auto'); + + protected readonly classes = computed(() => mergeClasses('toaster group', toastVariants({ variant: this.variant() }), this.class())); +} \ No newline at end of file diff --git a/src/app/shared/components/toast/toast.variants.ts b/src/app/shared/components/toast/toast.variants.ts new file mode 100644 index 0000000..d7bed9c --- /dev/null +++ b/src/app/shared/components/toast/toast.variants.ts @@ -0,0 +1,15 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const toastVariants = cva('group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg', { + variants: { + variant: { + default: 'group-[.toaster]:bg-background group-[.toaster]:text-foreground', + destructive: 'group-[.toaster]:bg-destructive group-[.toaster]:text-destructive-foreground destructive group-[.toaster]:border-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, +}); + +export type ZardToastVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/toggle-group/toggle-group.component.ts b/src/app/shared/components/toggle-group/toggle-group.component.ts new file mode 100644 index 0000000..c3a5c80 --- /dev/null +++ b/src/app/shared/components/toggle-group/toggle-group.component.ts @@ -0,0 +1,181 @@ +import { ClassValue } from 'clsx'; + +import { ChangeDetectionStrategy, Component, computed, forwardRef, input, output, signal, ViewEncapsulation } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { toggleGroupVariants, toggleGroupItemVariants } from './toggle-group.variants'; + +export interface ZardToggleGroupItem { + value: string; + label?: string; + icon?: string; + disabled?: boolean; + ariaLabel?: string; +} + +type OnTouchedType = () => void; +type OnChangeType = (value: string | string[]) => void; + +@Component({ + selector: 'z-toggle-group', + exportAs: 'zToggleGroup', + standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + @for (item of items(); track item.value; let i = $index) { + + } +
    + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardToggleGroupComponent), + multi: true, + }, + ], +}) +export class ZardToggleGroupComponent implements ControlValueAccessor { + readonly zMode = input<'single' | 'multiple'>('multiple'); + readonly zType = input<'default' | 'outline'>('default'); + readonly zSize = input<'sm' | 'md' | 'lg'>('md'); + readonly value = input(); + readonly defaultValue = input(); + readonly disabled = input(false); + readonly class = input(''); + readonly items = input([]); + + readonly valueChange = output(); + + private internalValue = signal(undefined); + + protected readonly classes = computed(() => + mergeClasses( + toggleGroupVariants({ + zType: this.zType(), + zSize: this.zSize(), + }), + this.class(), + ), + ); + + protected readonly currentValue = computed(() => { + const internal = this.internalValue(); + const input = this.value(); + const defaultVal = this.defaultValue(); + + if (internal !== undefined) return internal; + if (input !== undefined) return input; + if (defaultVal !== undefined) return defaultVal; + + return this.zMode() === 'single' ? '' : []; + }); + + protected getItemClasses(index: number, total: number): string { + const baseClasses = toggleGroupItemVariants({ + zType: this.zType(), + zSize: this.zSize(), + }); + + const positionClasses = []; + + // Add rounded corners for first and last items + if (index === 0) { + positionClasses.push('first:rounded-l-md'); + } + if (index === total - 1) { + positionClasses.push('last:rounded-r-md'); + } + + // Handle borders for outline variant + if (this.zType() === 'outline') { + if (index === 0) { + // First item gets full border + positionClasses.push('border-l'); + } else { + // Other items don't get left border (connects to previous) + positionClasses.push('border-l-0'); + } + } + + // Focus z-index + positionClasses.push('focus:z-10', 'focus-visible:z-10'); + + return mergeClasses(baseClasses, ...positionClasses); + } + + protected isItemPressed(itemValue: string): boolean { + const current = this.currentValue(); + if (this.zMode() === 'single') { + return current === itemValue; + } + return Array.isArray(current) && current.includes(itemValue); + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onTouched: OnTouchedType = () => {}; + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onChangeFn: OnChangeType = () => {}; + + toggleItem(item: ZardToggleGroupItem) { + if (this.disabled() || item.disabled) return; + + const currentValue = this.currentValue(); + let newValue: string | string[]; + + if (this.zMode() === 'single') { + newValue = currentValue === item.value ? '' : item.value; + } else { + const currentArray = Array.isArray(currentValue) ? currentValue : []; + if (currentArray.includes(item.value)) { + newValue = currentArray.filter(v => v !== item.value); + } else { + newValue = [...currentArray, item.value]; + } + } + + this.internalValue.set(newValue); + this.valueChange.emit(newValue); + this.onChangeFn(newValue); + this.onTouched(); + } + + writeValue(value: string | string[]): void { + if (value !== undefined) { + this.internalValue.set(value); + } + } + + registerOnChange(fn: OnChangeType): void { + this.onChangeFn = fn; + } + + registerOnTouched(fn: OnTouchedType): void { + this.onTouched = fn; + } + + setDisabledState(_isDisabled: boolean): void { + // Note: disabled state is handled through the disabled input + // This method is required by ControlValueAccessor interface + } +} \ No newline at end of file diff --git a/src/app/shared/components/toggle-group/toggle-group.variants.ts b/src/app/shared/components/toggle-group/toggle-group.variants.ts new file mode 100644 index 0000000..6226201 --- /dev/null +++ b/src/app/shared/components/toggle-group/toggle-group.variants.ts @@ -0,0 +1,43 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const toggleGroupVariants = cva('flex w-fit items-center rounded-md', { + variants: { + zType: { + default: '', + outline: 'shadow-sm', + }, + zSize: { + sm: '', + md: '', + lg: '', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'md', + }, +}); + +export const toggleGroupItemVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-none gap-2 text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground', + { + variants: { + zType: { + default: 'bg-transparent', + outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground', + }, + zSize: { + sm: 'h-8 px-2.5 text-xs', + md: 'h-9 px-3 text-sm', + lg: 'h-10 px-4 text-sm', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'md', + }, + }, +); + +export type ZardToggleGroupVariants = VariantProps; +export type ZardToggleGroupItemVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/toggle/toggle.component.ts b/src/app/shared/components/toggle/toggle.component.ts new file mode 100644 index 0000000..3968d95 --- /dev/null +++ b/src/app/shared/components/toggle/toggle.component.ts @@ -0,0 +1,99 @@ +import { ChangeDetectionStrategy, Component, forwardRef, HostListener, ViewEncapsulation, signal, computed, input, output, linkedSignal } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ClassValue } from 'clsx'; + +import { toggleVariants, ZardToggleVariants } from './toggle.variants'; +import { mergeClasses, transform } from '@shared/utils/merge-classes'; + +type OnTouchedType = () => void; +type OnChangeType = (value: boolean) => void; + +@Component({ + selector: 'z-toggle', + exportAs: 'zToggle', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ZardToggleComponent), + multi: true, + }, + ], +}) +export class ZardToggleComponent implements ControlValueAccessor { + readonly zValue = input(); + readonly zDefault = input(false); + readonly zDisabled = input(false, { alias: 'disabled', transform }); + readonly zType = input('default'); + readonly zSize = input('md'); + readonly zAriaLabel = input('', { alias: 'aria-label' }); + readonly class = input(''); + + readonly onClick = output(); + readonly onHover = output(); + readonly onChange = output(); + + private isUsingNgModel = signal(false); + + protected readonly value = linkedSignal(() => this.zValue() || this.zDefault()); + + protected readonly disabled = linkedSignal(() => this.zDisabled()); + + protected readonly classes = computed(() => mergeClasses(toggleVariants({ zSize: this.zSize(), zType: this.zType() }), this.class())); + + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onTouched: OnTouchedType = () => {}; + // eslint-disable-next-line @typescript-eslint/no-empty-function + private onChangeFn: OnChangeType = () => {}; + + @HostListener('mouseenter') + handleHover() { + this.onHover.emit(); + } + + toggle() { + if (this.disabled()) return; + + const next = !this.value(); + + if (this.zValue() === undefined) { + this.value.set(next); + } + + this.onClick.emit(); + this.onChange.emit(next); + this.onChangeFn(next); + this.onTouched(); + } + + writeValue(val: boolean): void { + this.value.set(val ?? this.zDefault()); + } + + registerOnChange(fn: any): void { + this.onChangeFn = fn; + this.isUsingNgModel.set(true); + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled.set(isDisabled); + } +} diff --git a/src/app/shared/components/toggle/toggle.variants.ts b/src/app/shared/components/toggle/toggle.variants.ts new file mode 100644 index 0000000..c5f584c --- /dev/null +++ b/src/app/shared/components/toggle/toggle.variants.ts @@ -0,0 +1,23 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const toggleVariants = cva( + 'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground', + { + variants: { + zType: { + default: 'bg-transparent', + outline: 'border border-input bg-transparent hover:bg-accent hover:text-accent-foreground', + }, + zSize: { + sm: 'h-8 px-2', + md: 'h-9 px-3', + lg: 'h-10 px-3', + }, + }, + defaultVariants: { + zType: 'default', + zSize: 'md', + }, + }, +); +export type ZardToggleVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/components/tooltip/tooltip-positions.ts b/src/app/shared/components/tooltip/tooltip-positions.ts new file mode 100644 index 0000000..9560cab --- /dev/null +++ b/src/app/shared/components/tooltip/tooltip-positions.ts @@ -0,0 +1,34 @@ +import { ConnectedPosition } from '@angular/cdk/overlay'; + +export const TOOLTIP_POSITIONS_MAP: { [key: string]: ConnectedPosition } = { + top: { + originX: 'center', + originY: 'top', + overlayX: 'center', + overlayY: 'bottom', + offsetY: -8, + }, + bottom: { + originX: 'center', + originY: 'bottom', + overlayX: 'center', + overlayY: 'top', + offsetY: 8, + }, + left: { + originX: 'start', + originY: 'center', + overlayX: 'end', + overlayY: 'center', + offsetX: -8, + }, + right: { + originX: 'end', + originY: 'center', + overlayX: 'start', + overlayY: 'center', + offsetX: 8, + }, +}; + +export type ZardTooltipPositions = 'top' | 'bottom' | 'left' | 'right'; \ No newline at end of file diff --git a/src/app/shared/components/tooltip/tooltip.ts b/src/app/shared/components/tooltip/tooltip.ts new file mode 100644 index 0000000..93d9b34 --- /dev/null +++ b/src/app/shared/components/tooltip/tooltip.ts @@ -0,0 +1,194 @@ +import { merge, Subject, take, takeUntil } from 'rxjs'; + +import { Overlay, OverlayModule, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { isPlatformBrowser } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + ComponentRef, + computed, + Directive, + ElementRef, + inject, + input, + NgModule, + OnDestroy, + OnInit, + output, + PLATFORM_ID, + Renderer2, + signal, +} from '@angular/core'; + +import { mergeClasses } from '@shared/utils/merge-classes'; +import { TOOLTIP_POSITIONS_MAP, ZardTooltipPositions } from './tooltip-positions'; +import { tooltipVariants } from './tooltip.variants'; + +export type ZardTooltipTriggers = 'click' | 'hover'; + +@Directive({ + selector: '[zTooltip]', + exportAs: 'zTooltip', + host: { + style: 'cursor: pointer', + }, +}) +export class ZardTooltipDirective implements OnInit, OnDestroy { + private readonly destroy$ = new Subject(); + private overlayPositionBuilder = inject(OverlayPositionBuilder); + private elementRef = inject(ElementRef); + private overlay = inject(Overlay); + private renderer = inject(Renderer2); + private platformId = inject(PLATFORM_ID); + + private overlayRef?: OverlayRef; + private componentRef?: ComponentRef; + private scrollListenerRef?: () => void; + + readonly zTooltip = input(null); + readonly zPosition = input('top'); + readonly zTrigger = input('hover'); + + readonly zOnShow = output(); + readonly zOnHide = output(); + + get nativeElement() { + return this.elementRef.nativeElement; + } + + get overlayElement() { + return this.componentRef?.instance.elementRef.nativeElement; + } + + ngOnInit() { + this.setTriggers(); + + if (isPlatformBrowser(this.platformId)) { + const positionStrategy = this.overlayPositionBuilder.flexibleConnectedTo(this.elementRef).withPositions([TOOLTIP_POSITIONS_MAP[this.zPosition()]]); + this.overlayRef = this.overlay.create({ positionStrategy }); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + show() { + if (this.componentRef) return; + + const tooltipText = this.zTooltip(); + if (!tooltipText || tooltipText.trim() === '') return; + + const tooltipPortal = new ComponentPortal(ZardTooltipComponent); + this.componentRef = this.overlayRef?.attach(tooltipPortal); + if (!this.componentRef) return; + + this.componentRef.instance.setProps(tooltipText, this.zPosition(), this.zTrigger()); + this.componentRef.instance.state.set('opened'); + + this.componentRef.instance.onLoad$.pipe(take(1)).subscribe(() => { + this.zOnShow.emit(); + + switch (this.zTrigger()) { + case 'click': + if (!this.overlayRef) return; + + this.overlayRef + .outsidePointerEvents() + .pipe(takeUntil(merge(this.destroy$, this.overlayRef.detachments()))) + .subscribe(() => this.hide()); + break; + case 'hover': + this.renderer.listen( + this.elementRef.nativeElement, + 'mouseleave', + (event: Event) => { + event.preventDefault(); + this.hide(); + }, + { once: true }, + ); + break; + } + }); + + this.scrollListenerRef = this.renderer.listen(window, 'scroll', () => { + this.hide(0); + }); + } + + hide(animationDuration = 150) { + if (!this.componentRef) return; + + this.componentRef.instance.state.set('closed'); + + setTimeout(() => { + this.zOnHide.emit(); + + this.overlayRef?.detach(); + this.componentRef?.destroy(); + this.componentRef = undefined; + + if (this.scrollListenerRef) this.scrollListenerRef(); + }, animationDuration); + } + + private setTriggers() { + const showTrigger = this.zTrigger() === 'click' ? 'click' : 'mouseenter'; + + this.renderer.listen(this.elementRef.nativeElement, showTrigger, (event: Event) => { + event.preventDefault(); + this.show(); + }); + } +} + +@Component({ + selector: 'z-tooltip', + template: `{{ text() }}`, + host: { + '[class]': 'classes()', + '[attr.data-side]': 'position()', + '[attr.data-state]': 'state()', + }, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ZardTooltipComponent implements OnInit, OnDestroy { + private readonly destroy$ = new Subject(); + readonly elementRef = inject(ElementRef); + + protected position = signal('top'); + private trigger = signal('hover'); + protected text = signal(''); + + state = signal<'closed' | 'opened'>('closed'); + + private onLoadSubject$ = new Subject(); + onLoad$ = this.onLoadSubject$.asObservable(); + + protected readonly classes = computed(() => mergeClasses(tooltipVariants())); + + ngOnInit(): void { + this.onLoadSubject$.next(); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + this.onLoadSubject$.complete(); + } + + setProps(text: string | null, position: ZardTooltipPositions, trigger: ZardTooltipTriggers) { + if (text) this.text.set(text); + this.position.set(position); + this.trigger.set(trigger); + } +} + +@NgModule({ + imports: [OverlayModule, ZardTooltipComponent, ZardTooltipDirective], + exports: [ZardTooltipComponent, ZardTooltipDirective], +}) +export class ZardTooltipModule {} \ No newline at end of file diff --git a/src/app/shared/components/tooltip/tooltip.variants.ts b/src/app/shared/components/tooltip/tooltip.variants.ts new file mode 100644 index 0000000..7837cc4 --- /dev/null +++ b/src/app/shared/components/tooltip/tooltip.variants.ts @@ -0,0 +1,6 @@ +import { cva, VariantProps } from 'class-variance-authority'; + +export const tooltipVariants = cva( + 'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]', +); +export type ZardTooltipVariants = VariantProps; \ No newline at end of file diff --git a/src/app/shared/directives/tooltip.spec.ts b/src/app/shared/directives/tooltip.spec.ts new file mode 100644 index 0000000..93fa15a --- /dev/null +++ b/src/app/shared/directives/tooltip.spec.ts @@ -0,0 +1,8 @@ +import { Tooltip } from './tooltip'; + +describe('Tooltip', () => { + it('should create an instance', () => { + const directive = new Tooltip(); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/shared/directives/tooltip.ts b/src/app/shared/directives/tooltip.ts new file mode 100644 index 0000000..1819595 --- /dev/null +++ b/src/app/shared/directives/tooltip.ts @@ -0,0 +1,132 @@ +import { Directive, ElementRef, HostListener, Input, Renderer2 } from '@angular/core'; + +@Directive({ + selector: '[appTooltip]', +}) +export class Tooltip { + @Input('appTooltip') text: string | null = null; + @Input() tooltipDisabled = false; + @Input() tooltipPlacement: 'right' | 'left' | 'top' | 'bottom' = 'right'; + @Input() tooltipOffset = 10; + + private tooltipEl?: HTMLElement; + private removeFns: Array<() => void> = []; + + constructor(private el: ElementRef, private r: Renderer2) {} + + @HostListener('mouseenter') + onEnter() { + if (!this.text || this.tooltipDisabled) return; + this.create(); + this.position(); + } + + @HostListener('mouseleave') + onLeave() { + this.destroy(); + } + + @HostListener('focus') + onFocus() { + if (!this.text || this.tooltipDisabled) return; + this.create(); + this.position(); + } + + @HostListener('blur') + onBlur() { + this.destroy(); + } + + @HostListener('window:scroll') + onScroll() { + if (this.tooltipEl) this.position(); + } + + @HostListener('window:resize') + onResize() { + if (this.tooltipEl) this.position(); + } + + private create() { + if (this.tooltipEl) return; + + const el = this.r.createElement('div'); + this.r.setStyle(el, 'position', 'fixed'); + this.r.setStyle(el, 'zIndex', '999999'); + this.r.setStyle(el, 'pointerEvents', 'none'); + this.r.setStyle(el, 'padding', '6px 10px'); + this.r.setStyle(el, 'fontSize', '12px'); + this.r.setStyle(el, 'lineHeight', '1'); + this.r.setStyle(el, 'borderRadius', '8px'); + this.r.setStyle(el, 'background', 'rgba(0,0,0,0.92)'); + this.r.setStyle(el, 'color', '#fff'); + this.r.setStyle(el, 'boxShadow', '0 8px 24px rgba(0,0,0,.28)'); + this.r.setStyle(el, 'transform', 'translate3d(0,0,0)'); + this.r.setStyle(el, 'transition', 'opacity .15s ease'); + this.r.setStyle(el, 'opacity', '0'); + this.r.setProperty(el, 'innerText', this.text ?? ''); + + document.body.appendChild(el); + // fade in next tick + requestAnimationFrame(() => this.r.setStyle(el, 'opacity', '1')); + this.tooltipEl = el; + + // close on escape + const keyFn = this.r.listen('window', 'keydown', (e: KeyboardEvent) => { + if (e.key === 'Escape') this.destroy(); + }); + this.removeFns.push(keyFn); + } + + private position() { + if (!this.tooltipEl) return; + const host = this.el.nativeElement.getBoundingClientRect(); + const tip = this.tooltipEl.getBoundingClientRect(); + const offset = this.tooltipOffset; + + let top = host.top + host.height / 2 - tip.height / 2; + let left = host.right + offset; + + switch (this.tooltipPlacement) { + case 'left': + left = host.left - tip.width - offset; + break; + case 'top': + left = host.left + host.width / 2 - tip.width / 2; + top = host.top - tip.height - offset; + break; + case 'bottom': + left = host.left + host.width / 2 - tip.width / 2; + top = host.bottom + offset; + break; + default: // right + left = host.right + offset; + break; + } + + // viewport guard + const vw = window.innerWidth; + const vh = window.innerHeight; + if (left + tip.width > vw - 8) left = vw - tip.width - 8; + if (left < 8) left = 8; + if (top + tip.height > vh - 8) top = vh - tip.height - 8; + if (top < 8) top = 8; + + this.r.setStyle(this.tooltipEl, 'top', `${top}px`); + this.r.setStyle(this.tooltipEl, 'left', `${left}px`); + } + + private destroy() { + if (this.tooltipEl?.parentNode) { + this.tooltipEl.parentNode.removeChild(this.tooltipEl); + } + this.tooltipEl = undefined; + this.removeFns.forEach((fn) => fn()); + this.removeFns = []; + } + + ngOnDestroy() { + this.destroy(); + } +} diff --git a/src/app/shared/forms/agent-form/agent-form.html b/src/app/shared/forms/agent-form/agent-form.html new file mode 100644 index 0000000..90176a8 --- /dev/null +++ b/src/app/shared/forms/agent-form/agent-form.html @@ -0,0 +1,91 @@ +
    +
    + + +
    +
    + + +
    +
    + + + +
    + + Actif + Inactif + Suspendu + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + + +
    +
    + + +
    +
    + + +
    +
    + + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + + +
    + + @for (l of limits; track l.id) { + {{ l.nom }} + } + +
    +
    +
    + +
    + Annuler + Enregistrer +
    +
    + + diff --git a/src/app/shared/forms/agent-form/agent-form.ts b/src/app/shared/forms/agent-form/agent-form.ts new file mode 100644 index 0000000..4a8cdb1 --- /dev/null +++ b/src/app/shared/forms/agent-form/agent-form.ts @@ -0,0 +1,87 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { Agent, AgentStatus } from 'src/app/core/interfaces/agent'; +import { AgentLimit } from 'src/app/core/interfaces/agent-limit'; +import { AgentLimitService } from 'src/app/core/services/agent-limit'; + +@Component({ + selector: 'app-agent-form', + standalone: true, + templateUrl: './agent-form.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, ReactiveFormsModule, ZardFormModule, ZardInputDirective, ZardSelectComponent, ZardSelectItemComponent, ZardButtonComponent], +}) +export class AgentForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + limits: AgentLimit[] = []; + + private _value?: Agent; + @Input() set value(v: Agent | undefined) { this._value = v; this.hydrateFromValue(v); } + get value(): Agent | undefined { return this._value; } + + form: FormGroup; + submitted = false; + + constructor(private fb: FormBuilder, private limitService: AgentLimitService) { + this.form = this.fb.group({ + code: ['', Validators.required], + profile: ['', Validators.required], + statut: ['ACTIF' as AgentStatus, Validators.required], + zone: [''], + kiosk: [''], + fonction: [''], + dateEmbauche: [''], + + nom: ['', Validators.required], + prenom: ['', Validators.required], + phone: ['', [Validators.required, Validators.minLength(6)]], + + limiteInferieure: [0, [Validators.min(0)]], + limiteSuperieure: [0, [Validators.min(0)]], + limiteParTransaction: [0, [Validators.min(0)]], + limiteMinAirtime: [0, [Validators.min(0)]], + limiteMaxAirtime: [0, [Validators.min(0)]], + maxPeripheriques: [0, [Validators.min(0)]], + + limitId: ['', Validators.required], + }); + + this.limitService + .list({ page: 1, perPage: 100, search: '', sortKey: 'nom', sortDir: 'asc' } as any) + .subscribe((res) => (this.limits = res.data)); + } + + error(control: string): string { + const e = this.form.get(control)?.errors; if (!e) return ''; if (e['required']) return 'Requis'; return ''; + } + + private hydrateFromValue(v?: Agent) { + if (!v) { + this.form.reset({ + code: '', profile: '', statut: 'ACTIF', zone: '', kiosk: '', fonction: '', dateEmbauche: '', nom: '', prenom: '', phone: '', limiteInferieure: 0, limiteSuperieure: 0, limiteParTransaction: 0, limiteMinAirtime: 0, limiteMaxAirtime: 0, maxPeripheriques: 0, limitId: '', + }); + return; + } + this.form.reset({ + code: v.code, profile: v.profile, statut: v.statut, zone: v.zone ?? '', kiosk: v.kiosk ?? '', fonction: v.fonction ?? '', dateEmbauche: v.dateEmbauche ?? '', nom: v.nom, prenom: v.prenom, phone: v.phone, limiteInferieure: v.limiteInferieure ?? 0, limiteSuperieure: v.limiteSuperieure ?? 0, limiteParTransaction: v.limiteParTransaction ?? 0, limiteMinAirtime: v.limiteMinAirtime ?? 0, limiteMaxAirtime: v.limiteMaxAirtime ?? 0, maxPeripheriques: v.maxPeripheriques ?? 0, limitId: v.limitId ?? '', + }); + } + + onSubmit() { + this.submitted = true; + if (this.form.invalid) { this.form.markAllAsTouched(); return; } + const raw = this.form.getRawValue() as any; + const payload: Agent = { id: this.value?.id ?? '', code: raw.code, profile: raw.profile, statut: raw.statut, zone: raw.zone, kiosk: raw.kiosk, fonction: raw.fonction, dateEmbauche: raw.dateEmbauche, nom: raw.nom, prenom: raw.prenom, phone: raw.phone, limiteInferieure: +raw.limiteInferieure, limiteSuperieure: +raw.limiteSuperieure, limiteParTransaction: +raw.limiteParTransaction, limiteMinAirtime: +raw.limiteMinAirtime, limiteMaxAirtime: +raw.limiteMaxAirtime, maxPeripheriques: +raw.maxPeripheriques, limitId: raw.limitId }; + this.save.emit(payload); + } +} + + diff --git a/src/app/shared/forms/agent-full-form/agent-full-form.html b/src/app/shared/forms/agent-full-form/agent-full-form.html new file mode 100644 index 0000000..f414cc6 --- /dev/null +++ b/src/app/shared/forms/agent-full-form/agent-full-form.html @@ -0,0 +1,368 @@ +
    +
    + +
    Informations Emploi
    +
    + +
    +
    + +
    +
    + +
    + +
    + +
    + ActifInactifSuspendu +
    + +
    + +
    + +
    + +
    +
    +
    + + +
    Informations Personnelles
    +
    + +
    +
    + +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +
    +
    +
    + + +
    Paramètres de connexion
    +
    + +
    +
    + +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    +
    + +
    + + @for (l of limits; track l.id) { + + {{ l.nom }} + @if (l.isDefault) { + (Par défaut) + } @if (l.actif) { + • Actif + } + + } + +
    + @if (selectedLimit) { +
    +
    {{ selectedLimit.nom }}
    +
    + @if (selectedLimit.isDefault) { + Limite par défaut + } @if (selectedLimit.actif) { + Contrôle actif + } @else { + Contrôle inactif + } +
    +
    +
    Min Bet: {{ (selectedLimit.betMin ?? 0).toLocaleString('fr-FR') }}
    +
    Max Bet: {{ (selectedLimit.betMax ?? 0).toLocaleString('fr-FR') }}
    +
    Max Bet (tx): {{ (selectedLimit.maxBet ?? 0).toLocaleString('fr-FR') }}
    +
    +
    + } +
    +
    +
    +
    + +
    + +
    Informations Légales
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +
    +
    + + +
    +
    Membres de famille
    + + Ajouter un membre + +
    +
    + @for (fm of famille.controls; track $index; let i = $index) { +
    +
    +
    +
    + +
    +
    +
    Membre #{{ i + 1 }}
    +
    Informations du membre
    +
    +
    + + + +
    +
    + + +
    + +
    +
    + + +
    + + Conjoint(e) + Enfant + Parent + Autre + +
    +
    + + +
    + + Masculin + Féminin + +
    +
    + + +
    + +
    +
    +
    +
    + } @empty { +
    +
    +
    + +
    +
    +
    Aucun membre de famille
    +
    + Ajoutez les membres de la famille de l'agent +
    + + Ajouter le premier membre + +
    +
    +
    + } +
    +
    +
    + + +
    +
    Assignation TPE (actifs)
    + Gérer +
    +
    + @for (id of tpeArray.value; track id) { + + {{ tpeLabel(id) }} + + + } @empty { + Aucun TPE assigné + } +
    +
    + + + + + + @if (!isSelectedTpe(row.id)) { + + } @else { + + } + + + +
    + Terminer +
    +
    +
    diff --git a/src/app/shared/forms/agent-full-form/agent-full-form.ts b/src/app/shared/forms/agent-full-form/agent-full-form.ts new file mode 100644 index 0000000..c705921 --- /dev/null +++ b/src/app/shared/forms/agent-full-form/agent-full-form.ts @@ -0,0 +1,417 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardCardComponent } from '@shared/components/card/card.component'; +import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table'; +import { Paginator } from '@shared/components/paginator/paginator'; +import { SearchBar } from '@shared/components/search-bar/search-bar'; +import { Modal } from '@shared/components/modal/modal'; +import { Agent, AgentFamilyMember } from 'src/app/core/interfaces/agent'; +import { AgentLimit } from 'src/app/core/interfaces/agent-limit'; +import { AgentLimitService } from 'src/app/core/services/agent-limit'; +import { AgentFamilyMemberService } from 'src/app/core/services/agent-family-member'; +import { TpeDevice } from 'src/app/core/interfaces/tpe'; +import { TpeService } from 'src/app/core/services/tpe'; + +@Component({ + selector: 'app-agent-full-form', + standalone: true, + templateUrl: './agent-full-form.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardSelectComponent, + ZardSelectItemComponent, + ZardButtonComponent, + ZardCardComponent, + DataTable, + Paginator, + SearchBar, + Modal, + ], +}) +export class AgentFullForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + limits: AgentLimit[] = []; + selectedLimit: AgentLimit | null = null; + tpes: TpeDevice[] = []; + // TPE picker state + assignModalOpen = false; + tpeRows: TpeDevice[] = []; + tpeTotal = 0; + tpePage = 1; + tpePerPage = 10; + tpeSearch = ''; + tpeCols: TableColumn[] = [ + { key: 'imei', label: 'IMEI', sortable: true }, + { key: 'serial', label: 'N° Série', sortable: true }, + { key: 'marque', label: 'Marque', sortable: true }, + { key: 'modele', label: 'Modèle', sortable: true }, + { key: 'type', label: 'Type', sortable: true }, + { key: 'statut', label: 'Statut', sortable: true }, + ]; + + private _value?: Agent; + @Input() set value(v: Agent | undefined) { this._value = v; this.hydrate(v); } + get value(): Agent | undefined { return this._value; } + + form: FormGroup; + submitted = false; + + constructor( + private fb: FormBuilder, + private limitService: AgentLimitService, + private tpeService: TpeService, + private familyMemberService: AgentFamilyMemberService + ) { + this.form = this.fb.group({ + // Emploi + code: ['', Validators.required], profile: ['', Validators.required], principalCode: [''], caisseProfile: [''], statut: ['ACTIF', Validators.required], zone: [''], kiosk: [''], fonction: [''], dateEmbauche: [''], + // Perso + nom: ['', Validators.required], prenom: ['', Validators.required], autresNoms: [''], dateNaissance: [''], lieuNaissance: [''], adresse: [''], ville: [''], autoriserAides: [false], maxPeripheriques: [0, [Validators.min(0)]], + // Connexion & limites + phone: ['', Validators.required], pin: [''], limiteInferieure: [0], limiteSuperieure: [0], limiteParTransaction: [0], limiteMinAirtime: [0], limiteMaxAirtime: [0], limitId: ['', Validators.required], + // Légales + nationalite: [''], cni: [''], cniDelivreeLe: [''], cniDelivreeA: [''], residence: [''], autreAdresse1: [''], statutMarital: [''], epoux: [''], autreTelephone: [''], + // Famille + famille: this.fb.array([]), + // TPE (stored as IDs in form, converted to full objects on submit) + tpeIds: this.fb.array([]), + }); + + this.limitService.list({ page: 1, perPage: 100, search: '', sortKey: 'nom', sortDir: 'asc' } as any).subscribe((res) => { + this.limits = res.data; + + // Find default limit + const defaultLimit = this.limits.find((l) => l.isDefault); + + // Watch for limitId changes to update selectedLimit + this.form.get('limitId')?.valueChanges.subscribe((limitId) => { + if (limitId) { + this.selectedLimit = this.limits.find((l) => l.id === limitId) || null; + } else { + this.selectedLimit = null; + } + }); + + // Set initial selectedLimit if form already has a value + const currentLimitId = this.form.get('limitId')?.value; + if (currentLimitId) { + this.selectedLimit = this.limits.find((l) => l.id === currentLimitId) || null; + } else if (defaultLimit && !this.value?.id) { + // If no limit is set and we're creating a new agent, assign the default limit + this.form.patchValue({ limitId: defaultLimit.id }); + this.selectedLimit = defaultLimit; + } + }); + // initial fetch page for TPE modal + this.fetchTpes(); + } + + get famille(): FormArray { return this.form.get('famille') as FormArray; } + get tpeArray(): FormArray { return this.form.get('tpeIds') as FormArray; } + + addFamily() { + this.famille.push( + this.fb.group({ + id: [''], // Will be set when saved + agentId: [this.value?.id || ''], + nom: ['', Validators.required], + statut: [''], + dateNaissance: [''], + sexe: [''], + }) + ); + } + removeFamily(i: number) { + const familyGroup = this.famille.at(i); + const familyId = familyGroup?.get('id')?.value; + // If it has an ID, it means it's already saved - we'll delete it when saving the agent + // For now, just remove from form + this.famille.removeAt(i); + } + + private hydrate(v?: Agent) { + this.famille.clear(); + this.tpeArray.clear(); + this.selectedLimit = null; + + if (!v) { + // Find default limit to assign it automatically + const defaultLimit = this.limits.find((l) => l.isDefault); + const defaultLimitId = defaultLimit?.id || ''; + + this.form.reset({ + code: '', + profile: '', + principalCode: '', + caisseProfile: '', + statut: 'ACTIF', + zone: '', + kiosk: '', + fonction: '', + dateEmbauche: '', + nom: '', + prenom: '', + autresNoms: '', + dateNaissance: '', + lieuNaissance: '', + adresse: '', + ville: '', + autoriserAides: false, + maxPeripheriques: 0, + phone: '', + pin: '', + limiteInferieure: 0, + limiteSuperieure: 0, + limiteParTransaction: 0, + limiteMinAirtime: 0, + limiteMaxAirtime: 0, + limitId: defaultLimitId, + nationalite: '', + cni: '', + cniDelivreeLe: '', + cniDelivreeA: '', + residence: '', + autreAdresse1: '', + statutMarital: '', + epoux: '', + autreTelephone: '', + }); + + // Set selectedLimit if default limit exists + if (defaultLimit) { + this.selectedLimit = defaultLimit; + } + return; + } + + this.form.patchValue(v); + + // Set selected limit if limitId exists + if (v.limitId) { + this.selectedLimit = this.limits.find((l) => l.id === v.limitId) || null; + } + + // Load family members separately via AgentFamilyMemberService + if (v.id) { + this.familyMemberService.getByAgentId(v.id).subscribe({ + next: (members) => { + members.forEach((m) => { + this.famille.push( + this.fb.group({ + id: [m.id || ''], + agentId: [m.agentId || v.id], + nom: [m.nom, Validators.required], + statut: [m.statut || ''], + dateNaissance: [m.dateNaissance || ''], + sexe: [m.sexe || ''], + }) + ); + }); + }, + error: () => { + // If error, just continue without family members + }, + }); + } + + // Load assigned TPE IDs from tpes array + (v.tpes ?? []).forEach((tpe) => this.tpeArray.push(this.fb.control(tpe.id))); + } + + onToggleTpe(id: string, checked: boolean) { + const idx = this.tpeArray.value.indexOf(id); + if (checked && idx === -1) this.tpeArray.push(this.fb.control(id)); + if (!checked && idx !== -1) this.tpeArray.removeAt(idx); + } + + openAssignTpe() { + this.assignModalOpen = true; + this.fetchTpes(); + } + closeAssignTpe() { + this.assignModalOpen = false; + // Refresh TPE list to show current assignments + this.fetchTpes(); + } + onTpeSearch(q: string) { this.tpeSearch = q; this.tpePage = 1; this.fetchTpes(); } + onTpePageChange(p: number) { this.tpePage = p; this.fetchTpes(); } + onTpePerPageChange(pp: number) { this.tpePerPage = pp; this.fetchTpes(); } + isSelectedTpe(id: string) { return (this.tpeArray.value as string[]).includes(id); } + + fetchTpes() { + this.tpeService + .list({ page: this.tpePage, perPage: this.tpePerPage, search: this.tpeSearch, sortKey: 'imei', sortDir: 'asc' } as any) + .subscribe((res) => { + // Only show VALIDE TPEs that are either not assigned or assigned to this agent + const currentAgentId = this.value?.id; + this.tpeRows = res.data.filter((t) => { + if (t.statut !== 'VALIDE') return false; + // If TPE is assigned but to this agent, show it + if (t.assigne && currentAgentId) { + // We need to check if this TPE is assigned to this agent + // For now, show all VALIDE TPEs - the backend should handle assignment logic + return true; + } + // Show unassigned TPEs + return !t.assigne; + }); + this.tpeTotal = this.tpeRows.length; + }); + } + + tpeLabel(id: string): string { + const list = [...(this.tpeRows ?? []), ...(this.tpes ?? [])]; + const found = list.find((t) => t.id === id); + if (found) { + return `${found.imei} (${found.marque} ${found.modele})`; + } + // Try to load from service if not in current list + return id; + } + + // === Validation helpers === + error(control: string): string { + // Handle nested paths like "famille.0.nom" + const parts = control.split('.'); + let c: any = this.form; + for (const part of parts) { + if (c.get) { + c = c.get(part); + } else if (c.controls && c.controls[part]) { + c = c.controls[part]; + } else { + return ''; + } + if (!c) return ''; + } + + const invalid = c.invalid && (c.touched || this.submitted); + if (!invalid) return ''; + const e = c.errors || {}; + if (e['required']) return 'Ce champ est obligatoire'; + if (e['min']) return `Valeur minimale: ${e['min'].min}`; + if (e['minlength']) return `Minimum ${e['minlength'].requiredLength} caractères`; + return 'Valeur invalide'; + } + + onSubmit() { + this.submitted = true; + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + const raw = this.form.getRawValue() as any; + + // Convert TPE IDs to full TPE objects + const tpeIds = [...this.tpeArray.value] as string[]; + const tpes: TpeDevice[] = tpeIds + .map((id) => { + // Try to find in tpeRows first (current modal list) + const found = this.tpeRows.find((t) => t.id === id); + if (found) return found; + // Try to find in existing tpes (from value) + const existing = this.value?.tpes?.find((t) => t.id === id); + if (existing) return existing; + // If not found, create a minimal TPE object (backend will fill in details) + // This shouldn't happen in normal flow, but handle gracefully + return null; + }) + .filter((t): t is TpeDevice => t !== null); + + // Prepare agent payload (without famille - family members are handled separately) + const payload: Agent = { + ...(this.value ?? {}), + id: this.value?.id || '', + ...raw, + tpes: tpes, + } as Agent; + + // Remove tpeIds from payload (it's not part of Agent interface) + delete (payload as any).tpeIds; + + // Emit the agent payload first + // Family members will be saved separately in the parent component + this.save.emit(payload); + } + + // Get family members data for saving + getFamilyMembersData(): Array<{ id?: string; agentId: string; nom: string; statut?: string; dateNaissance?: string; sexe?: string }> { + return this.famille.value.map((fm: any) => ({ + id: fm.id || undefined, + agentId: fm.agentId || this.value?.id || '', + nom: fm.nom, + statut: fm.statut || undefined, + dateNaissance: fm.dateNaissance || undefined, + sexe: fm.sexe || undefined, + })); + } + + resetForm() { + this._value = undefined; + this.selectedLimit = null; + this.famille.clear(); + this.tpeArray.clear(); + + // Find default limit to assign it automatically + const defaultLimit = this.limits.find((l) => l.isDefault); + const defaultLimitId = defaultLimit?.id || ''; + + this.form.reset({ + code: '', + profile: '', + principalCode: '', + caisseProfile: '', + statut: 'ACTIF', + zone: '', + kiosk: '', + fonction: '', + dateEmbauche: '', + nom: '', + prenom: '', + autresNoms: '', + dateNaissance: '', + lieuNaissance: '', + adresse: '', + ville: '', + autoriserAides: false, + maxPeripheriques: 0, + phone: '', + pin: '', + limiteInferieure: 0, + limiteSuperieure: 0, + limiteParTransaction: 0, + limiteMinAirtime: 0, + limiteMaxAirtime: 0, + limitId: defaultLimitId, + nationalite: '', + cni: '', + cniDelivreeLe: '', + cniDelivreeA: '', + residence: '', + autreAdresse1: '', + statutMarital: '', + epoux: '', + autreTelephone: '', + }); + + // Set selectedLimit if default limit exists + if (defaultLimit) { + this.selectedLimit = defaultLimit; + } + this.submitted = false; + } +} + + diff --git a/src/app/shared/forms/course-form/course-form.css b/src/app/shared/forms/course-form/course-form.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/forms/course-form/course-form.html b/src/app/shared/forms/course-form/course-form.html new file mode 100644 index 0000000..5e091be --- /dev/null +++ b/src/app/shared/forms/course-form/course-form.html @@ -0,0 +1,393 @@ +
    + +
    +
    +
    + 🏇 +
    +
    +

    Informations principales

    +

    Détails essentiels de la course

    +
    +
    + +
    + + + + + @for (t of courseTypes; track t.value) { + {{ t.label }} + } + + + @if (isInvalid('type')) { +

    + {{ errorMessage('type') }} +

    + } +
    + + + + + + @if (isInvalid('numero')) { +

    + {{ errorMessage('numero') }} +

    + } +
    +
    + + + + + + @if (isInvalid('nom')) { +

    + {{ errorMessage('nom') }} +

    + } +
    +
    +
    +
    + + +
    +
    +
    + 🕓 +
    +
    +

    Dates & heures

    +

    + Renseignez les horaires clés de la course et des périodes de paris +

    +
    +
    + +
    + + +
    + + +
    + @if(isInvalid('dateDepartCourse')) { +

    + {{ errorMessage('dateDepartCourse') }} +

    + } +
    + +
    + + +
    + + +
    +
    + + + +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    + 📅 +
    +
    +

    Réunion associée

    +

    + Sélectionnez la réunion concernée ou créez-en une nouvelle +

    +
    +
    + + + + + + @if (loadingReunions()) { + Chargement des réunions... + } @else { @for (r of filteredReunions(); track r.id) { + + {{ r.nom }} – {{ r.hippodrome.nom }} ({{ r.hippodrome.ville }}) + + } } + + + +
    + + +
    +
    +
    + ⚙️ +
    +
    +

    Détails techniques

    +

    Caractéristiques de la course

    +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + +
    +
    +
    + 📊 +
    +
    +

    Statut et numérotation

    +
    +
    + +
    + + + + + @for (s of courseStatus; track s.value) { + {{ s.label }} + } + + + + + + + + + + +
    +
    + + +
    +
    +
    + 👤 +
    +
    +

    Informations de suivi

    +

    Traçabilité et validation

    +
    +
    + +
    + + + + + + + + + + + + + +
    +
    +
    diff --git a/src/app/shared/forms/course-form/course-form.spec.ts b/src/app/shared/forms/course-form/course-form.spec.ts new file mode 100644 index 0000000..8090cd5 --- /dev/null +++ b/src/app/shared/forms/course-form/course-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CourseForm } from './course-form'; + +describe('CourseForm', () => { + let component: CourseForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CourseForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CourseForm); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/forms/course-form/course-form.ts b/src/app/shared/forms/course-form/course-form.ts new file mode 100644 index 0000000..fd4a291 --- /dev/null +++ b/src/app/shared/forms/course-form/course-form.ts @@ -0,0 +1,447 @@ +import { CommonModule } from '@angular/common'; +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + Output, + computed, + signal, + OnInit, + OnDestroy, + effect, +} from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { Course, CourseStatut, CourseType } from 'src/app/core/interfaces/course'; +import { Reunion } from 'src/app/core/interfaces/reunion'; +import { ReunionService } from 'src/app/core/services/reunion'; +import { Subscription } from 'rxjs'; + +@Component({ + selector: 'app-course-form', + standalone: true, + templateUrl: './course-form.html', + styleUrls: ['./course-form.css'], + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardSelectComponent, + ZardSelectItemComponent, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CourseForm implements OnInit, AfterViewInit, OnDestroy { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + private _value?: Course; + @Input() set value(v: Course | undefined) { + this._value = v; + } + get value(): Course | undefined { + return this._value; + } + + form: FormGroup; + submitted = false; + + reunions = signal([]); + loadingReunions = signal(false); + searchQuery = signal(''); + selectedReunionLabel = signal(''); + initializing = signal(false); + private subs = new Subscription(); + + constructor( + private fb: FormBuilder, + private reunionService: ReunionService, + private cdr: ChangeDetectorRef + ) { + this.form = this.fb.group({ + type: ['', Validators.required], + numero: [null, [Validators.required, Validators.min(1)]], + nom: ['', [Validators.required, Validators.minLength(3)]], + dateDepartCourse: ['', Validators.required], + dateDebutParis: ['', Validators.required], + dateFinParis: ['', Validators.required], + reunionId: new FormControl(null, Validators.required), + reunionCourse: [1], + particularite: [''], + partants: [null, [Validators.required, Validators.min(1)]], + distance: [''], + condition: [''], + statut: [CourseStatut.PROGRAMMEE, Validators.required], + createdBy: ['agent-001'], + validatedBy: [''], + }); + + // Watch for reunionId changes to update the label + // This handles BOTH create and edit modes when user selects a reunion + this.subs.add( + this.form.get('reunionId')?.valueChanges.subscribe((reunionId) => { + // Skip if we're initializing (to avoid interfering with form initialization) + if (this.initializing()) { + return; + } + + // Handle empty/null values + if (!reunionId || reunionId === '') { + this.selectedReunionLabel.set(''); + return; + } + + // Update label when user selects a reunion (both create and edit) + if (this.reunions().length > 0) { + // Try both string and direct comparison since form control might convert types + const matchingReunion = this.reunions().find( + (r) => String(r.id) === String(reunionId) || r.id === reunionId + ); + if (matchingReunion) { + const reunionLabel = `${matchingReunion.nom} – ${matchingReunion.hippodrome.nom} (${matchingReunion.hippodrome.ville})`; + this.selectedReunionLabel.set(reunionLabel); + // Force change detection to ensure the label is displayed immediately + this.cdr.markForCheck(); + } else { + this.selectedReunionLabel.set(''); + } + } else { + this.selectedReunionLabel.set(''); + } + }) || new Subscription() + ); + + // Effect to handle form initialization when value or reunions change + effect(() => { + const v = this.value; + const reunionsList = this.reunions(); + const isLoading = this.loadingReunions(); + + // CRITICAL: Enable/disable reunion control based on loading state + // This is the most important part for create mode to work + const reunionControl = this.form.get('reunionId'); + if (isLoading) { + reunionControl?.disable({ emitEvent: false }); + } else { + reunionControl?.enable({ emitEvent: false }); + // Force change detection after enabling to ensure the select is clickable + this.cdr.markForCheck(); + } + + // Handle edit mode (value is defined) - populate form with course data + if (v !== null && v !== undefined) { + this.initializing.set(true); + + let reunionId: string | null = null; + let reunionLabel: string = ''; + if (v.reunion?.id) { + reunionId = String(v.reunion.id); + if (reunionsList.length > 0) { + const matchingReunion = reunionsList.find((r) => String(r.id) === reunionId); + if (matchingReunion) { + reunionLabel = `${matchingReunion.nom} – ${matchingReunion.hippodrome.nom} (${matchingReunion.hippodrome.ville})`; + this.selectedReunionLabel.set(reunionLabel); + } else { + reunionId = null; + this.selectedReunionLabel.set(''); + } + } else { + if (v.reunion.nom) { + reunionLabel = `${v.reunion.nom} – ${v.reunion.hippodrome?.nom || ''} (${ + v.reunion.hippodrome?.ville || '' + })`; + this.selectedReunionLabel.set(reunionLabel); + } + reunionId = null; + } + } else { + this.selectedReunionLabel.set(''); + } + + this.form.patchValue( + { + type: v.type ?? '', + numero: v.numero ?? null, + nom: v.nom ?? '', + dateDepartCourse: v.dateDepartCourse ?? '', + dateDebutParis: v.dateDebutParis ?? v.dateDepartCourse ?? '', + dateFinParis: v.dateFinParis ?? v.dateDepartCourse ?? '', + reunionId, + reunionCourse: (v as any).reunionCourse ?? v.numero ?? 1, + particularite: (v as any).particularite ?? '', + partants: (v as any).partants ?? null, + distance: (v as any).distance ?? null, + condition: (v as any).condition ?? '', + statut: v.statut ?? CourseStatut.PROGRAMMEE, + createdBy: (v as any).createdBy ?? 'agent-001', + validatedBy: (v as any).validatedBy ?? '', + }, + { emitEvent: false } + ); + + this.form.markAsPristine(); + this.form.markAsUntouched(); + queueMicrotask(() => this.initializing.set(false)); + } + // Create mode (v === null or undefined) - DO NOTHING except ensure control is enabled + // The form already has default values, just let the user fill it + // The valueChanges subscription will handle label updates when user selects + + // When reunions finish loading - re-apply value if we have one (for edit mode) + else if (reunionsList.length > 0 && !isLoading) { + // Reunions just finished loading - re-apply value if we have one + const currentValue = this.value; + if (currentValue?.reunion?.id) { + const reunionId = String(currentValue.reunion.id); + const matchingReunion = reunionsList.find((r) => String(r.id) === reunionId); + if (matchingReunion) { + // Set the label manually + const reunionLabel = `${matchingReunion.nom} – ${matchingReunion.hippodrome.nom} (${matchingReunion.hippodrome.ville})`; + this.selectedReunionLabel.set(reunionLabel); + // Wait for Angular to render the select items + setTimeout(() => { + this.form.patchValue({ reunionId }, { emitEvent: false }); + this.cdr.detectChanges(); + }, 50); + } + } + } + }); + } + + private hydrateFromValue(v?: Course) { + if (v) { + const patch = { + type: v.type ?? '', + numero: v.numero ?? null, + nom: v.nom ?? '', + dateDepartCourse: v.dateDepartCourse ?? '', + dateDebutParis: v.dateDebutParis ?? v.dateDepartCourse ?? '', + dateFinParis: v.dateFinParis ?? v.dateDepartCourse ?? '', + reunionId: (v as any).reunionId ?? v.reunion?.id ?? '', + reunionCourse: (v as any).reunionCourse ?? v.numero ?? 1, + particularite: (v as any).particularite ?? '', + partants: (v as any).partants ?? null, + distance: (v as any).distance ?? null, + condition: (v as any).condition ?? '', + statut: v.statut ?? CourseStatut.PROGRAMMEE, + createdBy: (v as any).createdBy ?? 'agent-001', + validatedBy: (v as any).validatedBy ?? '', + }; + this.form.reset(patch); + this.form.markAsPristine(); + this.form.markAsUntouched(); + } else { + this.form.reset({ + type: '', + numero: null, + nom: '', + dateDepartCourse: '', + dateDebutParis: '', + dateFinParis: '', + reunionId: '', + reunionCourse: 1, + particularite: '', + partants: null, + distance: null, + condition: '', + statut: CourseStatut.PROGRAMMEE, + createdBy: 'agent-001', + validatedBy: '', + }); + this.form.markAsPristine(); + this.form.markAsUntouched(); + } + } + + isInvalid(control: string): boolean { + const ctrl = this.form.get(control); + return !!(ctrl && ctrl.invalid && (ctrl.touched || this.submitted)); + } + + errorMessage(control: string): string | null { + const c = this.form.get(control); + if (!c || !c.errors) return null; + if (c.errors['required']) return 'Ce champ est obligatoire'; + if (c.errors['minlength']) return `Minimum ${c.errors['minlength'].requiredLength} caractères`; + if (c.errors['min']) return `Valeur minimale : ${c.errors['min'].min}`; + return null; + } + + getDatePart(control: string): string { + const val = this.form.get(control)?.value; + return val ? new Date(val).toISOString().slice(0, 10) : ''; + } + + getTimePart(control: string): string { + const val = this.form.get(control)?.value; + if (!val) return ''; + const d = new Date(val); + return d.toISOString().slice(11, 16); + } + + setDatePart(control: string, date: string) { + const current = new Date(this.form.get(control)?.value || new Date()); + const [h, m] = [current.getHours(), current.getMinutes()]; + const newDate = new Date( + `${date}T${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:00Z` + ); + this.form.patchValue({ [control]: newDate.toISOString() }); + } + + setTimePart(control: string, time: string) { + const current = new Date(this.form.get(control)?.value || new Date()); + const [h, m] = time.split(':').map(Number); + current.setHours(h, m); + this.form.patchValue({ [control]: current.toISOString() }); + } + + onSubmit() { + this.submitted = true; + if (!this.form.valid) { + this.form.markAllAsTouched(); + return; + } + + const raw = this.form.getRawValue() as any; + + // Find the reunion object from the reunions list + const reunionId = raw.reunionId; + const reunion = this.reunions().find((r) => String(r.id) === String(reunionId)); + + if (!reunion) { + console.error('Reunion not found:', reunionId); + return; + } + + const payload: Partial = { + ...(this.value ?? {}), + type: raw.type, + numero: +raw.numero, + nom: raw.nom, + dateDepartCourse: raw.dateDepartCourse, + dateDebutParis: raw.dateDebutParis, + dateFinParis: raw.dateFinParis, + reunion: reunion!, // Will be set if reunion is found + reunionCourse: +raw.reunionCourse, + particularite: raw.particularite ?? '', + partants: +raw.partants, + distance: +raw.distance, + condition: raw.condition ?? '', + statut: raw.statut, + createdBy: raw.createdBy, + validatedBy: raw.validatedBy || null, + }; + + // Ensure reunion is found + if (!reunion) { + console.error('Reunion not found:', reunionId); + return; + } + + this.save.emit(payload as Course); + } + + // === Filter Reunions === + filteredReunions = computed(() => { + const q = this.searchQuery().toLowerCase(); + return this.reunions().filter( + (r) => + r.nom.toLowerCase().includes(q) || + r.hippodrome.nom.toLowerCase().includes(q) || + r.hippodrome.ville.toLowerCase().includes(q) + ); + }); + + courseTypes = [ + { label: 'Tiercé', value: CourseType.TIERCE }, + { label: 'Quarté + Tiercé', value: CourseType.QUARTE }, + { label: 'Quinté + Tiercé', value: CourseType.QUINTE }, + ]; + + courseStatus = [ + { label: 'Programmée', value: CourseStatut.PROGRAMMEE }, + { label: 'Créée', value: CourseStatut.CREATED }, + { label: 'Validée', value: CourseStatut.VALIDATED }, + { label: 'En cours', value: CourseStatut.RUNNING }, + { label: 'Clôturée', value: CourseStatut.CLOSED }, + { label: 'Annulée', value: CourseStatut.CANCELED }, + ]; + + ngOnInit() { + // Fetch reunions from API + this.loadingReunions.set(true); + this.subs.add( + this.reunionService.list({ page: 1, perPage: 1000 }, false).subscribe({ + next: (result) => { + this.reunions.set(result.data); + this.loadingReunions.set(false); + // Force enable the control after loading + setTimeout(() => { + const reunionControl = this.form.get('reunionId'); + reunionControl?.enable({ emitEvent: false }); + this.cdr.markForCheck(); + }, 100); + }, + error: (err) => { + console.error('Error loading reunions:', err); + this.loadingReunions.set(false); + }, + }) + ); + } + + ngAfterViewInit() { + // After view is initialized, ensure the reunion value and label are set correctly + const currentValue = this.value; + if (currentValue?.reunion?.id && this.reunions().length > 0) { + const reunionId = String(currentValue.reunion.id); + const matchingReunion = this.reunions().find((r) => String(r.id) === reunionId); + if (matchingReunion) { + // Set the label manually + const reunionLabel = `${matchingReunion.nom} – ${matchingReunion.hippodrome.nom} (${matchingReunion.hippodrome.ville})`; + this.selectedReunionLabel.set(reunionLabel); + // Wait a bit for the select items to be fully rendered + setTimeout(() => { + const currentFormValue = this.form.get('reunionId')?.value; + if (String(currentFormValue) !== reunionId) { + this.form.patchValue({ reunionId }, { emitEvent: false }); + this.cdr.detectChanges(); + } + }, 100); + } + } + } + + onReunionSelectionChange(value: string) { + // Immediately update the label when user selects a reunion + if (value && this.reunions().length > 0) { + const matchingReunion = this.reunions().find( + (r) => String(r.id) === String(value) || r.id === value + ); + if (matchingReunion) { + const reunionLabel = `${matchingReunion.nom} – ${matchingReunion.hippodrome.nom} (${matchingReunion.hippodrome.ville})`; + this.selectedReunionLabel.set(reunionLabel); + this.cdr.markForCheck(); + } else { + } + } + } + + ngOnDestroy() { + this.subs.unsubscribe(); + } +} diff --git a/src/app/shared/forms/hippodrome-form/hippodrome-form.css b/src/app/shared/forms/hippodrome-form/hippodrome-form.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/forms/hippodrome-form/hippodrome-form.html b/src/app/shared/forms/hippodrome-form/hippodrome-form.html new file mode 100644 index 0000000..e4189dd --- /dev/null +++ b/src/app/shared/forms/hippodrome-form/hippodrome-form.html @@ -0,0 +1,85 @@ +
    + +
    + + + + + + + + + + + + + +
    + + + + + + + @for (c of countries; track c.value) { + {{ c.label }} + } + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    +
    + + + @if(showInternalActions()) { +
    + + +
    + } +
    diff --git a/src/app/shared/forms/hippodrome-form/hippodrome-form.spec.ts b/src/app/shared/forms/hippodrome-form/hippodrome-form.spec.ts new file mode 100644 index 0000000..cc1b11f --- /dev/null +++ b/src/app/shared/forms/hippodrome-form/hippodrome-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HippodromeForm } from './hippodrome-form'; + +describe('HippodromeForm', () => { + let component: HippodromeForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HippodromeForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(HippodromeForm); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/forms/hippodrome-form/hippodrome-form.ts b/src/app/shared/forms/hippodrome-form/hippodrome-form.ts new file mode 100644 index 0000000..df57f57 --- /dev/null +++ b/src/app/shared/forms/hippodrome-form/hippodrome-form.ts @@ -0,0 +1,151 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + effect, + input, + OnDestroy, + output, + signal, +} from '@angular/core'; +import { + FormBuilder, + ReactiveFormsModule, + Validators, + FormGroup, + FormControl, +} from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { Hippodrome } from 'src/app/core/interfaces/hippodrome'; + +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; + +@Component({ + selector: 'app-hippodrome-form', + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardSelectComponent, + ZardSelectItemComponent, + ZardButtonComponent, + ], + templateUrl: './hippodrome-form.html', + styleUrls: ['./hippodrome-form.css'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class HippodromeForm implements OnDestroy { + // Inputs + value = input | null>(null); + showInternalActions = input(false); + + // Outputs + save = output>(); + cancel = output(); + + submitting = signal(false); + initializing = signal(false); + submitted = signal(false); + paysInteracted = signal(false); + + form!: FormGroup; + private subs = new Subscription(); + + countries = [ + { value: 'France', label: 'France' }, + { value: 'Mali', label: 'Mali' }, + { value: 'Sénégal', label: 'Sénégal' }, + { value: 'Maroc', label: 'Maroc' }, + { value: 'Tunisie', label: 'Tunisie' }, + { value: 'Algérie', label: 'Algérie' }, + { value: 'Côte d’Ivoire', label: 'Côte d’Ivoire' }, + { value: 'Belgique', label: 'Belgique' }, + { value: 'Suisse', label: 'Suisse' }, + { value: 'Canada', label: 'Canada' }, + { value: 'Mauritanie', label: 'Mauritanie' }, + ]; + + constructor(private fb: FormBuilder) { + this.form = this.fb.group({ + nom: ['', Validators.required], + ville: ['', Validators.required], + pays: new FormControl(null, { validators: [Validators.required] }), + capacite: new FormControl(null, { + validators: [Validators.min(1)], + }), + actif: new FormControl(true), + description: new FormControl(null), + }); + + // Auto-update form when editing + effect( + () => { + const v = this.value(); + this.initializing.set(true); + + this.form.reset( + { nom: '', ville: '', pays: null, capacite: null, actif: true, description: null }, + { emitEvent: false } + ); + + if (v) + this.form.patchValue( + { + nom: v.nom ?? '', + ville: v.ville ?? '', + pays: v.pays ?? null, + capacite: v.capacite ?? null, + actif: v.actif ?? true, + description: v.description ?? null, + }, + { emitEvent: false } + ); + + this.form.markAsPristine(); + this.form.markAsUntouched(); + this.paysInteracted.set(false); + queueMicrotask(() => this.initializing.set(false)); + }, + { allowSignalWrites: true } + ); + + const pays = this.form.get('pays')!; + this.subs.add(pays.valueChanges.subscribe(() => this.paysInteracted.set(true))); + } + + invalid(name: keyof Hippodrome) { + const c = this.form.get(name as string); + return !!((this.submitted() || c?.touched || c?.dirty) && c?.invalid); + } + + showPaysError(): boolean { + const c = this.form.get('pays'); + if (!c || this.initializing()) return false; + return (this.submitted() || this.paysInteracted()) && c.hasError('required'); + } + + onSubmit() { + this.submitted.set(true); + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + this.submitting.set(true); + this.save.emit(this.form.value as Partial); + + // Reset form state after emitting (parent will handle actual form reset via value input) + // But reset submitting state so button is enabled again + this.submitting.set(false); + this.submitted.set(false); + } + + ngOnDestroy() { + this.subs.unsubscribe(); + } +} diff --git a/src/app/shared/forms/limit-form/limit-form.html b/src/app/shared/forms/limit-form/limit-form.html new file mode 100644 index 0000000..3fd232b --- /dev/null +++ b/src/app/shared/forms/limit-form/limit-form.html @@ -0,0 +1,74 @@ +
    +
    + +
    + +
    + +
    + + +
    + +
    +
    + +
    + +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +
    +
    diff --git a/src/app/shared/forms/limit-form/limit-form.ts b/src/app/shared/forms/limit-form/limit-form.ts new file mode 100644 index 0000000..3e5fc8c --- /dev/null +++ b/src/app/shared/forms/limit-form/limit-form.ts @@ -0,0 +1,112 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { AgentLimit } from 'src/app/core/interfaces/agent-limit'; + +@Component({ + selector: 'app-limit-form', + standalone: true, + templateUrl: './limit-form.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, ReactiveFormsModule, ZardFormModule, ZardInputDirective], +}) +export class LimitForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + private _value?: AgentLimit; + @Input() set value(v: AgentLimit | undefined) { + this._value = v; + this.hydrate(v); + } + get value(): AgentLimit | undefined { + return this._value; + } + + form: FormGroup; + submitted = false; + + constructor(private fb: FormBuilder) { + this.form = this.fb.group({ + code: ['', Validators.required], + configCode: ['', Validators.required], + nom: ['', Validators.required], + isDefault: [false], + actif: [true], + betMin: [0], + betMax: [0], + maxBet: [0], + maxDisburseBet: [0], + airtimeMin: [0], + airtimeMax: [0], + }); + } + + error(control: string): string { + const c = this.form.get(control); + if (!c) return ''; + const invalid = c.invalid && (c.touched || this.submitted); + if (!invalid) return ''; + const e = c.errors || {}; + if (e['required']) return 'Ce champ est obligatoire'; + return 'Valeur invalide'; + } + + private hydrate(v?: AgentLimit) { + if (!v) { + this.form.reset({ + code: '', + configCode: '', + nom: '', + isDefault: false, + actif: true, + betMin: 0, + betMax: 0, + maxBet: 0, + maxDisburseBet: 0, + airtimeMin: 0, + airtimeMax: 0, + }); + return; + } + this.form.reset({ + code: v.code, + configCode: v.configCode, + nom: v.nom, + isDefault: v.isDefault, + actif: v.actif, + betMin: v.betMin ?? 0, + betMax: v.betMax ?? 0, + maxBet: v.maxBet ?? 0, + maxDisburseBet: v.maxDisburseBet ?? 0, + airtimeMin: v.airtimeMin ?? 0, + airtimeMax: v.airtimeMax ?? 0, + }); + } + + onSubmit() { + this.submitted = true; + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + const raw = this.form.getRawValue() as any; + const payload: AgentLimit = { + id: this.value?.id ?? '', + code: raw.code, + configCode: raw.configCode, + nom: raw.nom, + isDefault: !!raw.isDefault, + actif: !!raw.actif, + betMin: +raw.betMin, + betMax: +raw.betMax, + maxBet: +raw.maxBet, + maxDisburseBet: +raw.maxDisburseBet, + airtimeMin: +raw.airtimeMin, + airtimeMax: +raw.airtimeMax, + }; + this.save.emit(payload); + } +} diff --git a/src/app/shared/forms/nonpartant-form/nonpartant-form.css b/src/app/shared/forms/nonpartant-form/nonpartant-form.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/forms/nonpartant-form/nonpartant-form.html b/src/app/shared/forms/nonpartant-form/nonpartant-form.html new file mode 100644 index 0000000..682fd6e --- /dev/null +++ b/src/app/shared/forms/nonpartant-form/nonpartant-form.html @@ -0,0 +1,74 @@ +
    +
    +
    +

    + Déclarer les non-partants +

    + + {{ chosenNumbers().length }} sélection(s) / {{ partantsMax() }} partants + +
    + +
    + +
    + @for (row of rows.controls; track $index) { +
    + + + + + @for (num of optionsForRow($index); track num) { + {{ num }} + } + + + @if (rows.at($index).get('numero')?.errors?.['range']) { +
    Numéro en dehors de la plage autorisée.
    + } @if (rows.at($index).get('numero')?.hasError('required') && + rows.at($index).get('numero')?.touched) { +
    Ce champ est obligatoire.
    + } +
    + + + + + + + + + + + + + + + + +
    + } +
    +
    diff --git a/src/app/shared/forms/nonpartant-form/nonpartant-form.spec.ts b/src/app/shared/forms/nonpartant-form/nonpartant-form.spec.ts new file mode 100644 index 0000000..0f4bfc4 --- /dev/null +++ b/src/app/shared/forms/nonpartant-form/nonpartant-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NonpartantForm } from './nonpartant-form'; + +describe('NonpartantForm', () => { + let component: NonpartantForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NonpartantForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NonpartantForm); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/forms/nonpartant-form/nonpartant-form.ts b/src/app/shared/forms/nonpartant-form/nonpartant-form.ts new file mode 100644 index 0000000..a516c7f --- /dev/null +++ b/src/app/shared/forms/nonpartant-form/nonpartant-form.ts @@ -0,0 +1,221 @@ +import { CommonModule } from '@angular/common'; +import { + Component, + EventEmitter, + Input, + OnChanges, + OnDestroy, + OnInit, + Output, + SimpleChanges, + computed, + signal, +} from '@angular/core'; +import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { Subscription } from 'rxjs'; +import { Course } from 'src/app/core/interfaces/course'; + +/** One non-partant row shape (controls) */ +type NonPartantRow = { + numero: FormControl; + motif: FormControl; + declarePar: FormControl; +}; + +/** Whole form shape */ +type NonPartantFormShape = { + nonPartants: FormArray>; +}; + +@Component({ + selector: 'app-nonpartant-form', + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardButtonComponent, + ZardSelectComponent, + ZardSelectItemComponent, + ], + templateUrl: './nonpartant-form.html', +}) +export class NonPartantForm implements OnInit, OnChanges, OnDestroy { + @Input() course!: Course | null; + + /** We emit the UPDATED course with merged nonPartants */ + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + // 🔒 Strongly typed form + form = new FormGroup({ + nonPartants: new FormArray>([]), + }); + + private sub = new Subscription(); + + // reactive signals + partantsMax = signal(0); + chosenNumbers = signal([]); // current selected numbers across rows + + // --------- lifecycle ---------- + ngOnInit(): void { + this.seedFromCourse(); + + // Keep chosenNumbers in sync + this.sub.add( + this.rows.valueChanges.subscribe(() => { + const nums = this.rows.controls + .map((g) => g.controls.numero.value) + .filter((n): n is number => Number.isFinite(n as number)); + this.chosenNumbers.set(nums); + }) + ); + + // initial seed chosenNumbers + const nums = this.rows.controls + .map((g) => g.controls.numero.value) + .filter((n): n is number => Number.isFinite(n as number)); + this.chosenNumbers.set(nums); + } + + ngOnChanges(changes: SimpleChanges): void { + if ('course' in changes && this.course) { + this.seedFromCourse(); + } + } + + ngOnDestroy(): void { + this.sub.unsubscribe(); + } + + // --------- typed helpers ---------- + get rows(): FormArray> { + return this.form.controls.nonPartants; + } + + private createRow(initial?: { + numero?: number | null; + motif?: string; + declarePar?: string; + }): FormGroup { + return new FormGroup({ + numero: new FormControl(initial?.numero ?? null, { + validators: [Validators.required, Validators.min(1)], + nonNullable: false, + }), + motif: new FormControl(initial?.motif ?? '', { + nonNullable: true, + }), + declarePar: new FormControl(initial?.declarePar ?? 'commissaire-1', { + validators: [Validators.required], + nonNullable: true, + }), + }); + } + + private seedFromCourse() { + const max = this.course?.partants ?? 0; + this.partantsMax.set(max); + + const arr = new FormArray>([]); + + const existing = this.course?.nonPartants ?? []; + if (existing.length) { + for (const np of existing) { + arr.push( + this.createRow({ + numero: Number(np), + motif: '', + declarePar: 'commissaire-1', + }) + ); + } + } else { + arr.push(this.createRow()); + } + + // Replace control with properly typed FormArray> + this.form.setControl('nonPartants', arr); + + // Refresh chosen numbers + const nums = arr.controls + .map((g) => g.controls.numero.value) + .filter((n): n is number => Number.isFinite(n as number)); + this.chosenNumbers.set(nums); + + // Optional: coerce numero values to numbers if your z-select emits strings + // (keep this if you notice strings arriving) + this.sub.add( + arr.valueChanges.subscribe(() => { + arr.controls.forEach((g) => { + const v = g.controls.numero.value as unknown as string | number | null; + if (typeof v === 'string') { + const n = Number(v); + if (!Number.isNaN(n)) g.controls.numero.setValue(n, { emitEvent: false }); + } + }); + }) + ); + } + + addRow() { + if (!this.hasFreeSlots()) return; + this.rows.push(this.createRow()); + } + + removeRow(i: number) { + this.rows.removeAt(i); + } + + // --------- options & validation ---------- + /** All numbers 1..partants */ + allNumbers = computed(() => Array.from({ length: this.partantsMax() }, (_, i) => i + 1)); + + /** Remaining (not chosen elsewhere) */ + remainingNumbers = computed(() => { + const chosen = new Set(this.chosenNumbers()); + return this.allNumbers().filter((n) => !chosen.has(n)); + }); + + /** Options for a given row = remaining + its current value (so it stays visible) */ + optionsForRow(i: number) { + const current = this.rows.at(i).controls.numero.value ?? undefined; + const base = this.remainingNumbers(); + return typeof current === 'number' ? [...base, current].sort((a, b) => a - b) : base; + } + + hasFreeSlots(): boolean { + return this.remainingNumbers().length > 0; + } + + onSubmit() { + const max = this.partantsMax(); + let ok = true; + + this.rows.controls.forEach((g) => { + const num = g.controls.numero.value; + if (typeof num !== 'number' || num < 1 || num > max) { + g.controls.numero.setErrors({ range: true }); + ok = false; + } + }); + + if (ok && this.form.valid && this.course) { + const nonPartants = this.rows.controls.map((g) => { + const numero = g.controls.numero.value as number; + return String(numero); + }); + + this.save.emit(nonPartants); + } else { + this.form.markAllAsTouched(); + } + } +} diff --git a/src/app/shared/forms/permission-form/permission-form.html b/src/app/shared/forms/permission-form/permission-form.html new file mode 100644 index 0000000..e26ffbd --- /dev/null +++ b/src/app/shared/forms/permission-form/permission-form.html @@ -0,0 +1,18 @@ +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    +
    +
    + + diff --git a/src/app/shared/forms/permission-form/permission-form.ts b/src/app/shared/forms/permission-form/permission-form.ts new file mode 100644 index 0000000..5a00c76 --- /dev/null +++ b/src/app/shared/forms/permission-form/permission-form.ts @@ -0,0 +1,73 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { Permission } from 'src/app/core/interfaces/role'; + +@Component({ + selector: 'app-permission-form', + standalone: true, + templateUrl: './permission-form.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [CommonModule, ReactiveFormsModule, ZardFormModule, ZardInputDirective], +}) +export class PermissionForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + private _value?: Permission; + @Input() set value(v: Permission | undefined) { + this._value = v; + this.hydrateFromValue(v); + } + get value(): Permission | undefined { + return this._value; + } + + form: FormGroup; + submitted = false; + + constructor(private fb: FormBuilder) { + this.form = this.fb.group({ + name: ['', Validators.required], + description: [''], + }); + } + + private hydrateFromValue(v?: Permission) { + this.form.reset({ + name: v?.name ?? '', + description: v?.description ?? '', + }); + } + + isInvalid(control: string): boolean { + const ctrl = this.form.get(control); + return !!(ctrl && ctrl.invalid && (ctrl.touched || this.submitted)); + } + + errorMessage(control: string): string | null { + const c = this.form.get(control); + if (!c || !c.errors) return null; + if (c.errors['required']) return 'Ce champ est obligatoire'; + return null; + } + + onSubmit() { + this.submitted = true; + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + const raw = this.form.getRawValue() as any; + const payload: Permission = { + id: this.value?.id ?? '', + name: raw.name, + description: raw.description, + }; + this.save.emit(payload); + } +} + + diff --git a/src/app/shared/forms/resultat-form/resultat-form.css b/src/app/shared/forms/resultat-form/resultat-form.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/forms/resultat-form/resultat-form.html b/src/app/shared/forms/resultat-form/resultat-form.html new file mode 100644 index 0000000..2289212 --- /dev/null +++ b/src/app/shared/forms/resultat-form/resultat-form.html @@ -0,0 +1,141 @@ +
    + +
    +
    +

    {{ course.nom }}

    +

    + {{ course.reunion.nom }} • {{ course.reunion.hippodrome.nom }} +

    +
    + + {{ reqLen() }} chevaux / {{ maxNum() }} partants + +
    + + +
    + + + +
    + + +
    + @for (pg of places.controls; track $index; let i = $index) { +
    +
    +
    + @if (!isPlaceSkipped(i)) { + + {{ getActualPlaceNumber(i) }} + + Place {{ getActualPlaceNumber(i) }} + @if (hasExAequoAtPlace(i)) { + Ex-aequo + } } @else { + + — + + Place sautée + } +
    + @if (!isPlaceSkipped(i)) { + + } +
    + + @if (!isPlaceSkipped(i)) { + + @if (hasHorsesAtPlace(i)) { +
    + @for (ctrl of picksAt(i).controls; track $index; let j = $index) { @if (typeof ctrl.value + === 'number') { +
    + {{ ctrl.value }} + +
    + } } +
    + } + + +
    + @for (ctrl of picksAt(i).controls; track $index; let j = $index) { +
    + + @for (num of optionsForPlace(i, j); track num) { + {{ num }} + } + + @if (ctrl.invalid && ctrl.touched) { + Requis + } +
    + } +
    + } +
    + } +
    + + + @if (combinations().length > 0) { +
    +
    + {{ combinations().length }} combinaisons +
    +
    + @for (combo of combinations(); track $index) { + {{ + combo.join('-') + }} + } +
    +
    + } +
    diff --git a/src/app/shared/forms/resultat-form/resultat-form.spec.ts b/src/app/shared/forms/resultat-form/resultat-form.spec.ts new file mode 100644 index 0000000..f7d1858 --- /dev/null +++ b/src/app/shared/forms/resultat-form/resultat-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ResultatForm } from './resultat-form'; + +describe('ResultatForm', () => { + let component: ResultatForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ResultatForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ResultatForm); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/forms/resultat-form/resultat-form.ts b/src/app/shared/forms/resultat-form/resultat-form.ts new file mode 100644 index 0000000..5afd8f4 --- /dev/null +++ b/src/app/shared/forms/resultat-form/resultat-form.ts @@ -0,0 +1,543 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output, computed } from '@angular/core'; +import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { Course, CourseType } from 'src/app/core/interfaces/course'; +import { Resultat } from 'src/app/core/interfaces/resultat'; + +type PlaceRow = { picks: FormArray> }; +type ResultatShape = { places: FormArray> }; + +@Component({ + standalone: true, + selector: 'app-resultat-form', + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardSelectComponent, + ZardSelectItemComponent, + ], + templateUrl: './resultat-form.html', +}) +export class ResultatForm { + @Input() course!: Course; + @Input() resultat?: Resultat | null; + @Output() save = new EventEmitter(); + @Output() validate = new EventEmitter(); + @Output() confirm = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + form = new FormGroup({ + places: new FormArray>([]), + }); + + reqLen = computed(() => + this.course?.type === CourseType.TIERCE ? 3 : this.course?.type === CourseType.QUARTE ? 4 : 5 + ); + maxNum = computed(() => this.course?.partants ?? 0); + npSet = computed(() => new Set(this.course?.nonPartants ?? [])); + statut = computed((): 'CREATED' | 'VALIDATED' | 'NONE' => { + return this.resultat ? 'CREATED' : 'NONE'; + }); + + canValidate(): boolean { + return this.statut() === 'CREATED'; + } + + canConfirm(): boolean { + return this.statut() === 'VALIDATED'; + } + + // Helper methods for template + hasHorsesAtPlace(i: number): boolean { + return this.picksAt(i).controls.some((c) => typeof c.value === 'number'); + } + + getHorsesAtPlace(i: number): string { + return this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number') + .join('='); + } + + // Calculate all combinations when there are ex-aequo + combinations = computed(() => { + const places: number[][] = []; + for (let i = 0; i < this.places.length; i++) { + if (this.isPlaceSkipped(i)) continue; + const placeHorses = this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + if (placeHorses.length > 0) { + places.push(placeHorses); + } + } + + // Generate all combinations based on ex-aequo + return this.generateCombinations(places); + }); + + // Generate combinations considering ex-aequo + private generateCombinations(places: number[][]): number[][] { + if (places.length === 0) return []; + + // If all horses are in first place (all ex-aequo), generate all permutations + if (places.length === 1 && places[0].length === this.reqLen()) { + return this.generatePermutations(places[0]); + } + + // Otherwise, generate combinations based on ex-aequo in each place + const result: number[][] = []; + this.generateCombinationsRecursive(places, 0, [], result); + return result; + } + + private generateCombinationsRecursive( + places: number[][], + placeIndex: number, + current: number[], + result: number[][] + ): void { + if (placeIndex >= places.length) { + if (current.length === this.reqLen()) { + result.push([...current]); + } + return; + } + + const placeHorses = places[placeIndex]; + if (placeHorses.length === 1) { + // Single horse, add it and continue + current.push(placeHorses[0]); + this.generateCombinationsRecursive(places, placeIndex + 1, current, result); + current.pop(); + } else { + // Ex-aequo: generate all permutations of these horses + const perms = this.generatePermutations(placeHorses); + for (const perm of perms) { + // Add all horses from this place + current.push(...perm); + this.generateCombinationsRecursive(places, placeIndex + 1, current, result); + // Remove them + current.splice(current.length - perm.length); + } + } + } + + private isAutoPopulating = false; + + // Generate all permutations of an array + private generatePermutations(arr: number[]): number[][] { + if (arr.length <= 1) return [arr]; + const result: number[][] = []; + for (let i = 0; i < arr.length; i++) { + const rest = [...arr.slice(0, i), ...arr.slice(i + 1)]; + const perms = this.generatePermutations(rest); + for (const perm of perms) { + result.push([arr[i], ...perm]); + } + } + return result; + } + + // Check if a place has ex-aequo (multiple horses) + hasExAequoAtPlace(i: number): boolean { + const placeHorses = this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + return placeHorses.length > 1; + } + + // Check if a place should be skipped + // Simple logic: If previous places with horses have consumed position (i+1), skip it + isPlaceSkipped(i: number): boolean { + if (i === 0) return false; // First place is never skipped + + // Calculate total positions consumed by previous places (only places with horses) + let totalConsumed = 0; + for (let j = 0; j < i; j++) { + const horses = this.picksAt(j) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + // Only places with horses consume positions + if (horses.length > 0) { + totalConsumed += horses.length; + } + } + + // The place at index i should be at position (i + 1) if no ex-aequo + // If totalConsumed >= (i + 1), then position (i+1) is already taken, so skip + // Example: If place 0 has 2 horses, totalConsumed = 2 + // Place 1 (i=1) should be at position 2, but 2 >= 2, so skipped ✓ + // Place 2 (i=2) should be at position 3, but 2 >= 3 is false, so active ✓ + return totalConsumed >= i + 1; + } + + // Get the actual place number displayed to user + getActualPlaceNumber(i: number): number { + if (i === 0) return 1; + + // Count actual positions used by non-skipped places before this one + let position = 1; + for (let j = 0; j < i; j++) { + if (!this.isPlaceSkipped(j)) { + const horses = this.picksAt(j) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + position += horses.length > 0 ? horses.length : 1; + } + } + return position; + } + + // Count total horses selected across all places + totalHorsesSelected = computed(() => { + let total = 0; + for (let i = 0; i < this.places.length; i++) { + const placeHorses = this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + total += placeHorses.length; + } + return total; + }); + + ngOnInit() { + this.seed(); + // Watch for changes to auto-populate places when ex-aequo is detected + this.setupAutoPopulate(); + } + ngOnChanges() { + this.seed(); + } + + private setupAutoPopulate() { + // Watch for changes in all places to clear skipped places + this.places.controls.forEach((placeGroup) => { + placeGroup.controls.picks.valueChanges.subscribe(() => { + this.autoPopulatePlaces(); + }); + }); + } + + private autoPopulatePlaces() { + if (this.isAutoPopulating) return; // Prevent infinite loops + this.isAutoPopulating = true; + + // Clear places that should be skipped (when previous place has ex-aequo) + for (let i = 1; i < this.places.length; i++) { + if (this.isPlaceSkipped(i)) { + const placePicks = this.picksAt(i); + const currentHorses = placePicks.controls + .map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + + // Clear this place if it has horses (it should be skipped) + if (currentHorses.length > 0) { + while (placePicks.length > 0) { + placePicks.removeAt(0); + } + placePicks.push(this.makePickControl(null)); + } + } + } + + this.isAutoPopulating = false; + } + + private seed() { + if (!this.course) return; + + const placesFA = new FormArray>([]); + const len = this.reqLen(); + + // Extract existing places from resultat if available. + // Backend now returns ordreArrivee as an array of cheval numbers and + // chevauxDeadHeat as the subset that are in dead-heat (ex-aequo). + let existing: number[][] = []; + if (this.resultat && this.resultat.ordreArrivee && this.resultat.ordreArrivee.length > 0) { + const deadHeatSet = new Set(this.resultat.chevauxDeadHeat || []); + + const allHorses = this.resultat.ordreArrivee; + const allInDeadHeat = + allHorses.every((num) => deadHeatSet.has(num)) && allHorses.length === len; + + if (allInDeadHeat) { + // All horses are in first place (ex-aequo) + existing = [allHorses]; // Put all in first place + // Fill remaining places with empty arrays + for (let i = 1; i < len; i++) { + existing.push([]); + } + } else { + // Group consecutive horses that are in dead heat together + const groups: number[][] = []; + let currentGroup: number[] = []; + + this.resultat.ordreArrivee.forEach((num, index) => { + const isInDeadHeat = deadHeatSet.has(num); + const prevNum = index > 0 ? this.resultat!.ordreArrivee[index - 1] : null; + const prevIsInDeadHeat = prevNum !== null && deadHeatSet.has(prevNum); + + if (isInDeadHeat && prevIsInDeadHeat && currentGroup.length > 0) { + // Continue the current dead heat group + currentGroup.push(num); + } else { + // Start a new group + if (currentGroup.length > 0) { + groups.push(currentGroup); + } + currentGroup = [num]; + } + }); + + // Don't forget the last group + if (currentGroup.length > 0) { + groups.push(currentGroup); + } + + // Limit to required places + existing = groups.slice(0, len); + } + } + + // Create form controls for each place + for (let i = 0; i < len; i++) { + const picksFA = new FormArray>([]); + const row = existing[i] ?? []; + if (row.length) { + row.forEach((n) => picksFA.push(this.makePickControl(n))); + } else { + picksFA.push(this.makePickControl(null)); + } + placesFA.push(new FormGroup({ picks: picksFA })); + } + this.form.setControl('places', placesFA); + } + + private makePickControl(initial: number | null) { + const ctrl = new FormControl(initial, [Validators.required]); + + ctrl.valueChanges.subscribe((v) => { + if (typeof v === 'string') { + const n = Number(v); + ctrl.setValue(Number.isNaN(n) ? null : (n as any), { emitEvent: false }); + return; + } + + if (v === null) { + const loc = this.locateControl(ctrl); + if (!loc) return; + const { placeIdx, pickIdx } = loc; + const fa = this.picksAt(placeIdx); + if (fa.length > 1) fa.removeAt(pickIdx); + // Check if we need to clear skipped places after removal + setTimeout(() => this.autoPopulatePlaces(), 0); + return; + } + + const loc = this.locateControl(ctrl); + if (!loc) return; + const { placeIdx, pickIdx } = loc; + const fa = this.picksAt(placeIdx); + const dup = fa.controls.some((c, idx) => idx !== pickIdx && c.value === v); + if (dup) { + ctrl.setValue(null, { emitEvent: false }); + return; + } + + // After value change, check if we need to clear skipped places + setTimeout(() => this.autoPopulatePlaces(), 0); + }); + + return ctrl; + } + + private locateControl( + ctrl: FormControl + ): { placeIdx: number; pickIdx: number } | null { + for (let i = 0; i < this.places.length; i++) { + const fa = this.picksAt(i); + const idx = fa.controls.indexOf(ctrl); + if (idx !== -1) return { placeIdx: i, pickIdx: idx }; + } + return null; + } + + get places(): FormArray> { + return this.form.controls.places; + } + picksAt(i: number): FormArray> { + return this.places.at(i).controls.picks; + } + + private usedElsewhere(exceptPlace: number): Set { + const used = new Set(); + this.places.controls.forEach((pg, idx) => { + if (idx === exceptPlace) return; + pg.controls.picks.controls.forEach((c) => { + if (typeof c.value === 'number') used.add(c.value); + }); + }); + return used; + } + + optionsForPlace(i: number, j: number): number[] { + const all = Array.from({ length: this.maxNum() }, (_, k) => k + 1); + const np = this.npSet(); + + // Count total horses already selected (excluding current control's value) + const allUsed = new Set(); + this.places.controls.forEach((pg) => { + pg.controls.picks.controls.forEach((c) => { + if (typeof c.value === 'number') allUsed.add(c.value); + }); + }); + + // Remove the current control's value from used set (so it can be re-selected) + const currentValue = this.picksAt(i).at(j).value; + if (typeof currentValue === 'number') { + allUsed.delete(currentValue); + } + + // Also remove other horses in the same place (for ex-aequo) + const usedSamePlace = new Set( + this.picksAt(i) + .controls.map((c, idx) => (idx === j ? null : c.value)) + .filter((v): v is number => typeof v === 'number') + ); + + // CRITICAL: Count how many horses are already selected (excluding current control) + const totalSelected = allUsed.size; + const requiredHorses = this.reqLen(); + + // CRITICAL: If we already have exactly reqLen() horses selected (excluding current control), + // block ALL new selections (except keeping current value) + if (totalSelected >= requiredHorses) { + // Only allow keeping the current value if it exists + if (typeof currentValue === 'number') { + return [currentValue]; + } + return []; + } + + // For first place: can have multiple horses (ex-aequo), but not duplicates in same place + // For other places: can only use horses not used anywhere + let opts: number[]; + if (i === 0) { + // First place: can have multiple horses (ex-aequo), but not duplicates in same place + opts = all.filter((n) => !np.has(String(n)) && !usedSamePlace.has(n)); + } else { + // Other places: can only use horses not used anywhere + opts = all.filter((n) => !np.has(String(n)) && !allUsed.has(n) && !usedSamePlace.has(n)); + } + + // Always include current value if it exists (so user can keep their current selection) + if (typeof currentValue === 'number' && !opts.includes(currentValue)) { + opts.push(currentValue); + } + + return opts.sort((a, b) => a - b); + } + + addTie(i: number) { + // Don't allow adding ex-aequo to skipped places + if (this.isPlaceSkipped(i)) return; + + // Check if we can add another horse (ex-aequo) + const totalSelected = this.totalHorsesSelected(); + const requiredHorses = this.reqLen(); + + // Only allow adding ex-aequo if we haven't reached the required number of horses yet + if (totalSelected >= requiredHorses) { + // Already have enough horses - don't allow adding more + return; + } + + this.picksAt(i).push(this.makePickControl(null)); + // Check if we need to clear skipped places after adding + setTimeout(() => this.autoPopulatePlaces(), 0); + } + + removePick(i: number, j: number) { + const fa = this.picksAt(i); + fa.removeAt(j); + if (fa.length === 0) fa.push(this.makePickControl(null)); + } + + private isValidGlobally(): boolean { + const np = this.npSet(); + const max = this.maxNum(); + + // Collect all selected horses (excluding skipped places) + const allHorses: number[] = []; + for (let i = 0; i < this.places.length; i++) { + if (this.isPlaceSkipped(i)) continue; // Skip places that should be skipped + const picks = this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + allHorses.push(...picks); + } + + // Must have exactly reqLen() horses + if (allHorses.length !== this.reqLen()) return false; + + // All horses must be valid (within range, not non-partants, unique) + for (const horse of allHorses) { + if (horse < 1 || horse > max || np.has(String(horse))) return false; + } + if (new Set(allHorses).size !== allHorses.length) return false; + + // First place must have at least one horse + const firstPlaceHorses = this.picksAt(0) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + if (firstPlaceHorses.length === 0) return false; + + // Check that all non-skipped places have valid horses (not just form.valid which includes skipped places) + for (let i = 0; i < this.places.length; i++) { + if (!this.isPlaceSkipped(i)) { + const placeHorses = this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + // Each non-skipped place must have at least one horse + if (placeHorses.length === 0) { + return false; + } + // Check that all controls in this place are valid + const placePicks = this.picksAt(i); + for (const ctrl of placePicks.controls) { + if (ctrl.invalid && ctrl.touched) { + return false; + } + } + } + } + + return true; + } + + canSave(): boolean { + return this.isValidGlobally(); + } + + onSave() { + if (!this.isValidGlobally()) { + this.form.markAllAsTouched(); + return; + } + // Build places array, excluding skipped places + const places: number[][] = []; + for (let i = 0; i < this.places.length; i++) { + if (this.isPlaceSkipped(i)) continue; // Skip places that should be skipped + const placeHorses = this.picksAt(i) + .controls.map((c) => c.value) + .filter((v): v is number => typeof v === 'number'); + places.push(placeHorses); + } + this.save.emit(places); + } +} diff --git a/src/app/shared/forms/reunion-form/reunion-form.css b/src/app/shared/forms/reunion-form/reunion-form.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/forms/reunion-form/reunion-form.html b/src/app/shared/forms/reunion-form/reunion-form.html new file mode 100644 index 0000000..5b54aa2 --- /dev/null +++ b/src/app/shared/forms/reunion-form/reunion-form.html @@ -0,0 +1,99 @@ +
    + + + + + + + + + +
    + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + @if (loadingHippodromes()) { + Chargement des hippodromes... + } @else { @for (h of hippodromes(); track h.id) { + {{ h.nom }} ({{ h.ville }}, {{ h.pays }}) + } } + + + + + + + + + + @for (s of statuts; track s.value) { + {{ s.label }} + } + + + + + + + + + + + + + + @if (showInternalActions()) { +
    + + +
    + } +
    diff --git a/src/app/shared/forms/reunion-form/reunion-form.spec.ts b/src/app/shared/forms/reunion-form/reunion-form.spec.ts new file mode 100644 index 0000000..5717dee --- /dev/null +++ b/src/app/shared/forms/reunion-form/reunion-form.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReunionForm } from './reunion-form'; + +describe('ReunionForm', () => { + let component: ReunionForm; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ReunionForm] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ReunionForm); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/forms/reunion-form/reunion-form.ts b/src/app/shared/forms/reunion-form/reunion-form.ts new file mode 100644 index 0000000..31bdc73 --- /dev/null +++ b/src/app/shared/forms/reunion-form/reunion-form.ts @@ -0,0 +1,310 @@ +import { CommonModule } from '@angular/common'; +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + effect, + input, + OnDestroy, + output, + signal, + OnInit, +} from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { Subscription } from 'rxjs'; + +import { Reunion, ReunionStatut } from 'src/app/core/interfaces/reunion'; +import { Hippodrome } from 'src/app/core/interfaces/hippodrome'; +import { HippodromeService } from 'src/app/core/services/hippodrome'; + +import { ZardButtonComponent } from '@shared/components/button/button.component'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; + +@Component({ + selector: 'app-reunion-form', + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardSelectComponent, + ZardSelectItemComponent, + ZardButtonComponent, + ], + templateUrl: './reunion-form.html', + styleUrls: ['./reunion-form.css'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ReunionForm implements OnInit, AfterViewInit, OnDestroy { + value = input | null>(null); + save = output>(); + cancel = output(); + showInternalActions = input(false); + + submitting = signal(false); + submitted = signal(false); + initializing = signal(false); + loadingHippodromes = signal(false); + + private subs = new Subscription(); + form!: FormGroup; + + hippodromes = signal([]); + selectedHippodromeLabel = signal(''); + statuts = Object.values(ReunionStatut).map((s) => ({ + value: s, + label: + s === ReunionStatut.PLANIFIEE + ? 'Planifiée' + : s === ReunionStatut.EN_COURS + ? 'En cours' + : s === ReunionStatut.TERMINEE + ? 'Terminée' + : 'Annulée', + })); + + constructor( + private fb: FormBuilder, + private hippodromeService: HippodromeService, + private cdr: ChangeDetectorRef + ) { + this.form = this.fb.group({ + code: ['', Validators.required], + nom: ['', Validators.required], + date: ['', Validators.required], + numero: [1, [Validators.required, Validators.min(1)]], + statut: [ReunionStatut.PLANIFIEE, Validators.required], + hippodromeId: new FormControl(null, Validators.required), + totalCourses: new FormControl(null, [Validators.min(1)]), + }); + + // Update label when user selects a different hippodrome (both create and edit modes) + // We always set the label manually to ensure it displays correctly + this.subs.add( + this.form.get('hippodromeId')?.valueChanges.subscribe((hippodromeId) => { + // Skip if we're initializing (to avoid interfering with form initialization) + if (this.initializing()) { + return; + } + + // Always update the label when user selects a hippodrome (both create and edit) + // Update immediately without setTimeout for better responsiveness + if (hippodromeId && this.hippodromes().length > 0) { + // Try both string and direct comparison since form control might convert types + const matchingHippodrome = this.hippodromes().find( + (h) => String(h.id) === String(hippodromeId) || h.id === hippodromeId + ); + if (matchingHippodrome) { + const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`; + this.selectedHippodromeLabel.set(hippodromeLabel); + // Force change detection to ensure the label is displayed immediately + this.cdr.markForCheck(); + } else { + this.selectedHippodromeLabel.set(''); + } + } else { + this.selectedHippodromeLabel.set(''); + } + }) || new Subscription() + ); + + // When editing - watch both value and hippodromes to handle timing + effect(() => { + const v = this.value(); + const hippodromesList = this.hippodromes(); + const isLoading = this.loadingHippodromes(); + + // Enable/disable hippodrome control based on loading state + const hippodromeControl = this.form.get('hippodromeId'); + if (isLoading) { + hippodromeControl?.disable({ emitEvent: false }); + } else { + hippodromeControl?.enable({ emitEvent: false }); + } + + // Only reset if we have a new value (not just hippodromes loading) + if (v !== null && v !== undefined) { + this.initializing.set(true); + + // Get hippodromeId - convert to string if needed + let hippodromeId: string | null = null; + let hippodromeLabel: string = ''; + if (v.hippodrome?.id) { + hippodromeId = String(v.hippodrome.id); + // Only set hippodromeId if hippodromes are loaded and the ID exists + // This ensures the select component can find the matching label + if (hippodromesList.length > 0) { + const matchingHippodrome = hippodromesList.find((h) => String(h.id) === hippodromeId); + if (matchingHippodrome) { + // Set the label manually so the select component displays it + hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`; + this.selectedHippodromeLabel.set(hippodromeLabel); + } else { + hippodromeId = null; // Don't set if not found + this.selectedHippodromeLabel.set(''); + } + } else { + // If hippodromes aren't loaded yet, use the hippodrome from the value if available + if (v.hippodrome.nom) { + hippodromeLabel = `${v.hippodrome.nom} (${v.hippodrome.ville}, ${v.hippodrome.pays})`; + this.selectedHippodromeLabel.set(hippodromeLabel); + } + // Don't set the value yet - it will be set once hippodromes finish loading + hippodromeId = null; + } + } else { + this.selectedHippodromeLabel.set(''); + } + + this.form.patchValue( + { + code: v.code ?? '', + nom: v.nom ?? '', + date: v.date ?? '', + numero: v.numero ?? 1, + statut: v.statut ?? ReunionStatut.PLANIFIEE, + hippodromeId, + totalCourses: v.totalCourses ?? null, + }, + { emitEvent: false } + ); + + this.form.markAsPristine(); + this.form.markAsUntouched(); + queueMicrotask(() => this.initializing.set(false)); + } else if (v === null) { + // Reset form when value is cleared + this.initializing.set(true); + this.selectedHippodromeLabel.set(''); + this.form.reset( + { + code: '', + nom: '', + date: '', + numero: 1, + statut: ReunionStatut.PLANIFIEE, + hippodromeId: null, + totalCourses: null, + }, + { emitEvent: false } + ); + this.form.markAsPristine(); + this.form.markAsUntouched(); + queueMicrotask(() => this.initializing.set(false)); + } else if (hippodromesList.length > 0 && !isLoading) { + // Hippodromes just finished loading - re-apply value if we have one + const currentValue = this.value(); + if (currentValue?.hippodrome?.id) { + const hippodromeId = String(currentValue.hippodrome.id); + const matchingHippodrome = hippodromesList.find((h) => String(h.id) === hippodromeId); + if (matchingHippodrome) { + // Set the label manually + const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`; + this.selectedHippodromeLabel.set(hippodromeLabel); + // Wait for Angular to render the select items + setTimeout(() => { + this.form.patchValue({ hippodromeId }, { emitEvent: false }); + this.cdr.detectChanges(); + }, 50); + } + } + } + }); + } + + ngOnInit() { + // Fetch hippodromes from API + this.loadingHippodromes.set(true); + this.subs.add( + this.hippodromeService.list({ page: 1, perPage: 1000 }, false).subscribe({ + next: (result) => { + this.hippodromes.set(result.data); + this.loadingHippodromes.set(false); + // The effect will automatically re-run when hippodromes signal updates + }, + error: (err) => { + console.error('Error loading hippodromes:', err); + this.loadingHippodromes.set(false); + }, + }) + ); + } + + ngAfterViewInit() { + // After view is initialized, ensure the hippodrome value and label are set correctly + const currentValue = this.value(); + if (currentValue?.hippodrome?.id && this.hippodromes().length > 0) { + const hippodromeId = String(currentValue.hippodrome.id); + const matchingHippodrome = this.hippodromes().find((h) => String(h.id) === hippodromeId); + if (matchingHippodrome) { + // Set the label manually + const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`; + this.selectedHippodromeLabel.set(hippodromeLabel); + // Wait a bit for the select items to be fully rendered + setTimeout(() => { + const currentFormValue = this.form.get('hippodromeId')?.value; + if (String(currentFormValue) !== hippodromeId) { + this.form.patchValue({ hippodromeId }, { emitEvent: false }); + this.cdr.detectChanges(); + } + }, 100); + } + } + } + + onHippodromeSelectionChange(value: string) { + // Immediately update the label when user selects a hippodrome + if (value && this.hippodromes().length > 0) { + const matchingHippodrome = this.hippodromes().find( + (h) => String(h.id) === String(value) || h.id === value + ); + if (matchingHippodrome) { + const hippodromeLabel = `${matchingHippodrome.nom} (${matchingHippodrome.ville}, ${matchingHippodrome.pays})`; + this.selectedHippodromeLabel.set(hippodromeLabel); + this.cdr.markForCheck(); + } + } + } + + invalid(name: string): boolean { + const c = this.form.get(name); + return !!((this.submitted() || c?.touched || c?.dirty) && c?.invalid); + } + + onSubmit() { + this.submitted.set(true); + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + this.submitting.set(true); + + const values = this.form.value; + const hippodrome = this.hippodromes().find((h) => h.id === values.hippodromeId) ?? null; + + this.save.emit({ + ...values, + hippodrome, + } as Partial); + + // Reset form state after emitting (parent will handle actual form reset via value input) + // But reset submitting state so button is enabled again + this.submitting.set(false); + this.submitted.set(false); + } + + ngOnDestroy() { + this.subs.unsubscribe(); + } +} diff --git a/src/app/shared/forms/role-form/role-form.html b/src/app/shared/forms/role-form/role-form.html new file mode 100644 index 0000000..cb053df --- /dev/null +++ b/src/app/shared/forms/role-form/role-form.html @@ -0,0 +1,36 @@ +
    +
    + + +
    + +
    +
    + + +
    + +
    +
    +
    + +
    +
    Permissions
    +
    +
    + @for (p of permissions; track p.id) { + + } +
    +
    + @if (submitted && selectedPermissionsCount() === 0) { +

    Sélectionnez au moins une permission.

    + } +
    +
    diff --git a/src/app/shared/forms/role-form/role-form.ts b/src/app/shared/forms/role-form/role-form.ts new file mode 100644 index 0000000..f3825d2 --- /dev/null +++ b/src/app/shared/forms/role-form/role-form.ts @@ -0,0 +1,120 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { FormsModule } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardCheckboxComponent } from '@shared/components/checkbox/checkbox.component'; +import { Permission, Role } from 'src/app/core/interfaces/role'; + +@Component({ + selector: 'app-role-form', + standalone: true, + templateUrl: './role-form.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + ZardFormModule, + ZardInputDirective, + ZardCheckboxComponent, + ], +}) +export class RoleForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + private _permissions: Permission[] = []; + @Input() set permissions(list: Permission[]) { + this._permissions = list ?? []; + this.buildPermissionControls(); + } + get permissions(): Permission[] { + return this._permissions; + } + + private _value?: Role; + @Input() set value(v: Role | undefined) { + this._value = v; + this.hydrateFromValue(v); + } + get value(): Role | undefined { + return this._value; + } + + form: FormGroup; + submitted = false; + + constructor(private fb: FormBuilder) { + this.form = this.fb.group({ + name: ['', Validators.required], + description: [''], + permMap: this.fb.group({}), + }); + } + + private buildPermissionControls() { + const group = this.form.get('permMap') as FormGroup; + if (!group) return; + // Keep existing states where possible + const current = (group.getRawValue?.() as Record) || {}; + const selectedNames = new Set( + (this._value?.permissions ?? []).map((p) => p.name) + ); + // Rebuild controls for each permission (keyed by its name) + const newGroup: Record = {}; + for (const p of this._permissions) { + const key = p.name; + const initial = current[key] ?? selectedNames.has(key); + newGroup[key] = this.fb.control(!!initial); + } + // Replace the group + this.form.setControl('permMap', this.fb.group(newGroup)); + } + + private hydrateFromValue(v?: Role) { + this.form.patchValue({ name: v?.name ?? '', description: v?.description ?? '' }); + // Rebuild permission map based on new value and available permissions + this.buildPermissionControls(); + } + + onSubmit() { + this.submitted = true; + if (this.form.invalid || this.selectedPermissionsCount() === 0) { + this.form.markAllAsTouched(); + return; + } + const raw = this.form.getRawValue() as any; + const permMap: Record = (this.form.get('permMap') as FormGroup).getRawValue(); + const selectedNames = Object.entries(permMap) + .filter(([, v]) => !!v) + .map(([k]) => k); + const permissions = this._permissions.filter((p) => selectedNames.includes(p.name)); + const payload: Role = { + id: this.value?.id ?? '', + name: raw.name, + description: raw.description, + permissions, + }; + this.save.emit(payload); + } + + selectedPermissionsCount(): number { + const permMap: Record = (this.form.get('permMap') as FormGroup).getRawValue(); + return Object.values(permMap).filter(Boolean).length; + } + + isInvalid(control: string): boolean { + const ctrl = this.form.get(control); + return !!(ctrl && ctrl.invalid && (ctrl.touched || this.submitted)); + } + + errorMessage(control: string): string | null { + const c = this.form.get(control); + if (!c || !c.errors) return null; + if (c.errors['required']) return 'Ce champ est obligatoire'; + if (c.errors['minlength']) return `Minimum ${c.errors['minlength'].requiredLength} caractères`; + return null; + } +} diff --git a/src/app/shared/forms/tpe-form/tpe-form.html b/src/app/shared/forms/tpe-form/tpe-form.html new file mode 100644 index 0000000..993e779 --- /dev/null +++ b/src/app/shared/forms/tpe-form/tpe-form.html @@ -0,0 +1,42 @@ +
    +
    + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + + @for (t of types; track t.value) { + {{ t.label }} + } + +
    +
    + + + +
    + +
    +
    + + + +
    + +
    +
    +
    +
    diff --git a/src/app/shared/forms/tpe-form/tpe-form.ts b/src/app/shared/forms/tpe-form/tpe-form.ts new file mode 100644 index 0000000..52d968b --- /dev/null +++ b/src/app/shared/forms/tpe-form/tpe-form.ts @@ -0,0 +1,125 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { TpeDevice, TpeType } from 'src/app/core/interfaces/tpe'; + +@Component({ + selector: 'app-tpe-form', + standalone: true, + templateUrl: './tpe-form.html', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardSelectComponent, + ZardSelectItemComponent, + ], +}) +export class TpeForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + private _value?: TpeDevice; + private _skipHydration = false; + @Input() set value(v: TpeDevice | undefined) { + this._value = v; + if (!this._skipHydration) { + this.hydrateFromValue(v); + } + } + get value(): TpeDevice | undefined { + return this._value; + } + + form: FormGroup; + submitted = false; + + constructor(private fb: FormBuilder) { + this.form = this.fb.group({ + imei: ['', [Validators.required, Validators.minLength(10)]], + serial: ['', Validators.required], + type: ['POS' as TpeType, Validators.required], + marque: ['', Validators.required], + modele: ['', Validators.required], + }); + } + + isInvalid(control: string): boolean { + const c = this.form.get(control); + return !!(c && c.invalid && (c.touched || this.submitted)); + } + errorMessage(control: string): string { + const e = this.form.get(control)?.errors; + if (!e) return ''; + if (e['required']) return 'Ce champ est obligatoire'; + if (e['minlength']) return `Minimum ${e['minlength'].requiredLength} caractères`; + return ''; + } + + private hydrateFromValue(v?: TpeDevice) { + if (!v) { + this.form.reset({ + imei: '', + serial: '', + type: 'POS', + marque: '', + modele: '', + }); + this.submitted = false; // Reset submitted flag when form is cleared + return; + } + this.form.reset({ + imei: v.imei, + serial: v.serial, + type: v.type, + marque: v.marque, + modele: v.modele, + }); + } + + onSubmit() { + this.submitted = true; + if (this.form.invalid) { + this.form.markAllAsTouched(); + return; + } + const raw = this.form.getRawValue() as any; + const payload: Partial = { + imei: raw.imei, + serial: raw.serial, + type: raw.type, + marque: raw.marque, + modele: raw.modele, + }; + // Preserve existing id, statut, and assigne if editing + if (this.value?.id) { + payload.id = this.value.id; + } + this.save.emit(payload as TpeDevice); + } + + resetForm() { + this._skipHydration = true; // Prevent hydration when clearing value + this._value = undefined; + this.form.reset({ + imei: '', + serial: '', + type: 'POS', + marque: '', + modele: '', + }); + this.submitted = false; + this._skipHydration = false; // Re-enable hydration + } + + types = [ + { label: 'POS', value: 'POS' as TpeType }, + { label: 'Autre', value: 'OTHER' as TpeType }, + ]; +} diff --git a/src/app/shared/forms/user-form/user-form.css b/src/app/shared/forms/user-form/user-form.css new file mode 100644 index 0000000..e268024 --- /dev/null +++ b/src/app/shared/forms/user-form/user-form.css @@ -0,0 +1,3 @@ +/* Empty file: avoid Tailwind @apply in component CSS to prevent build errors */ + + diff --git a/src/app/shared/forms/user-form/user-form.html b/src/app/shared/forms/user-form/user-form.html new file mode 100644 index 0000000..ba99c37 --- /dev/null +++ b/src/app/shared/forms/user-form/user-form.html @@ -0,0 +1,96 @@ +
    +
    + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + + @for (r of roles; track r.id) { + {{ r.name }} + } + +
    +
    + + + + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + +
    +
    + + + +
    + + Active + Cancelled + Suspended + +
    +
    +
    +
    diff --git a/src/app/shared/forms/user-form/user-form.ts b/src/app/shared/forms/user-form/user-form.ts new file mode 100644 index 0000000..87b2f8d --- /dev/null +++ b/src/app/shared/forms/user-form/user-form.ts @@ -0,0 +1,148 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ZardFormModule } from '@shared/components/form/form.module'; +import { ZardInputDirective } from '@shared/components/input/input.directive'; +import { ZardSelectItemComponent } from '@shared/components/select/select-item.component'; +import { ZardSelectComponent } from '@shared/components/select/select.component'; +import { User, UserStatus } from 'src/app/core/interfaces/user'; +import { Role } from 'src/app/core/interfaces/role'; +import { RoleService } from 'src/app/core/services/role'; + +@Component({ + selector: 'app-user-form', + standalone: true, + templateUrl: './user-form.html', + styleUrls: ['./user-form.css'], + imports: [ + CommonModule, + ReactiveFormsModule, + ZardFormModule, + ZardInputDirective, + ZardSelectComponent, + ZardSelectItemComponent, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UserForm { + @Output() save = new EventEmitter(); + @Output() cancel = new EventEmitter(); + + private _value?: User; + @Input() set value(v: User | undefined) { + this._value = v; + this.hydrateFromValue(v); + } + get value(): User | undefined { + return this._value; + } + + form: FormGroup; + submitted = false; + + roles: Role[] = []; + + constructor(private fb: FormBuilder, private roleService: RoleService) { + this.form = this.fb.group({ + nom: ['', Validators.required], + prenom: ['', Validators.required], + identifiant: ['', Validators.required], + password: ['', [Validators.minLength(8)]], + matriculeAgent: ['', Validators.required], + roleId: ['', Validators.required], + restrictionConnexion: [false], + restrictionAutomatique: [false], + nombreIpAutorise: [0, [Validators.required, Validators.min(0)]], + nombreIpAutoAutorise: [0, [Validators.required, Validators.min(0)]], + statut: ['ACTIVE' as UserStatus, Validators.required], + }); + + this.roleService.allPermissions(); // warm permissions cache if needed + // Fetch roles by listing and mapping unique names; use mock store if needed + // Reuse RoleService through list with default params + this.roleService + .list({ page: 1, perPage: 100, search: '', sortKey: 'name', sortDir: 'asc' } as any) + .subscribe((res) => { + this.roles = res.data as any; + }); + } + + isInvalid(control: string): boolean { + const ctrl = this.form.get(control); + return !!(ctrl && ctrl.invalid && (ctrl.touched || this.submitted)); + } + + errorMessage(control: string): string | null { + const c = this.form.get(control); + if (!c || !c.errors) return null; + if (c.errors['required']) return 'Ce champ est obligatoire'; + if (c.errors['min']) return `Valeur minimale : ${c.errors['min'].min}`; + if (c.errors['minlength']) return `Minimum ${c.errors['minlength'].requiredLength} caractères`; + return null; + } + + private hydrateFromValue(v?: User) { + if (v) { + this.form.reset({ + nom: v.nom, + prenom: v.prenom, + identifiant: v.identifiant, + password: '', + matriculeAgent: v.matriculeAgent, + // Utiliser roleId directement si le rôle n'est pas hydraté + roleId: v.role?.id ?? v.roleId ?? '', + restrictionConnexion: v.restrictionConnexion, + restrictionAutomatique: v.restrictionAutomatique, + nombreIpAutorise: v.nombreIpAutorise, + nombreIpAutoAutorise: v.nombreIpAutoAutorise, + statut: v.statut, + }); + } else { + this.form.reset({ + nom: '', + prenom: '', + identifiant: '', + password: '', + matriculeAgent: '', + roleId: '', + restrictionConnexion: false, + restrictionAutomatique: false, + nombreIpAutorise: 0, + nombreIpAutoAutorise: 0, + statut: 'ACTIVE' as UserStatus, + }); + } + } + + onSubmit() { + this.submitted = true; + if (!this.form.valid) { + this.form.markAllAsTouched(); + return; + } + const raw = this.form.getRawValue() as any; + const role = this.roles.find((r) => r.id === raw.roleId); + if (!role) { + return; + } + const payload: User = { + ...(this.value ?? {}), + id: this.value?.id ?? '', + nom: raw.nom, + prenom: raw.prenom, + identifiant: raw.identifiant, + // Only send password if filled (for create or change) + password: raw.password ? String(raw.password) : undefined, + matriculeAgent: raw.matriculeAgent, + roleId: role.id, + role, + restrictionConnexion: !!raw.restrictionConnexion, + restrictionAutomatique: !!raw.restrictionAutomatique, + nombreIpAutorise: Number(raw.nombreIpAutorise), + nombreIpAutoAutorise: Number(raw.nombreIpAutoAutorise), + statut: raw.statut as UserStatus, + updatedAt: new Date().toISOString(), + }; + this.save.emit(payload); + } +} diff --git a/src/app/shared/paging/data-source.ts b/src/app/shared/paging/data-source.ts new file mode 100644 index 0000000..2e5421d --- /dev/null +++ b/src/app/shared/paging/data-source.ts @@ -0,0 +1,28 @@ +import { signal, computed } from '@angular/core'; +import { ListParams, SortDir } from './paging'; + +export class TableDataSource { + readonly page = signal(1); + readonly perPage = signal(10); + readonly search = signal(''); + readonly sortKey = signal(undefined); + readonly sortDir = signal(''); // '', 'asc', 'desc' + + readonly params = computed(() => ({ + page: this.page(), + perPage: this.perPage(), + search: this.search() || undefined, + sortKey: this.sortKey(), + sortDir: this.sortDir(), + })); + + resetToFirst() { + this.page.set(1); + } + + setSort(key?: string, dir: SortDir = '') { + this.sortKey.set(key); + this.sortDir.set(dir); + this.resetToFirst(); + } +} diff --git a/src/app/shared/paging/normalize-page.ts b/src/app/shared/paging/normalize-page.ts new file mode 100644 index 0000000..f6fa39f --- /dev/null +++ b/src/app/shared/paging/normalize-page.ts @@ -0,0 +1,75 @@ +import { PagedResult, PageMeta } from './paging'; + +/** + * Normalizes various pagination response formats into a unified PagedResult. + * Automatically merges any extra statistical fields inside `meta`. + */ +export function normalizePage(raw: any, reqPage: number, perPage: number): PagedResult { + // 🟩 Case 1 — Spring Data style: { content, totalElements, number, size } + if (raw && Array.isArray(raw.content) && typeof raw.totalElements === 'number') { + return { + data: raw.content as T[], + meta: { + page: (raw.number ?? 0) + 1, + perPage: raw.size ?? perPage, + total: raw.totalElements, + ...(raw.meta ?? {}), // merge extra meta if provided + } as PageMeta, + }; + } + + // 🟩 Case 2 — API or local mock: { data, meta: { total, ...extra } } + if (raw && Array.isArray(raw.data) && raw.meta?.total != null) { + return { + data: raw.data as T[], + meta: { + page: raw.meta.page ?? reqPage, + perPage: raw.meta.perPage ?? perPage, + total: raw.meta.total, + ...raw.meta, // keep any custom stats + } as PageMeta, + }; + } + + // 🟩 Case 3 — Generic REST: { items, total | total_count, ...extra } + if (raw && Array.isArray(raw.items) && (raw.total != null || raw.total_count != null)) { + const total = raw.total ?? raw.total_count; + return { + data: raw.items as T[], + meta: { + page: reqPage, + perPage, + total, + ...raw.meta, // optional + } as PageMeta, + }; + } + + // 🟩 Case 4 — Direct array (no meta) + if (Array.isArray(raw)) { + const start = (reqPage - 1) * perPage; + const data = (raw as T[]).slice(start, start + perPage); + return { + data, + meta: { + page: reqPage, + perPage, + total: (raw as T[]).length, + } as PageMeta, + }; + } + + // 🟩 Fallback — ensure consistency even with minimal info + const data = Array.isArray(raw?.data) ? (raw.data as T[]) : []; + const total = typeof raw?.total === 'number' ? raw.total : data.length ?? 0; + + return { + data, + meta: { + page: raw?.meta?.page ?? reqPage, + perPage: raw?.meta?.perPage ?? perPage, + total, + ...raw?.meta, // merge additional stats safely + } as PageMeta, + }; +} diff --git a/src/app/shared/paging/paginated-http.service.ts b/src/app/shared/paging/paginated-http.service.ts new file mode 100644 index 0000000..9ddb6e0 --- /dev/null +++ b/src/app/shared/paging/paginated-http.service.ts @@ -0,0 +1,61 @@ +import { HttpClient, HttpParams } from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { BackendConfig, ListParams, PagedResult } from './paging'; +import { normalizePage } from './normalize-page'; +import { environment } from 'src/environments/environment.development'; + +const springDefaults: BackendConfig = { + pageParam: 'page', + sizeParam: 'size', + searchParam: 'q', + zeroBasedPageIndex: true, + buildSort: (key, dir) => (key && dir ? ['sort', `${key},${dir}`] : null), + mapClientSortKey: (k) => k, +}; + +@Injectable({ providedIn: 'root' }) +export class PaginatedHttpService { + private http = inject(HttpClient); + + // Helper method to get ngrok bypass headers + private getNgrokHeaders(): Record { + const isNgrok = environment.apiBaseUrl.includes('ngrok-free.app') || environment.apiBaseUrl.includes('ngrok.io') || environment.apiBaseUrl.includes('ngrok'); + return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {}; + } + + fetch( + url: string, + params: ListParams, + cfg: BackendConfig = springDefaults + ): Observable> { + const { + pageParam = 'page', + sizeParam = 'size', + searchParam = 'q', + zeroBasedPageIndex = true, + buildSort = springDefaults.buildSort!, + mapClientSortKey = springDefaults.mapClientSortKey!, + } = cfg; + + let httpParams = new HttpParams() + .set(pageParam, String(zeroBasedPageIndex ? Math.max(params.page - 1, 0) : params.page)) + .set(sizeParam, String(params.perPage)); + + if (params.search) httpParams = httpParams.set(searchParam, params.search); + + const apiSortKey = mapClientSortKey(params.sortKey); + const sortPair = buildSort(apiSortKey, params.sortDir); + if (sortPair) httpParams = httpParams.set(sortPair[0], sortPair[1]); + + if (params.extra) { + for (const [k, v] of Object.entries(params.extra)) { + if (v !== undefined && v !== null && v !== '') httpParams = httpParams.set(k, String(v)); + } + } + + return this.http + .get(url, { params: httpParams, headers: this.getNgrokHeaders() }) + .pipe(map((raw) => normalizePage(raw, params.page, params.perPage))); + } +} diff --git a/src/app/shared/paging/paging.ts b/src/app/shared/paging/paging.ts new file mode 100644 index 0000000..3ddf760 --- /dev/null +++ b/src/app/shared/paging/paging.ts @@ -0,0 +1,32 @@ +// src/app/shared/paging/paging.models.ts +export type SortDir = 'asc' | 'desc' | ''; + +export interface ListParams { + page: number; // 1-based for UI + perPage: number; + search?: string; + sortKey?: string; + sortDir?: SortDir; + extra?: Record; +} + +export interface PageMeta { + page: number; // 1-based + perPage: number; + total: number; // -1 if unknown + [key: string]: any; +} + +export interface PagedResult { + data: T[]; + meta: PageMeta; +} + +export interface BackendConfig { + pageParam?: string; // default 'page' (Spring is 0-based) + sizeParam?: string; // default 'size' + searchParam?: string; // default 'q' + zeroBasedPageIndex?: boolean; // default true (Spring) + buildSort?: (key?: string, dir?: SortDir) => [param: string, value: string] | null; + mapClientSortKey?: (key?: string) => string | undefined; +} diff --git a/src/app/shared/shared-module.ts b/src/app/shared/shared-module.ts new file mode 100644 index 0000000..3da0323 --- /dev/null +++ b/src/app/shared/shared-module.ts @@ -0,0 +1,8 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + declarations: [], + imports: [CommonModule], +}) +export class SharedModule {} diff --git a/src/app/shared/utils/cn.ts b/src/app/shared/utils/cn.ts new file mode 100644 index 0000000..a7dc3a1 --- /dev/null +++ b/src/app/shared/utils/cn.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} \ No newline at end of file diff --git a/src/app/shared/utils/merge-classes.ts b/src/app/shared/utils/merge-classes.ts new file mode 100644 index 0000000..6e79043 --- /dev/null +++ b/src/app/shared/utils/merge-classes.ts @@ -0,0 +1,17 @@ +import { twMerge } from 'tailwind-merge'; +import { ClassValue, clsx } from 'clsx'; + +export type { ClassValue }; + +export function mergeClasses(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export function transform(value: boolean | string): boolean { + return typeof value === 'string' ? value === '' : value; +} + +export function generateId(prefix = ''): string { + const id = crypto.randomUUID(); + return prefix ? `${prefix}-${id}` : id; +} \ No newline at end of file diff --git a/src/app/shared/utils/number.ts b/src/app/shared/utils/number.ts new file mode 100644 index 0000000..5138542 --- /dev/null +++ b/src/app/shared/utils/number.ts @@ -0,0 +1,13 @@ +function clamp(value: number, [min, max]: [number, number]): number { + return Math.min(max, Math.max(min, value)); +} + +function roundToStep(value: number, min: number, step: number): number { + return Math.round((value - min) / step) * step + min; +} + +function convertValueToPercentage(value: number, min: number, max: number): number { + return ((value - min) / (max - min)) * 100; +} + +export { clamp, roundToStep, convertValueToPercentage }; \ No newline at end of file diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts new file mode 100644 index 0000000..5bd797b --- /dev/null +++ b/src/environments/environment.development.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + apiBaseUrl: 'https://custody-holding-rogers-less.trycloudflare.com', +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..5bd797b --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,4 @@ +export const environment = { + production: false, + apiBaseUrl: 'https://custody-holding-rogers-less.trycloudflare.com', +}; diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..4e6de3f --- /dev/null +++ b/src/index.html @@ -0,0 +1,28 @@ + + + + + Plateforme de Jeux PMU (PJP) + + + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..33107b4 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; +import '@angular/common/locales/global/fr'; + +bootstrapApplication(App, appConfig).catch((err) => console.error(err)); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..a572670 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,253 @@ +@import 'tailwindcss'; +@import 'lucide-static/font/lucide.css'; +@plugin "tailwindcss-animate"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); + + --surface: #faf9f7; + --text: #1a1a1a; + --heading: #0c1a0e; +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); + + /* --color-surface: #0f1a12; + --color-text: #e6f0e9; + --color-heading: #f1f7f3; */ + --surface: #0f1a12; + --text: #e6f0e9; + --heading: #f1f7f3; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + + --color-surface: var(--surface); + --color-text: var(--text); + --color-heading: var(--heading); + + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + + /* PMU brand tokens */ + --color-pmu-vert: #1c5a29; /* Primary */ + --color-pmu-jaune: #fae500; /* Accent */ + --color-pmu-rouge: #c31617; /* Danger */ + + /* --color-surface: #faf9f7; + --color-text: #1a1a1a; + --color-heading: #0c1a0e; */ + + /* sizes/radii */ + --radius-xl: 1rem; +} + +/* utilities bound to CSS variables (Tailwind v4 works config-less) */ +html, +body, +app-root { + height: 100%; +} + +/* Keyframes */ +@keyframes pmu-float { + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } + 100% { + transform: translateY(0); + } +} + +/* Utility: animate-float */ +@utility animate-float { + animation: pmu-float 6s ease-in-out infinite; +} + +/* Variants (optional) */ +@utility animate-float-slow { + animation-duration: 10s; +} +@utility animate-float-fast { + animation-duration: 3s; +} +@utility animate-float-sm { + animation-name: pmu-float-sm; +} +@utility animate-float-lg { + animation-name: pmu-float-lg; +} + +/* Small & Large amplitudes */ +@keyframes pmu-float-sm { + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(-6px); + } + 100% { + transform: translateY(0); + } +} +@keyframes pmu-float-lg { + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(-16px); + } + 100% { + transform: translateY(0); + } +} + +/* Respect reduced motion */ +@media (prefers-reduced-motion: reduce) { + .animate-float, + .animate-float-slow, + .animate-float-fast, + .animate-float-sm, + .animate-float-lg { + animation: none !important; + } +} + +/* === Keyframes for glowing effect === */ +@keyframes pmu-glow { + 0%, + 100% { + box-shadow: 0 0 4px rgba(250, 229, 0, 0.4), /* yellow */ 0 0 8px rgba(28, 90, 41, 0.3), + /* green */ 0 0 12px rgba(195, 22, 23, 0.2); /* red */ + } + 50% { + box-shadow: 0 0 8px rgba(250, 229, 0, 0.7), 0 0 16px rgba(28, 90, 41, 0.5), + 0 0 24px rgba(195, 22, 23, 0.4); + } +} + +/* === Utility class === */ +@utility animate-glow { + animation: pmu-glow 3s ease-in-out infinite alternate; +} + +/* === Optional variants === */ +@utility animate-glow-slow { + animation-duration: 6s; +} +@utility animate-glow-fast { + animation-duration: 1.5s; +} +@utility animate-glow-strong { + filter: brightness(1.2); +} + +/* === Respect reduced motion === */ +@media (prefers-reduced-motion: reduce) { + .animate-glow, + .animate-glow-slow, + .animate-glow-fast { + animation: none !important; + } +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..264f459 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..da57124 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,40 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve", + "baseUrl": "./", + "paths": { + "@shared/*": [ + "src/app/shared/*" + ] + } + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} \ No newline at end of file diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..04df34c --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.ts" + ] +}