@rashidee/co2 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +25449 -0
- package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
- package/drizzle/0001_wise_wolf_cub.sql +13 -0
- package/drizzle/0002_secret_spyke.sql +16 -0
- package/drizzle/0003_workspace_config_singleton.sql +16 -0
- package/drizzle/0004_terminal_claim_v120.sql +11 -0
- package/drizzle/0005_brainstorm_v124.sql +10 -0
- package/drizzle/0006_project_v140.sql +6 -0
- package/drizzle/0007_prd_wizard_v124.sql +14 -0
- package/drizzle/0008_workflow_artifact_v126.sql +24 -0
- package/drizzle/0009_project_import_v150.sql +7 -0
- package/drizzle/0010_quality_v151.sql +3 -0
- package/drizzle/meta/0000_snapshot.json +1335 -0
- package/drizzle/meta/0001_snapshot.json +1416 -0
- package/drizzle/meta/0002_snapshot.json +1510 -0
- package/drizzle/meta/_journal.json +83 -0
- package/package.json +41 -0
- package/plugin/.claude-plugin/marketplace.json +36 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/README.md +625 -0
- package/plugin/SKILLS.md +350 -0
- package/plugin/co2.svg +5 -0
- package/plugin/commands/brainstorm-loop.md +129 -0
- package/plugin/skills/conductor-defect/SKILL.md +898 -0
- package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
- package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
- package/plugin/skills/depgen-k8s/SKILL.md +468 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
- package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
- package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
- package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
- package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
- package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
- package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
- package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
- package/plugin/skills/modelgen-relational/SKILL.md +707 -0
- package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
- package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
- package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
- package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
- package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
- package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
- package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
- package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
- package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
- package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
- package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
- package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
- package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
- package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
- package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
- package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
- package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
- package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
- package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
- package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
- package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
- package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
- package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
- package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
- package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
- package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
- package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
- package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
- package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
- package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
- package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
- package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
- package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
- package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
- package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
- package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
- package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
- package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
- package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
- package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
- package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
- package/plugin/skills/testgen-functional/SKILL.md +1381 -0
- package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
- package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
- package/plugin/skills/util-projectinit/SKILL.md +307 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
- package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
- package/plugin/skills/util-projectsync/SKILL.md +501 -0
- package/plugin/skills/util-updprd/SKILL.md +324 -0
- package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
- package/plugin/skills/util-ustagger/SKILL.md +154 -0
- package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
- package/static/assets/arc-DRKw7sjs.js +1 -0
- package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
- package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
- package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
- package/static/assets/channel-C2nVbuwv.js +1 -0
- package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
- package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
- package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
- package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
- package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
- package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
- package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
- package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
- package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
- package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
- package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
- package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
- package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
- package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
- package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
- package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
- package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
- package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
- package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
- package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
- package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
- package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
- package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
- package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
- package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
- package/static/assets/graph-C9eacEi8.js +1 -0
- package/static/assets/index-LZSQkLE2.css +41 -0
- package/static/assets/index-y7XDSgMG.js +495 -0
- package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
- package/static/assets/init-Gi6I4Gst.js +1 -0
- package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
- package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
- package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
- package/static/assets/katex-C5jXJg4s.js +257 -0
- package/static/assets/layout-DEXfKzaS.js +1 -0
- package/static/assets/linear-CLO--ryk.js +1 -0
- package/static/assets/map-Czzmt4hB.js +1 -0
- package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
- package/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
- package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
- package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
- package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
- package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
- package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
- package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
- package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
- package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
- package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
- package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
- package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
- package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
- package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
- package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
- package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
- package/static/index.html +24 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# Routing Patterns — go_router 14 + Feature-Based Architecture
|
|
2
|
+
|
|
3
|
+
This reference describes the go_router conventions and feature-based directory
|
|
4
|
+
structure used in the spec. Include this content in Sections 4 and 7 of the generated
|
|
5
|
+
specification.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Feature-Based Directory Structure
|
|
10
|
+
|
|
11
|
+
Each PRD module maps to a `lib/features/<module>/` folder. This co-locates everything
|
|
12
|
+
related to a feature (model, data, application, presentation) so the coding agent can
|
|
13
|
+
implement one feature folder at a time.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
lib/
|
|
17
|
+
├── main.dart # App entry — Firebase, Hive, Riverpod scope
|
|
18
|
+
├── app.dart # MaterialApp.router + theme + locale
|
|
19
|
+
├── firebase_options.dart # Generated by `flutterfire configure`
|
|
20
|
+
├── router/
|
|
21
|
+
│ └── app_router.dart # go_router configuration + guards
|
|
22
|
+
├── core/
|
|
23
|
+
│ ├── api/
|
|
24
|
+
│ │ ├── dio_provider.dart # Dio + interceptors
|
|
25
|
+
│ │ ├── api_failure.dart # Sealed failure type
|
|
26
|
+
│ │ ├── auth_interceptor.dart # Bearer token injector
|
|
27
|
+
│ │ └── paginated_response.dart # Generic page envelope
|
|
28
|
+
│ ├── auth/
|
|
29
|
+
│ │ ├── auth_repository.dart # signIn/signOut/refresh
|
|
30
|
+
│ │ ├── auth_provider.dart # Riverpod auth state
|
|
31
|
+
│ │ ├── auth_state.dart # @freezed AuthState
|
|
32
|
+
│ │ └── roles.dart # Role constants
|
|
33
|
+
│ ├── local_storage/
|
|
34
|
+
│ │ ├── hive_setup.dart # Hive.initFlutter + adapters
|
|
35
|
+
│ │ ├── secure_storage_provider.dart # flutter_secure_storage
|
|
36
|
+
│ │ └── box_provider.dart # Typed Box providers
|
|
37
|
+
│ ├── notification/
|
|
38
|
+
│ │ ├── notification_service.dart # FCM + flutter_local_notifications
|
|
39
|
+
│ │ ├── notification_handler.dart # Foreground/background/tap
|
|
40
|
+
│ │ └── notification_navigation.dart # Deep-link routing
|
|
41
|
+
│ ├── theme/
|
|
42
|
+
│ │ ├── app_theme.dart
|
|
43
|
+
│ │ ├── app_colors.dart
|
|
44
|
+
│ │ └── app_typography.dart
|
|
45
|
+
│ ├── env/
|
|
46
|
+
│ │ └── env_config.dart
|
|
47
|
+
│ └── utils/
|
|
48
|
+
│ ├── validators.dart
|
|
49
|
+
│ ├── formatters.dart
|
|
50
|
+
│ └── url_helper.dart
|
|
51
|
+
├── shared/
|
|
52
|
+
│ ├── widgets/
|
|
53
|
+
│ │ ├── app_scaffold.dart
|
|
54
|
+
│ │ ├── loading_indicator.dart
|
|
55
|
+
│ │ ├── empty_state.dart
|
|
56
|
+
│ │ ├── error_state.dart
|
|
57
|
+
│ │ ├── confirm_dialog.dart
|
|
58
|
+
│ │ ├── status_chip.dart
|
|
59
|
+
│ │ ├── search_bar_field.dart
|
|
60
|
+
│ │ ├── pull_to_refresh_list.dart
|
|
61
|
+
│ │ ├── infinite_scroll_list.dart
|
|
62
|
+
│ │ ├── app_network_image.dart
|
|
63
|
+
│ │ ├── cached_svg_icon.dart
|
|
64
|
+
│ │ └── shell_screen.dart # Bottom-nav shell
|
|
65
|
+
│ ├── form/
|
|
66
|
+
│ │ ├── form_text_field.dart
|
|
67
|
+
│ │ ├── form_dropdown_field.dart
|
|
68
|
+
│ │ ├── form_date_picker_field.dart
|
|
69
|
+
│ │ ├── form_switch_field.dart
|
|
70
|
+
│ │ └── form_image_picker_field.dart # [If ImagePicker = yes]
|
|
71
|
+
│ ├── navigation/
|
|
72
|
+
│ │ └── nav_config.dart # NavItem list
|
|
73
|
+
│ └── providers/
|
|
74
|
+
│ └── theme_mode_provider.dart # ThemeMode controller (Hive-backed)
|
|
75
|
+
├── features/
|
|
76
|
+
│ ├── auth/ # [If Auth != none]
|
|
77
|
+
│ │ ├── presentation/
|
|
78
|
+
│ │ │ ├── login_screen.dart # [If Auth = Local]
|
|
79
|
+
│ │ │ └── auth_callback_screen.dart # [If Auth = Keycloak]
|
|
80
|
+
│ │ └── auth.dart # Barrel
|
|
81
|
+
│ ├── dashboard/
|
|
82
|
+
│ │ ├── presentation/
|
|
83
|
+
│ │ │ └── dashboard_screen.dart
|
|
84
|
+
│ │ └── dashboard.dart
|
|
85
|
+
│ ├── {{module1}}/ # PRD module 1 (e.g., task)
|
|
86
|
+
│ │ ├── data/
|
|
87
|
+
│ │ │ └── {{module1}}_repository.dart
|
|
88
|
+
│ │ ├── domain/
|
|
89
|
+
│ │ │ └── {{module1}}.dart # @freezed + Hive adapter
|
|
90
|
+
│ │ ├── application/
|
|
91
|
+
│ │ │ └── {{module1}}_providers.dart # Generated providers
|
|
92
|
+
│ │ ├── presentation/
|
|
93
|
+
│ │ │ ├── {{module1}}_list_screen.dart
|
|
94
|
+
│ │ │ ├── {{module1}}_detail_screen.dart
|
|
95
|
+
│ │ │ ├── {{module1}}_form_screen.dart
|
|
96
|
+
│ │ │ └── widgets/
|
|
97
|
+
│ │ │ └── {{module1}}_card.dart
|
|
98
|
+
│ │ └── {{module1}}.dart # Barrel
|
|
99
|
+
│ └── {{module2}}/
|
|
100
|
+
│ └── (same structure)
|
|
101
|
+
└── l10n/ # [If i18n = yes]
|
|
102
|
+
├── app_en.arb
|
|
103
|
+
└── app_ms.arb
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Route Tree Definition
|
|
109
|
+
|
|
110
|
+
```dart
|
|
111
|
+
// lib/router/app_router.dart
|
|
112
|
+
import 'package:flutter/material.dart';
|
|
113
|
+
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
114
|
+
import 'package:go_router/go_router.dart';
|
|
115
|
+
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
116
|
+
|
|
117
|
+
import '../core/auth/auth_provider.dart';
|
|
118
|
+
import '../core/auth/auth_state.dart';
|
|
119
|
+
import '../core/auth/roles.dart';
|
|
120
|
+
import '../features/auth/presentation/auth_callback_screen.dart';
|
|
121
|
+
import '../features/auth/presentation/login_screen.dart';
|
|
122
|
+
import '../features/dashboard/presentation/dashboard_screen.dart';
|
|
123
|
+
import '../features/profile/presentation/profile_screen.dart';
|
|
124
|
+
import '../features/task/presentation/task_form_screen.dart';
|
|
125
|
+
import '../features/task/presentation/task_list_screen.dart';
|
|
126
|
+
import '../shared/widgets/shell_screen.dart';
|
|
127
|
+
|
|
128
|
+
part 'app_router.g.dart';
|
|
129
|
+
|
|
130
|
+
@Riverpod(keepAlive: true)
|
|
131
|
+
GoRouter appRouter(AppRouterRef ref) {
|
|
132
|
+
final authState = ref.watch(authProvider);
|
|
133
|
+
|
|
134
|
+
return GoRouter(
|
|
135
|
+
initialLocation: '/dashboard',
|
|
136
|
+
debugLogDiagnostics: true,
|
|
137
|
+
redirect: (context, state) {
|
|
138
|
+
final isAuthed = authState.valueOrNull is _Authenticated;
|
|
139
|
+
final atLogin = state.matchedLocation == '/login' ||
|
|
140
|
+
state.matchedLocation.startsWith('/auth/');
|
|
141
|
+
|
|
142
|
+
// [If Auth = Local]
|
|
143
|
+
if (!isAuthed && !atLogin) return '/login';
|
|
144
|
+
if (isAuthed && atLogin) return '/dashboard';
|
|
145
|
+
return null;
|
|
146
|
+
},
|
|
147
|
+
routes: [
|
|
148
|
+
// ── Auth routes (public) ───────────────────────────────────────────
|
|
149
|
+
// [If Auth = Local]
|
|
150
|
+
GoRoute(
|
|
151
|
+
path: '/login',
|
|
152
|
+
name: 'login',
|
|
153
|
+
builder: (context, state) => const LoginScreen(),
|
|
154
|
+
),
|
|
155
|
+
// [If Auth = Keycloak]
|
|
156
|
+
GoRoute(
|
|
157
|
+
path: '/auth/callback',
|
|
158
|
+
name: 'auth-callback',
|
|
159
|
+
builder: (context, state) => const AuthCallbackScreen(),
|
|
160
|
+
),
|
|
161
|
+
|
|
162
|
+
// ── Authenticated shell (bottom navigation) ────────────────────────
|
|
163
|
+
ShellRoute(
|
|
164
|
+
builder: (context, state, child) => ShellScreen(child: child),
|
|
165
|
+
routes: [
|
|
166
|
+
GoRoute(
|
|
167
|
+
path: '/dashboard',
|
|
168
|
+
name: 'dashboard',
|
|
169
|
+
builder: (context, state) => const DashboardScreen(),
|
|
170
|
+
),
|
|
171
|
+
|
|
172
|
+
// Task — accessible by WORKER and ADMIN
|
|
173
|
+
GoRoute(
|
|
174
|
+
path: '/task',
|
|
175
|
+
name: 'task-list',
|
|
176
|
+
redirect: (context, state) =>
|
|
177
|
+
_requireRoles(authState, [Roles.worker, Roles.admin]),
|
|
178
|
+
builder: (context, state) => const TaskListScreen(),
|
|
179
|
+
routes: [
|
|
180
|
+
GoRoute(
|
|
181
|
+
path: 'create',
|
|
182
|
+
name: 'task-create',
|
|
183
|
+
builder: (context, state) => const TaskFormScreen(),
|
|
184
|
+
),
|
|
185
|
+
GoRoute(
|
|
186
|
+
path: ':id',
|
|
187
|
+
name: 'task-detail',
|
|
188
|
+
builder: (context, state) =>
|
|
189
|
+
TaskFormScreen(taskId: state.pathParameters['id']),
|
|
190
|
+
routes: [
|
|
191
|
+
GoRoute(
|
|
192
|
+
path: 'edit',
|
|
193
|
+
name: 'task-edit',
|
|
194
|
+
builder: (context, state) =>
|
|
195
|
+
TaskFormScreen(taskId: state.pathParameters['id']),
|
|
196
|
+
),
|
|
197
|
+
],
|
|
198
|
+
),
|
|
199
|
+
],
|
|
200
|
+
),
|
|
201
|
+
|
|
202
|
+
GoRoute(
|
|
203
|
+
path: '/profile',
|
|
204
|
+
name: 'profile',
|
|
205
|
+
builder: (context, state) => const ProfileScreen(),
|
|
206
|
+
),
|
|
207
|
+
|
|
208
|
+
// Add additional module routes following the same pattern...
|
|
209
|
+
],
|
|
210
|
+
),
|
|
211
|
+
|
|
212
|
+
// ── Error routes ───────────────────────────────────────────────────
|
|
213
|
+
GoRoute(
|
|
214
|
+
path: '/unauthorized',
|
|
215
|
+
builder: (context, state) => Scaffold(
|
|
216
|
+
appBar: AppBar(title: const Text('Unauthorized')),
|
|
217
|
+
body: const Center(
|
|
218
|
+
child: Text('You do not have permission to access this page.'),
|
|
219
|
+
),
|
|
220
|
+
),
|
|
221
|
+
),
|
|
222
|
+
],
|
|
223
|
+
errorBuilder: (context, state) => Scaffold(
|
|
224
|
+
appBar: AppBar(title: const Text('Not Found')),
|
|
225
|
+
body: const Center(child: Text('404 — Page not found.')),
|
|
226
|
+
),
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
String? _requireRoles(AsyncValue<AuthState> authState, List<String> requiredRoles) {
|
|
231
|
+
final state = authState.valueOrNull;
|
|
232
|
+
if (state is! _Authenticated) return null; // top-level redirect handles auth
|
|
233
|
+
final hasAny = requiredRoles.any((r) => state.user.roles.contains(r));
|
|
234
|
+
return hasAny ? null : '/unauthorized';
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## ShellScreen (Bottom Navigation Shell)
|
|
241
|
+
|
|
242
|
+
```dart
|
|
243
|
+
// lib/shared/widgets/shell_screen.dart
|
|
244
|
+
import 'package:flutter/material.dart';
|
|
245
|
+
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
246
|
+
import 'package:go_router/go_router.dart';
|
|
247
|
+
|
|
248
|
+
import '../../core/auth/auth_provider.dart';
|
|
249
|
+
import '../../core/auth/auth_state.dart';
|
|
250
|
+
import '../navigation/nav_config.dart';
|
|
251
|
+
|
|
252
|
+
class ShellScreen extends ConsumerWidget {
|
|
253
|
+
const ShellScreen({super.key, required this.child});
|
|
254
|
+
final Widget child;
|
|
255
|
+
|
|
256
|
+
@override
|
|
257
|
+
Widget build(BuildContext context, WidgetRef ref) {
|
|
258
|
+
final authState = ref.watch(authProvider);
|
|
259
|
+
final user = authState.valueOrNull is _Authenticated
|
|
260
|
+
? (authState.value! as _Authenticated).user
|
|
261
|
+
: null;
|
|
262
|
+
|
|
263
|
+
final visibleItems = navigationItems.where((item) {
|
|
264
|
+
if (item.requiredRoles == null) return true;
|
|
265
|
+
if (user == null) return false;
|
|
266
|
+
return item.requiredRoles!.any(user.roles.contains);
|
|
267
|
+
}).toList();
|
|
268
|
+
|
|
269
|
+
final location = GoRouterState.of(context).matchedLocation;
|
|
270
|
+
final currentIndex = visibleItems.indexWhere(
|
|
271
|
+
(item) => location.startsWith(item.path),
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
return Scaffold(
|
|
275
|
+
body: child,
|
|
276
|
+
bottomNavigationBar: NavigationBar(
|
|
277
|
+
selectedIndex: currentIndex < 0 ? 0 : currentIndex,
|
|
278
|
+
onDestinationSelected: (index) => context.go(visibleItems[index].path),
|
|
279
|
+
destinations: [
|
|
280
|
+
for (final item in visibleItems)
|
|
281
|
+
NavigationDestination(
|
|
282
|
+
icon: Icon(item.icon),
|
|
283
|
+
selectedIcon: Icon(item.activeIcon),
|
|
284
|
+
label: item.label,
|
|
285
|
+
),
|
|
286
|
+
],
|
|
287
|
+
),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Deep Link Configuration
|
|
296
|
+
|
|
297
|
+
go_router automatically handles deep links registered in the Android manifest and iOS
|
|
298
|
+
`Info.plist`. For FCM notification payloads, use the same path structure.
|
|
299
|
+
|
|
300
|
+
**Notification payload example:**
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"notification": {
|
|
304
|
+
"title": "New task assigned",
|
|
305
|
+
"body": "Inspect site #42"
|
|
306
|
+
},
|
|
307
|
+
"data": {
|
|
308
|
+
"deeplink": "/task/abc-123"
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Handler (in `NotificationService`):**
|
|
314
|
+
```dart
|
|
315
|
+
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
316
|
+
final deeplink = message.data['deeplink'] as String?;
|
|
317
|
+
if (deeplink != null) {
|
|
318
|
+
// Read router from ProviderContainer at app root (set up in app.dart)
|
|
319
|
+
rootContainer.read(appRouterProvider).go(deeplink);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Typed Route Helpers (Optional via go_router_builder)
|
|
327
|
+
|
|
328
|
+
If `go_router_builder` is added, define typed route classes for compile-time safety:
|
|
329
|
+
|
|
330
|
+
```dart
|
|
331
|
+
@TypedGoRoute<TaskDetailRoute>(path: '/task/:id')
|
|
332
|
+
class TaskDetailRoute extends GoRouteData {
|
|
333
|
+
const TaskDetailRoute({required this.id});
|
|
334
|
+
final String id;
|
|
335
|
+
|
|
336
|
+
@override
|
|
337
|
+
Widget build(BuildContext context, GoRouterState state) =>
|
|
338
|
+
TaskFormScreen(taskId: id);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Navigate:
|
|
342
|
+
// TaskDetailRoute(id: 'abc-123').push(context);
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## AppBar Back Behaviour
|
|
348
|
+
|
|
349
|
+
go_router handles back gestures and the AppBar back arrow automatically via
|
|
350
|
+
`canPop()`. Inside a `ShellRoute`, prefer `context.go(<parent>)` over `context.pop()`
|
|
351
|
+
when navigating between top-level tabs.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Route Guards Summary
|
|
356
|
+
|
|
357
|
+
| Guard | Location | Behaviour |
|
|
358
|
+
|---|---|---|
|
|
359
|
+
| Auth required | Top-level `GoRouter.redirect` | Push `/login` when unauthenticated |
|
|
360
|
+
| Role required | Per-route `redirect` callback | Push `/unauthorized` when role missing |
|
|
361
|
+
| Onboarding | Top-level `GoRouter.redirect` | Push `/onboarding` on first launch (Hive flag) |
|
|
362
|
+
| Deep link from FCM | `NotificationService.onMessageOpenedApp` | Push payload `deeplink` via router instance |
|