diff --git a/.claude/agents/architecture-reviewer.md b/.claude/agents/architecture-reviewer.md index 8918f26d..887a4f0b 100644 --- a/.claude/agents/architecture-reviewer.md +++ b/.claude/agents/architecture-reviewer.md @@ -58,6 +58,7 @@ and load any additional skills as needed for specific review challenges. 6. Missing tests for use cases or repositories 7. Complex BLoC without bloc_test coverage 8. Test coverage below 70% for business logic +9. Hardcoded user-facing strings — must use `core_localization` (Slang) via `t.
.`. All `Text('...')` with literal English/Spanish strings in presentation layer must be replaced with localized keys ### MODERATE (Request Fix, can be deferred with justification): 1. Missing doc comments on public APIs @@ -103,7 +104,7 @@ Verify: - Business logic resides exclusively in use cases - Entities are in domain, models in data, widgets in presentation -### Step 3: Design System Compliance +### Step 3: Design System & Localization Compliance ```bash # Hardcoded colors @@ -117,9 +118,22 @@ grep -rn -E "EdgeInsets\.(all|symmetric|only)\(" apps/mobile/apps/*/lib/features # Direct icon imports grep -rn "^import.*icons" apps/mobile/apps/*/lib/features/ + +# Hardcoded user-facing strings (look for Text('...') with literal strings) +grep -rn "Text(['\"]" apps/mobile/packages/features/ +# Also check for hardcoded strings in SnackBar, AlertDialog, AppBar title, etc. +grep -rn "title: ['\"]" apps/mobile/packages/features/ +grep -rn "label: ['\"]" apps/mobile/packages/features/ +grep -rn "hintText: ['\"]" apps/mobile/packages/features/ ``` -All styling must come from the design system. No exceptions. +All styling must come from the design system. All user-facing strings must come from `core_localization` via Slang (`t.
.`). No exceptions. + +**Localization rules:** +- Strings defined in `packages/core_localization/lib/src/l10n/en.i18n.json` and `es.i18n.json` +- Accessed via `t.
.` (e.g., `t.client_create_order.review.invalid_arguments`) +- Both `en` and `es` JSON files must be updated together +- Regenerate with `dart run slang` from `packages/core_localization/` directory ### Step 4: State Management Review For every BLoC in changed files, verify: diff --git a/.claude/skills/krow-mobile-architecture/SKILL.md b/.claude/skills/krow-mobile-architecture/SKILL.md index eccc0bb2..2ba4d4cf 100644 --- a/.claude/skills/krow-mobile-architecture/SKILL.md +++ b/.claude/skills/krow-mobile-architecture/SKILL.md @@ -266,11 +266,29 @@ design_system/ - Export `TranslationProvider` for `context.strings` access - Map domain failures to localized error messages via `ErrorTranslator` +**String Definition:** +- Strings are defined in `packages/core_localization/lib/src/l10n/en.i18n.json` (English) and `es.i18n.json` (Spanish) +- Both files MUST be updated together when adding/modifying strings +- Generated output: `strings.g.dart`, `strings_en.g.dart`, `strings_es.g.dart` +- Regenerate with: `cd packages/core_localization && dart run slang` + **Feature Integration:** ```dart -// Features access strings -Text(context.strings.loginButton) +// ✅ CORRECT: Access via Slang's global `t` accessor +import 'package:core_localization/core_localization.dart'; +Text(t.client_create_order.review.invalid_arguments) +Text(t.errors.order.creation_failed) + +// ❌ FORBIDDEN: Hardcoded user-facing strings +Text('Invalid review arguments') // Must use localized key +Text('Order created!') // Must use localized key +``` + +**RESTRICTION:** ALL user-facing strings in the presentation layer (Text widgets, SnackBars, AppBar titles, hints, labels, error messages, dialogs) MUST use localized keys via `t.
.`. No hardcoded English or Spanish strings. + +**BLoC Error Flow:** +```dart // BLoCs emit domain failures (not strings) emit(AuthError(InvalidCredentialsFailure())); @@ -879,6 +897,11 @@ Navigator.push(context, MaterialPageRoute(...)); // ← Use Modular Modular.to.navigate('/profile'); // ← Use safe extensions ``` +❌ **Hardcoded user-facing strings** +```dart +Text('Order created successfully!'); // ← Use t.section.key from core_localization +``` + ## Summary The architecture enforces: diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_header.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_header.dart deleted file mode 100644 index 75c05c80..00000000 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_header.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; - -/// Displays the "Review & Submit" title and subtitle at the top of the -/// review order page. -class ReviewOrderHeader extends StatelessWidget { - const ReviewOrderHeader({super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only( - left: UiConstants.space6, - right: UiConstants.space6, - top: UiConstants.space4, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Review & Submit', - style: UiTypography.headline2m, - ), - const SizedBox(height: UiConstants.space1), - Text( - 'Confirm details before posting', - style: UiTypography.body2r.textSecondary, - ), - ], - ), - ); - } -} diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_info_row.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_info_row.dart index 9add76e5..3946c1a8 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_info_row.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_info_row.dart @@ -19,18 +19,18 @@ class ReviewOrderInfoRow extends StatelessWidget { Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: UiConstants.space2, children: [ Flexible( child: Text( label, - style: UiTypography.body3r.textSecondary, + style: UiTypography.body2r.textSecondary, ), ), - const SizedBox(width: UiConstants.space3), Flexible( child: Text( value, - style: UiTypography.body3m, + style: UiTypography.body2m, textAlign: TextAlign.end, ), ), diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_positions_card.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_positions_card.dart index 18812630..0275f8cd 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_positions_card.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_positions_card.dart @@ -26,7 +26,7 @@ class ReviewOrderPositionsCard extends StatelessWidget { decoration: BoxDecoration( color: UiColors.white, borderRadius: UiConstants.radiusXl, - border: Border.all(color: UiColors.border), + border: Border.all(color: UiColors.border, width: 0.5), ), padding: const EdgeInsets.all(UiConstants.space4), child: Column( diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_section_card.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_section_card.dart index 33f8b5e8..83680bc1 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_section_card.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_section_card.dart @@ -23,7 +23,7 @@ class ReviewOrderSectionCard extends StatelessWidget { decoration: BoxDecoration( color: UiColors.white, borderRadius: UiConstants.radiusXl, - border: Border.all(color: UiColors.border), + border: Border.all(color: UiColors.border, width: 0.5), ), padding: const EdgeInsets.all(UiConstants.space4), child: Column( @@ -39,10 +39,7 @@ class ReviewOrderSectionCard extends StatelessWidget { if (onEdit != null) GestureDetector( onTap: onEdit, - child: Text( - 'Edit', - style: UiTypography.body3m.primary, - ), + child: Text('Edit', style: UiTypography.body3m.primary), ), ], ), diff --git a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_view.dart b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_view.dart index 46fb7453..13a2e7ec 100644 --- a/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_view.dart +++ b/apps/mobile/packages/features/client/orders/create_order/lib/src/presentation/widgets/review_order/review_order_view.dart @@ -2,7 +2,6 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'review_order_action_bar.dart'; import 'review_order_basics_card.dart'; -import 'review_order_header.dart'; import 'review_order_positions_card.dart'; import 'review_order_total_banner.dart'; @@ -56,10 +55,11 @@ class ReviewOrderView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: UiColors.bgMenu, appBar: UiAppBar( showBackButton: true, onLeadingPressed: onBack, + title: 'Review & Submit', + subtitle: 'Confirm details before posting', ), body: Column( children: [ @@ -68,7 +68,6 @@ class ReviewOrderView extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const ReviewOrderHeader(), Padding( padding: const EdgeInsets.symmetric( horizontal: UiConstants.space6, diff --git a/backend/dataconnect/dataconnect.yaml b/backend/dataconnect/dataconnect.yaml index 9e1775d6..39e01fdb 100644 --- a/backend/dataconnect/dataconnect.yaml +++ b/backend/dataconnect/dataconnect.yaml @@ -1,5 +1,5 @@ specVersion: "v1" -serviceId: "krow-workforce-db-validation" +serviceId: "krow-workforce-db" location: "us-central1" schema: source: "./schema" @@ -7,7 +7,7 @@ schema: postgresql: database: "krow_db" cloudSql: - instanceId: "krow-sql-validation" + instanceId: "krow-sql" # schemaValidation: "STRICT" # STRICT mode makes Postgres schema match Data Connect exactly. # schemaValidation: "COMPATIBLE" # COMPATIBLE mode makes Postgres schema compatible with Data Connect. connectorDirs: ["./connector"]