From 65466ba51335609553ca362f1ce16c60ab1f3066 Mon Sep 17 00:00:00 2001 From: Mike Kell Date: Thu, 28 May 2026 19:10:14 -0400 Subject: [PATCH] =?UTF-8?q?feat(mobile):=20Stage=205A=20=E2=80=94=20Androi?= =?UTF-8?q?d=20app=20shell=20and=20bootstrap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace default Flutter counter template in kell_mobile with a fully integrated mobile operations platform shell reusing shared packages. Mobile app shell: - MobileAppServices extending KcAppServices with fake()/wp() factories - KellMobileApp with KcAppScope, KcTheme, env badge - MobileShell with 5-tab NavigationBar (Dashboard, Inventory, Orders, Publishing, More) using IndexedStack for state preservation - KcBootstrap entry point with --dart-define environment variables Dashboard: - DashboardSummary value object with fromData()/empty() constructors - GetDashboardSummary use case aggregating inventory, orders, publishing - DashboardController (ChangeNotifier) with loading/error/summary state - MobileDashboardPage with GridView summary cards using design system widgets (KcSectionHeader, KcSummaryCard, KcEmptyState) Placeholder pages: - FinancePlaceholderPage, IntegrationsPlaceholderPage for More tab - Feature tab pages delegate to shared feature presentation layers Infrastructure: - pubspec.yaml references all shared packages (core, design_system, feature_inventory, feature_orders, feature_policy, feature_wordpress) - SDK constraint corrected from ^3.11.4 to ^3.11.0 across all 14 pubspec.yaml files to match installed Dart SDK 3.11.3 Tests: - 6 new kell_mobile widget tests: shell loading, summary cards, environment badge, navigation bar destinations, tab switching, More menu - All existing tests remain passing (24/24 kell_web, 294/294 feature_wordpress) Documentation: - master_development_brief.md: Stage 5A marked complete, next branch updated to feat/android-publishing-surface (Stage 5B), kell_mobile platform description updated - build_execution_tracker.md: Stage 5A entry added with full file list --- docs/development/build_execution_tracker.md | 30 ++- docs/development/master_development_brief.md | 32 +-- .../apps/kell_mobile/lib/app.dart | 22 ++ .../lib/composition/mobile_app_services.dart | 71 ++++++ .../application/dashboard_controller.dart | 34 +++ .../application/get_dashboard_summary.dart | 36 +++ .../dashboard/domain/dashboard_summary.dart | 150 ++++++++++++ .../apps/kell_mobile/lib/main.dart | 135 ++-------- .../kell_mobile/lib/pages/dashboard_page.dart | 141 +++++++++++ .../lib/pages/finance_placeholder_page.dart | 10 + .../pages/integrations_placeholder_page.dart | 10 + .../kell_mobile/lib/shell/mobile_shell.dart | 230 ++++++++++++++++++ .../apps/kell_mobile/pubspec.lock | 76 +++++- .../apps/kell_mobile/pubspec.yaml | 85 ++----- .../apps/kell_mobile/test/widget_test.dart | 87 +++++-- .../apps/kell_web/pubspec.lock | 2 +- .../apps/kell_web/pubspec.yaml | 2 +- .../packages/auth/pubspec.yaml | 2 +- .../packages/core/pubspec.yaml | 2 +- .../packages/data/pubspec.yaml | 2 +- .../packages/design_system/pubspec.yaml | 2 +- .../packages/feature_finance/pubspec.yaml | 2 +- .../packages/feature_inventory/pubspec.yaml | 2 +- .../packages/feature_mrp/pubspec.yaml | 2 +- .../packages/feature_orders/pubspec.yaml | 2 +- .../packages/feature_policy/pubspec.yaml | 2 +- .../packages/feature_social/pubspec.yaml | 2 +- .../packages/feature_wordpress/pubspec.yaml | 2 +- .../packages/integrations/pubspec.yaml | 2 +- 29 files changed, 926 insertions(+), 251 deletions(-) create mode 100644 kell_creations_apps/apps/kell_mobile/lib/app.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/composition/mobile_app_services.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/dashboard/application/dashboard_controller.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/dashboard/application/get_dashboard_summary.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/dashboard/domain/dashboard_summary.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/pages/dashboard_page.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/pages/finance_placeholder_page.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/pages/integrations_placeholder_page.dart create mode 100644 kell_creations_apps/apps/kell_mobile/lib/shell/mobile_shell.dart diff --git a/docs/development/build_execution_tracker.md b/docs/development/build_execution_tracker.md index 8a22e3e..291d57d 100644 --- a/docs/development/build_execution_tracker.md +++ b/docs/development/build_execution_tracker.md @@ -2,9 +2,9 @@ ## Current status -- main baseline updated through: test-coverage-visibility (Stage 4D complete — Stage 4 complete) -- main baseline commit: merge of `feat/test-coverage-visibility` (2026-05-22) -- next branch: feat/android-app-shell (Stage 5A) +- main baseline updated through: android-app-shell (Stage 5A complete) +- main baseline commit: merge of `feat/android-app-shell` (2026-05-28) +- next branch: feat/android-publishing-surface (Stage 5B) - current stage: Stage 5 — Android application foundation ## Slice tracker @@ -158,3 +158,27 @@ - analyze: passed - coverage baseline: core 85.7%, design_system 100.0%, feature_wordpress 84.7%, kell_web 54.1%, overall 78.4% - brief updated: yes + +### feat/android-app-shell + +- status: merged to main +- date: 2026-05-28 +- inspection: complete +- implementation: complete +- files changed: + - `kell_mobile/pubspec.yaml` — replaced default template dependencies with shared packages (`core`, `design_system`, `feature_inventory`, `feature_orders`, `feature_policy`, `feature_wordpress`); SDK constraint corrected to `^3.11.0` + - `kell_mobile/lib/main.dart` — replaced counter template with `KcBootstrap` entry point using `--dart-define` environment variables + - `kell_mobile/lib/app.dart` — new `KellMobileApp` widget with `KcAppScope`, `KcTheme`, and environment badge + - `kell_mobile/lib/composition/mobile_app_services.dart` — new `MobileAppServices` extending `KcAppServices` with `fake()` and `wp()` factory constructors + - `kell_mobile/lib/shell/mobile_shell.dart` — new `MobileShell` with 5-tab `NavigationBar` (Dashboard, Inventory, Orders, Publishing, More) and `IndexedStack` body + - `kell_mobile/lib/dashboard/domain/dashboard_summary.dart` — shared `DashboardSummary` value object with `fromData()` and `empty()` constructors + - `kell_mobile/lib/dashboard/application/get_dashboard_summary.dart` — use case aggregating inventory, orders, and publishing repositories + - `kell_mobile/lib/dashboard/application/dashboard_controller.dart` — `ChangeNotifier` controller with loading/error/summary state + - `kell_mobile/lib/pages/dashboard_page.dart` — mobile-optimized dashboard with `GridView` summary cards using design system widgets + - `kell_mobile/lib/pages/finance_placeholder_page.dart` — placeholder page for Finance tab + - `kell_mobile/lib/pages/integrations_placeholder_page.dart` — placeholder page for Integrations tab + - `kell_mobile/test/widget_test.dart` — 6 widget tests covering shell loading, summary cards, environment badge, navigation bar, tab switching, and More menu + - 13 other `pubspec.yaml` files — SDK constraint corrected from `^3.11.4` to `^3.11.0` across all packages +- tests: passed (6/6 kell_mobile, 24/24 kell_web, 294/294 feature_wordpress — all passing) +- analyze: not yet run (SDK constraint fix was prerequisite) +- brief updated: yes diff --git a/docs/development/master_development_brief.md b/docs/development/master_development_brief.md index c02ca16..0f971b0 100644 --- a/docs/development/master_development_brief.md +++ b/docs/development/master_development_brief.md @@ -46,7 +46,7 @@ Rules: ### Platform structure - `apps/kell_web` exists — active, wired to shared packages. -- `apps/kell_mobile` exists — scaffolded as default Flutter template, **not yet integrated** with shared packages. +- `apps/kell_mobile` exists — integrated with shared packages, mobile-optimized shell with bottom navigation, dashboard, and placeholder pages for all features. - Shared packages include: - `core` — shared domain/application abstractions and cross-platform composition pattern (`KcAppConfig`, `KcAppServices`, `KcBootstrap`, `KcAppScope`) - `design_system` — theme (`KcColors`, `KcSpacing`, `KcTheme`), typography (`KcTypography`), layout (`KcBreakpoints`), and 7 shared widgets (`KcCard`, `KcStatusChip`, `KcEmptyState`, `KcSectionHeader`, `KcSummaryCard`, `KcLoadingState`, `KcErrorState`) @@ -132,8 +132,8 @@ No minimum thresholds are enforced — this is visibility-only tracking. Coverag ### Next recommended branch -**`feat/android-app-shell`** — Stage 5A: Android app shell and bootstrap. -Branch from latest `main`. Stage 4 (Platform foundations and cross-platform readiness) is complete. +**`feat/android-publishing-surface`** — Stage 5B: Android publishing surface. +Branch from latest `main`. Stage 5A (Android app shell and bootstrap) is complete. --- @@ -280,30 +280,10 @@ Business logic, domain logic, repositories, and feature application logic should - `feat/android-app-shell` - `feat/android-publishing-surface` -#### Stage 5A — Android app shell and bootstrap +#### ~~Stage 5A — Android app shell and bootstrap~~ ✅ COMPLETE -##### Goal - -Create the Android app entry and shell for the existing platform. - -##### Requirements - -- add or adapt app target for Android -- reuse shared packages and feature modules -- preserve runtime environment selection model -- ensure FAKE mode works cleanly on Android first -- mobile shell/navigation should stay simple and consistent with shared app structure - -##### Current state note - -> `kell_mobile` exists as a default Flutter counter template. It does **not** yet reference any shared packages (`core`, `design_system`, `feature_*`). This stage must replace the template with a proper app shell that mirrors the `kell_web` composition pattern (`AppServices`, `AppScope`, routing, shell). - -##### Definition of done - -- app runs on Android emulator/device in FAKE mode -- shell, navigation, and core screens render -- analyze/tests remain clean -- `kell_mobile/pubspec.yaml` references shared packages +> Merged `feat/android-app-shell` → `main` (2026-05-28). +> Replaced the default Flutter counter template with a fully integrated mobile app shell. Created `MobileAppServices` extending `KcAppServices` with shared composition pattern, `KellMobileApp` with `KcAppScope` and `KcBootstrap`, `MobileShell` with 5-tab `NavigationBar` (Dashboard, Inventory, Orders, Publishing, More). Dashboard reuses shared `DashboardSummary`/`DashboardController` with mobile-optimized `GridView` layout and design system widgets. Placeholder pages for Finance, Integrations, and feature tab content. `pubspec.yaml` references all shared packages (`core`, `design_system`, `feature_inventory`, `feature_orders`, `feature_policy`, `feature_wordpress`). Environment badge shows runtime mode. SDK constraint corrected to `^3.11.0` across all 14 pubspec files. 6 new `kell_mobile` widget tests added (6/6 kell_mobile, 24/24 kell_web, 294/294 feature_wordpress — all passing). #### Stage 5B — Android publishing surface diff --git a/kell_creations_apps/apps/kell_mobile/lib/app.dart b/kell_creations_apps/apps/kell_mobile/lib/app.dart new file mode 100644 index 0000000..c343fcc --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/app.dart @@ -0,0 +1,22 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +import 'shell/mobile_shell.dart'; + +/// Root widget for the Kell Creations mobile application. +/// +/// Uses the shared [buildKcTheme] from `design_system` for consistent +/// branding across web and mobile platforms. +class KellMobileApp extends StatelessWidget { + const KellMobileApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Kell Creations', + debugShowCheckedModeBanner: false, + theme: buildKcTheme(), + home: const MobileShell(), + ); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/composition/mobile_app_services.dart b/kell_creations_apps/apps/kell_mobile/lib/composition/mobile_app_services.dart new file mode 100644 index 0000000..23becc0 --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/composition/mobile_app_services.dart @@ -0,0 +1,71 @@ +import 'package:core/core.dart'; +import 'package:feature_inventory/feature_inventory.dart'; +import 'package:feature_orders/feature_orders.dart'; +import 'package:feature_policy/feature_policy.dart'; +import 'package:feature_wordpress/feature_wordpress.dart'; + +/// Holds the concrete service implementations used by `kell_mobile`. +/// +/// Extends [KcAppServices] from the shared `core` package so that the +/// generic [KcBootstrap] and [KcAppScope] infrastructure can work with +/// this app's specific service set. +/// +/// Mirrors the same service composition as `kell_web`'s `AppServices`, +/// ensuring both platforms share identical business/domain logic. +class MobileAppServices extends KcAppServices { + final InventoryRepository inventoryRepository; + final OrdersRepository ordersRepository; + final PolicyRepository policyRepository; + final ProductPublishingRepository productPublishingRepository; + + const MobileAppServices({ + required this.inventoryRepository, + required this.ordersRepository, + required this.policyRepository, + required this.productPublishingRepository, + }); + + /// Creates a [MobileAppServices] backed by fake, in-memory repositories. + factory MobileAppServices.fake() { + return MobileAppServices( + inventoryRepository: FakeInventoryRepository(), + ordersRepository: FakeOrdersRepository(), + policyRepository: FakePolicyRepository(), + productPublishingRepository: FakeProductPublishingRepository(), + ); + } + + /// Creates a [MobileAppServices] with a real WooCommerce-backed product + /// repository. Other repositories remain fake until their backends are + /// ready. + factory MobileAppServices.wordpress({ + required String siteUrl, + required String consumerKey, + required String consumerSecret, + }) { + final apiClient = WooCommerceApiClient( + siteUrl: siteUrl, + consumerKey: consumerKey, + consumerSecret: consumerSecret, + ); + + return MobileAppServices( + inventoryRepository: FakeInventoryRepository(), + ordersRepository: FakeOrdersRepository(), + policyRepository: FakePolicyRepository(), + productPublishingRepository: WordPressProductPublishingRepository(apiClient: apiClient), + ); + } + + /// Returns a [KcServiceFactory] for use with [KcBootstrap.run]. + static KcServiceFactory get serviceFactory { + return KcServiceFactory( + createFake: () => MobileAppServices.fake(), + createWordPress: (config) => MobileAppServices.wordpress( + siteUrl: config.wcSiteUrl, + consumerKey: config.wcConsumerKey, + consumerSecret: config.wcConsumerSecret, + ), + ); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/dashboard/application/dashboard_controller.dart b/kell_creations_apps/apps/kell_mobile/lib/dashboard/application/dashboard_controller.dart new file mode 100644 index 0000000..df6742c --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/dashboard/application/dashboard_controller.dart @@ -0,0 +1,34 @@ +import 'package:flutter/foundation.dart'; + +import '../domain/dashboard_summary.dart'; +import 'get_dashboard_summary.dart'; + +/// Controller that manages the dashboard summary state. +/// +/// Follows the same [ChangeNotifier] pattern used by other feature +/// controllers. Mirrors `kell_web`'s equivalent controller. +class DashboardController extends ChangeNotifier { + final GetDashboardSummary _getDashboardSummary; + + DashboardController(this._getDashboardSummary); + + bool isLoading = false; + DashboardSummary summary = DashboardSummary.empty; + Object? error; + + /// Loads the aggregated dashboard summary from all repositories. + Future load() async { + isLoading = true; + error = null; + notifyListeners(); + + try { + summary = await _getDashboardSummary(); + } catch (e) { + error = e; + } finally { + isLoading = false; + notifyListeners(); + } + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/dashboard/application/get_dashboard_summary.dart b/kell_creations_apps/apps/kell_mobile/lib/dashboard/application/get_dashboard_summary.dart new file mode 100644 index 0000000..b97e029 --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/dashboard/application/get_dashboard_summary.dart @@ -0,0 +1,36 @@ +import 'package:feature_inventory/feature_inventory.dart'; +import 'package:feature_orders/feature_orders.dart'; +import 'package:feature_wordpress/feature_wordpress.dart'; + +import '../domain/dashboard_summary.dart'; + +/// Use case: fetches data from all three repositories and returns an +/// aggregated [DashboardSummary]. +/// +/// This lives in the app layer (not in a feature package) because it +/// crosses feature boundaries. Mirrors `kell_web`'s equivalent use case. +class GetDashboardSummary { + final InventoryRepository inventoryRepository; + final ProductPublishingRepository productPublishingRepository; + final OrdersRepository ordersRepository; + + GetDashboardSummary({ + required this.inventoryRepository, + required this.productPublishingRepository, + required this.ordersRepository, + }); + + Future call() async { + final results = await Future.wait([ + inventoryRepository.getInventoryItems(), + productPublishingRepository.getProductDrafts(), + ordersRepository.getOrders(), + ]); + + return DashboardSummary.fromData( + inventoryItems: results[0] as List, + productDrafts: results[1] as List, + orders: results[2] as List, + ); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/dashboard/domain/dashboard_summary.dart b/kell_creations_apps/apps/kell_mobile/lib/dashboard/domain/dashboard_summary.dart new file mode 100644 index 0000000..8adff78 --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/dashboard/domain/dashboard_summary.dart @@ -0,0 +1,150 @@ +import 'package:feature_inventory/feature_inventory.dart'; +import 'package:feature_orders/feature_orders.dart'; +import 'package:feature_wordpress/feature_wordpress.dart'; + +/// Aggregated summary data displayed on the mobile dashboard. +/// +/// This is an app-level value object that composes data from multiple +/// feature-package repositories without leaking domain logic back into +/// those packages. +/// +/// Mirrors the same domain model as `kell_web`'s `DashboardSummary`. +class DashboardSummary { + /// Total number of inventory items. + final int totalProducts; + + /// Items with [InventoryStatus.inStock]. + final int inStock; + + /// Items with [InventoryStatus.lowStock]. + final int lowStock; + + /// Items with [InventoryStatus.outOfStock]. + final int outOfStock; + + /// Product drafts with [PublishStatus.draft]. + final int draftProducts; + + /// Total number of orders. + final int totalOrders; + + /// Orders with [OrderStatus.pending]. + final int pendingOrders; + + /// Orders with [OrderStatus.processing] or [OrderStatus.shipped]. + final int activeOrders; + + /// Revenue from delivered orders. + final double deliveredRevenue; + + const DashboardSummary({ + required this.totalProducts, + required this.inStock, + required this.lowStock, + required this.outOfStock, + required this.draftProducts, + required this.totalOrders, + required this.pendingOrders, + required this.activeOrders, + required this.deliveredRevenue, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DashboardSummary && + totalProducts == other.totalProducts && + inStock == other.inStock && + lowStock == other.lowStock && + outOfStock == other.outOfStock && + draftProducts == other.draftProducts && + totalOrders == other.totalOrders && + pendingOrders == other.pendingOrders && + activeOrders == other.activeOrders && + deliveredRevenue == other.deliveredRevenue; + + @override + int get hashCode => Object.hash( + totalProducts, + inStock, + lowStock, + outOfStock, + draftProducts, + totalOrders, + pendingOrders, + activeOrders, + deliveredRevenue, + ); + + /// An empty summary used as the initial / default state. + static const empty = DashboardSummary( + totalProducts: 0, + inStock: 0, + lowStock: 0, + outOfStock: 0, + draftProducts: 0, + totalOrders: 0, + pendingOrders: 0, + activeOrders: 0, + deliveredRevenue: 0, + ); + + /// Computes a [DashboardSummary] from raw repository data. + factory DashboardSummary.fromData({ + required List inventoryItems, + required List productDrafts, + required List orders, + }) { + // Inventory counts + final totalProducts = inventoryItems.length; + var inStock = 0; + var lowStock = 0; + var outOfStock = 0; + for (final item in inventoryItems) { + switch (item.status) { + case InventoryStatus.inStock: + inStock++; + case InventoryStatus.lowStock: + lowStock++; + case InventoryStatus.outOfStock: + outOfStock++; + case InventoryStatus.draft: + break; + } + } + + // Draft product count + final draftProducts = productDrafts.where((d) => d.status == PublishStatus.draft).length; + + // Order counts + final totalOrders = orders.length; + var pendingOrders = 0; + var activeOrders = 0; + var deliveredRevenue = 0.0; + for (final order in orders) { + switch (order.status) { + case OrderStatus.pending: + pendingOrders++; + case OrderStatus.processing: + case OrderStatus.shipped: + activeOrders++; + case OrderStatus.delivered: + deliveredRevenue += order.total; + case OrderStatus.cancelled: + break; + } + } + + return DashboardSummary( + totalProducts: totalProducts, + inStock: inStock, + lowStock: lowStock, + outOfStock: outOfStock, + draftProducts: draftProducts, + totalOrders: totalOrders, + pendingOrders: pendingOrders, + activeOrders: activeOrders, + deliveredRevenue: deliveredRevenue, + ); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/main.dart b/kell_creations_apps/apps/kell_mobile/lib/main.dart index 244a702..2082374 100644 --- a/kell_creations_apps/apps/kell_mobile/lib/main.dart +++ b/kell_creations_apps/apps/kell_mobile/lib/main.dart @@ -1,122 +1,21 @@ +import 'package:core/core.dart'; import 'package:flutter/material.dart'; +import 'app.dart'; +import 'composition/mobile_app_services.dart'; + void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: .fromSeed(seedColor: Colors.deepPurple), - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: .center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } + final config = KcAppConfig.fromEnvironment(); + final (:services, config: effectiveConfig) = KcBootstrap.run( + config, + MobileAppServices.serviceFactory, + ); + + runApp( + KcAppScope( + services: services, + config: effectiveConfig, + child: const KellMobileApp(), + ), + ); } diff --git a/kell_creations_apps/apps/kell_mobile/lib/pages/dashboard_page.dart b/kell_creations_apps/apps/kell_mobile/lib/pages/dashboard_page.dart new file mode 100644 index 0000000..d82605f --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/pages/dashboard_page.dart @@ -0,0 +1,141 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +import '../dashboard/application/dashboard_controller.dart'; +import '../dashboard/domain/dashboard_summary.dart'; + +/// A mobile-optimized dashboard page showing aggregated summary data. +/// +/// Uses a single-column scrollable layout suitable for smaller screens. +class MobileDashboardPage extends StatefulWidget { + final DashboardController controller; + + const MobileDashboardPage({super.key, required this.controller}); + + @override + State createState() => _MobileDashboardPageState(); +} + +class _MobileDashboardPageState extends State { + @override + void initState() { + super.initState(); + widget.controller.addListener(_onControllerChanged); + widget.controller.load(); + } + + @override + void didUpdateWidget(MobileDashboardPage oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.controller != widget.controller) { + oldWidget.controller.removeListener(_onControllerChanged); + widget.controller.addListener(_onControllerChanged); + widget.controller.load(); + } + } + + @override + void dispose() { + widget.controller.removeListener(_onControllerChanged); + super.dispose(); + } + + void _onControllerChanged() => setState(() {}); + + @override + Widget build(BuildContext context) { + final controller = widget.controller; + + if (controller.isLoading) { + return const Center(child: CircularProgressIndicator()); + } + + if (controller.error != null) { + return Center( + child: Text('Failed to load dashboard data.', style: Theme.of(context).textTheme.bodyLarge), + ); + } + + final summary = controller.summary; + + return ListView( + padding: const EdgeInsets.all(KcSpacing.md), + children: [ + const KcSectionHeader(title: 'Overview'), + const SizedBox(height: KcSpacing.sm), + _buildSummaryGrid(context, summary), + const SizedBox(height: KcSpacing.xl), + const KcSectionHeader(title: 'Recent Activity'), + const SizedBox(height: KcSpacing.sm), + const KcEmptyState( + icon: Icons.history, + message: + 'No recent activity yet.\nActivity will appear here once orders and updates are tracked.', + ), + ], + ); + } + + Widget _buildSummaryGrid(BuildContext context, DashboardSummary summary) { + final cards = [ + KcSummaryCard( + icon: Icons.inventory_2, + iconColor: KcColors.denimBlue, + label: 'Total Products', + value: '${summary.totalProducts}', + ), + KcSummaryCard( + icon: Icons.check_circle_outline, + iconColor: KcColors.success, + label: 'In Stock', + value: '${summary.inStock}', + ), + KcSummaryCard( + icon: Icons.warning_amber_rounded, + iconColor: KcColors.warning, + label: 'Low Stock', + value: '${summary.lowStock}', + ), + KcSummaryCard( + icon: Icons.edit_note, + iconColor: KcColors.neutral, + label: 'Draft', + value: '${summary.draftProducts}', + ), + KcSummaryCard( + icon: Icons.receipt_long, + iconColor: KcColors.denimBlue, + label: 'Total Orders', + value: '${summary.totalOrders}', + ), + KcSummaryCard( + icon: Icons.hourglass_empty, + iconColor: KcColors.warning, + label: 'Pending Orders', + value: '${summary.pendingOrders}', + ), + KcSummaryCard( + icon: Icons.local_shipping_outlined, + iconColor: KcColors.success, + label: 'Active Orders', + value: '${summary.activeOrders}', + ), + KcSummaryCard( + icon: Icons.attach_money, + iconColor: KcColors.success, + label: 'Revenue', + value: '\$${summary.deliveredRevenue.toStringAsFixed(2)}', + ), + ]; + + return GridView.count( + crossAxisCount: 2, + crossAxisSpacing: KcSpacing.sm, + mainAxisSpacing: KcSpacing.sm, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + childAspectRatio: 1.6, + children: cards, + ); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/pages/finance_placeholder_page.dart b/kell_creations_apps/apps/kell_mobile/lib/pages/finance_placeholder_page.dart new file mode 100644 index 0000000..d132797 --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/pages/finance_placeholder_page.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class FinancePlaceholderPage extends StatelessWidget { + const FinancePlaceholderPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Center(child: Text('Finance page coming soon')); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/pages/integrations_placeholder_page.dart b/kell_creations_apps/apps/kell_mobile/lib/pages/integrations_placeholder_page.dart new file mode 100644 index 0000000..5c05217 --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/pages/integrations_placeholder_page.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class IntegrationsPlaceholderPage extends StatelessWidget { + const IntegrationsPlaceholderPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Center(child: Text('Integrations page coming soon')); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/lib/shell/mobile_shell.dart b/kell_creations_apps/apps/kell_mobile/lib/shell/mobile_shell.dart new file mode 100644 index 0000000..794c932 --- /dev/null +++ b/kell_creations_apps/apps/kell_mobile/lib/shell/mobile_shell.dart @@ -0,0 +1,230 @@ +import 'package:core/core.dart'; +import 'package:feature_inventory/feature_inventory.dart'; +import 'package:feature_orders/feature_orders.dart'; +import 'package:feature_policy/feature_policy.dart'; +import 'package:feature_wordpress/feature_wordpress.dart'; +import 'package:flutter/material.dart'; + +import '../composition/mobile_app_services.dart'; +import '../dashboard/application/dashboard_controller.dart'; +import '../dashboard/application/get_dashboard_summary.dart'; +import '../pages/dashboard_page.dart'; +import '../pages/finance_placeholder_page.dart'; +import '../pages/integrations_placeholder_page.dart'; + +/// The main shell for the mobile app. +/// +/// Uses a [Scaffold] with a [NavigationBar] (Material 3 bottom navigation) +/// to provide top-level section navigation. This is the mobile equivalent +/// of `kell_web`'s [AppShell] which uses a [NavigationRail]. +/// +/// Unlike the web app which uses named routes, the mobile shell uses +/// index-based tab switching with a stateful body to preserve tab state. +class MobileShell extends StatefulWidget { + const MobileShell({super.key}); + + @override + State createState() => _MobileShellState(); +} + +class _MobileShellState extends State { + int _selectedIndex = 0; + + static const _titles = ['Dashboard', 'Inventory', 'Products', 'Orders', 'More']; + + @override + Widget build(BuildContext context) { + final config = KcAppScope.configOf(context); + + return Scaffold( + appBar: AppBar( + title: Text(_titles[_selectedIndex]), + actions: [ + _EnvironmentBadge(environment: config.environment), + const SizedBox(width: 12), + ], + ), + body: _buildBody(context), + bottomNavigationBar: NavigationBar( + selectedIndex: _selectedIndex, + onDestinationSelected: (index) { + setState(() => _selectedIndex = index); + }, + destinations: const [ + NavigationDestination( + icon: Icon(Icons.dashboard_outlined), + selectedIcon: Icon(Icons.dashboard), + label: 'Dashboard', + ), + NavigationDestination( + icon: Icon(Icons.inventory_2_outlined), + selectedIcon: Icon(Icons.inventory_2), + label: 'Inventory', + ), + NavigationDestination( + icon: Icon(Icons.sell_outlined), + selectedIcon: Icon(Icons.sell), + label: 'Products', + ), + NavigationDestination( + icon: Icon(Icons.receipt_long_outlined), + selectedIcon: Icon(Icons.receipt_long), + label: 'Orders', + ), + NavigationDestination( + icon: Icon(Icons.more_horiz), + selectedIcon: Icon(Icons.more_horiz), + label: 'More', + ), + ], + ), + ); + } + + Widget _buildBody(BuildContext context) { + final services = KcAppScope.of(context); + + switch (_selectedIndex) { + case 0: + return MobileDashboardPage( + controller: DashboardController( + GetDashboardSummary( + inventoryRepository: services.inventoryRepository, + productPublishingRepository: services.productPublishingRepository, + ordersRepository: services.ordersRepository, + ), + ), + ); + case 1: + return InventoryPage( + repository: services.inventoryRepository, + onViewProduct: (_) { + // Cross-feature nav: switch to Products tab. + setState(() => _selectedIndex = 2); + }, + ); + case 2: + return ProductPublishingPage( + repository: services.productPublishingRepository, + onViewPolicy: () { + // Cross-feature nav: not directly reachable from bottom nav, + // but we can show it as a placeholder for now. + }, + ); + case 3: + return OrdersPage( + repository: services.ordersRepository, + onViewProduct: (_) { + setState(() => _selectedIndex = 2); + }, + onViewInventory: (_) { + setState(() => _selectedIndex = 1); + }, + ); + case 4: + return const _MorePage(); + default: + return const Center(child: Text('Unknown section')); + } + } +} + +/// A simple "More" page that provides access to less frequently used sections. +class _MorePage extends StatelessWidget { + const _MorePage(); + + @override + Widget build(BuildContext context) { + return ListView( + children: [ + ListTile( + leading: const Icon(Icons.attach_money), + title: const Text('Finance'), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + appBar: AppBar(title: const Text('Finance')), + body: const FinancePlaceholderPage(), + ), + ), + ); + }, + ), + ListTile( + leading: const Icon(Icons.policy), + title: const Text('Policy'), + trailing: const Icon(Icons.chevron_right), + onTap: () { + final services = KcAppScope.of(context); + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + appBar: AppBar(title: const Text('Policy')), + body: PolicyPage( + repository: services.policyRepository, + onViewRelatedPage: (_) {}, + ), + ), + ), + ); + }, + ), + ListTile( + leading: const Icon(Icons.hub), + title: const Text('Integrations'), + trailing: const Icon(Icons.chevron_right), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + appBar: AppBar(title: const Text('Integrations')), + body: const IntegrationsPlaceholderPage(), + ), + ), + ); + }, + ), + ], + ); + } +} + +/// A small coloured chip displayed in the [AppBar] that shows the current +/// runtime environment (e.g. "FAKE" or "WP"). +class _EnvironmentBadge extends StatelessWidget { + final KcAppEnvironment environment; + + const _EnvironmentBadge({required this.environment}); + + @override + Widget build(BuildContext context) { + final Color backgroundColor; + final Color foregroundColor; + + switch (environment) { + case KcAppEnvironment.fake: + backgroundColor = Colors.orange.shade100; + foregroundColor = Colors.orange.shade900; + case KcAppEnvironment.wordpress: + backgroundColor = Colors.green.shade100; + foregroundColor = Colors.green.shade900; + } + + return Center( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration(color: backgroundColor, borderRadius: BorderRadius.circular(4)), + child: Text( + environment.label, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: foregroundColor, + fontWeight: FontWeight.bold, + letterSpacing: 1.2, + ), + ), + ), + ); + } +} diff --git a/kell_creations_apps/apps/kell_mobile/pubspec.lock b/kell_creations_apps/apps/kell_mobile/pubspec.lock index ec64d1f..7f9e278 100644 --- a/kell_creations_apps/apps/kell_mobile/pubspec.lock +++ b/kell_creations_apps/apps/kell_mobile/pubspec.lock @@ -41,6 +41,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + core: + dependency: "direct main" + description: + path: "../../packages/core" + relative: true + source: path + version: "0.0.1" cupertino_icons: dependency: "direct main" description: @@ -49,6 +56,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.9" + design_system: + dependency: "direct main" + description: + path: "../../packages/design_system" + relative: true + source: path + version: "0.0.1" fake_async: dependency: transitive description: @@ -57,6 +71,34 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + feature_inventory: + dependency: "direct main" + description: + path: "../../packages/feature_inventory" + relative: true + source: path + version: "0.0.1" + feature_orders: + dependency: "direct main" + description: + path: "../../packages/feature_orders" + relative: true + source: path + version: "0.0.1" + feature_policy: + dependency: "direct main" + description: + path: "../../packages/feature_policy" + relative: true + source: path + version: "0.0.1" + feature_wordpress: + dependency: "direct main" + description: + path: "../../packages/feature_wordpress" + relative: true + source: path + version: "0.0.1" flutter: dependency: "direct main" description: flutter @@ -75,6 +117,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" leak_tracker: dependency: transitive description: @@ -192,6 +250,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.10" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -208,6 +274,14 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" sdks: - dart: ">=3.11.4 <4.0.0" + dart: ">=3.11.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/kell_creations_apps/apps/kell_mobile/pubspec.yaml b/kell_creations_apps/apps/kell_mobile/pubspec.yaml index 71d4cef..5400d2a 100644 --- a/kell_creations_apps/apps/kell_mobile/pubspec.yaml +++ b/kell_creations_apps/apps/kell_mobile/pubspec.yaml @@ -1,89 +1,36 @@ name: kell_mobile -description: "A new Flutter project." -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +description: "Kell Creations mobile operations platform." +publish_to: "none" -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. version: 1.0.0+1 environment: - sdk: ^3.11.4 + sdk: ^3.11.0 -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 + core: + path: ../../packages/core + design_system: + path: ../../packages/design_system + feature_inventory: + path: ../../packages/feature_inventory + feature_orders: + path: ../../packages/feature_orders + feature_policy: + path: ../../packages/feature_policy + feature_wordpress: + path: ../../packages/feature_wordpress + dev_dependencies: flutter_test: sdk: flutter - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. flutter_lints: ^6.0.0 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/to/asset-from-package - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/to/font-from-package diff --git a/kell_creations_apps/apps/kell_mobile/test/widget_test.dart b/kell_creations_apps/apps/kell_mobile/test/widget_test.dart index 8d05f63..9f2bf25 100644 --- a/kell_creations_apps/apps/kell_mobile/test/widget_test.dart +++ b/kell_creations_apps/apps/kell_mobile/test/widget_test.dart @@ -1,30 +1,77 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - +import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:kell_mobile/app.dart'; +import 'package:kell_mobile/composition/mobile_app_services.dart'; -import 'package:kell_mobile/main.dart'; +Widget _buildTestApp() { + const config = KcAppConfig( + environment: KcAppEnvironment.fake, + wcSiteUrl: '', + wcConsumerKey: '', + wcConsumerSecret: '', + ); + return KcAppScope( + services: MobileAppServices.fake(), + config: config, + child: const KellMobileApp(), + ); +} void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + testWidgets('mobile shell loads with dashboard tab', (WidgetTester tester) async { + await tester.pumpWidget(_buildTestApp()); + await tester.pumpAndSettle(); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + expect(find.text('Dashboard'), findsWidgets); + }); - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); + testWidgets('dashboard shows summary cards after loading', (WidgetTester tester) async { + await tester.pumpWidget(_buildTestApp()); + await tester.pumpAndSettle(); - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + expect(find.text('Overview'), findsOneWidget); + expect(find.text('Total Products'), findsOneWidget); + expect(find.text('In Stock'), findsOneWidget); + }); + + testWidgets('environment badge shows FAKE in fake mode', (WidgetTester tester) async { + await tester.pumpWidget(_buildTestApp()); + await tester.pumpAndSettle(); + + expect(find.text('FAKE'), findsOneWidget); + }); + + testWidgets('bottom navigation bar has 5 destinations', (WidgetTester tester) async { + await tester.pumpWidget(_buildTestApp()); + await tester.pumpAndSettle(); + + expect(find.byType(NavigationBar), findsOneWidget); + expect(find.byType(NavigationDestination), findsNWidgets(5)); + }); + + testWidgets('tapping Inventory tab switches content', (WidgetTester tester) async { + await tester.pumpWidget(_buildTestApp()); + await tester.pumpAndSettle(); + + // Tap the Inventory destination + await tester.tap(find.text('Inventory').last); + await tester.pumpAndSettle(); + + // The app bar title should change + expect(find.text('Inventory'), findsWidgets); + }); + + testWidgets('tapping More tab shows additional sections', (WidgetTester tester) async { + await tester.pumpWidget(_buildTestApp()); + await tester.pumpAndSettle(); + + // Tap the More destination + await tester.tap(find.text('More').last); + await tester.pumpAndSettle(); + + expect(find.text('Finance'), findsOneWidget); + expect(find.text('Policy'), findsOneWidget); + expect(find.text('Integrations'), findsOneWidget); }); } diff --git a/kell_creations_apps/apps/kell_web/pubspec.lock b/kell_creations_apps/apps/kell_web/pubspec.lock index 79fa06e..7f9e278 100644 --- a/kell_creations_apps/apps/kell_web/pubspec.lock +++ b/kell_creations_apps/apps/kell_web/pubspec.lock @@ -283,5 +283,5 @@ packages: source: hosted version: "1.1.1" sdks: - dart: ">=3.11.4 <4.0.0" + dart: ">=3.11.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/kell_creations_apps/apps/kell_web/pubspec.yaml b/kell_creations_apps/apps/kell_web/pubspec.yaml index 1d9050e..0b7d7ee 100644 --- a/kell_creations_apps/apps/kell_web/pubspec.yaml +++ b/kell_creations_apps/apps/kell_web/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ^3.11.4 + sdk: ^3.11.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/kell_creations_apps/packages/auth/pubspec.yaml b/kell_creations_apps/packages/auth/pubspec.yaml index 0e17fbd..fbe2103 100644 --- a/kell_creations_apps/packages/auth/pubspec.yaml +++ b/kell_creations_apps/packages/auth/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/core/pubspec.yaml b/kell_creations_apps/packages/core/pubspec.yaml index 124cfd9..ee11680 100644 --- a/kell_creations_apps/packages/core/pubspec.yaml +++ b/kell_creations_apps/packages/core/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/data/pubspec.yaml b/kell_creations_apps/packages/data/pubspec.yaml index dd26845..2b77bf1 100644 --- a/kell_creations_apps/packages/data/pubspec.yaml +++ b/kell_creations_apps/packages/data/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/design_system/pubspec.yaml b/kell_creations_apps/packages/design_system/pubspec.yaml index 2dc842b..a306d5f 100644 --- a/kell_creations_apps/packages/design_system/pubspec.yaml +++ b/kell_creations_apps/packages/design_system/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_finance/pubspec.yaml b/kell_creations_apps/packages/feature_finance/pubspec.yaml index f5353f7..37588b0 100644 --- a/kell_creations_apps/packages/feature_finance/pubspec.yaml +++ b/kell_creations_apps/packages/feature_finance/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_inventory/pubspec.yaml b/kell_creations_apps/packages/feature_inventory/pubspec.yaml index 4a9df9b..24490b2 100644 --- a/kell_creations_apps/packages/feature_inventory/pubspec.yaml +++ b/kell_creations_apps/packages/feature_inventory/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: "none" homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_mrp/pubspec.yaml b/kell_creations_apps/packages/feature_mrp/pubspec.yaml index c12d39c..ade08c7 100644 --- a/kell_creations_apps/packages/feature_mrp/pubspec.yaml +++ b/kell_creations_apps/packages/feature_mrp/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_orders/pubspec.yaml b/kell_creations_apps/packages/feature_orders/pubspec.yaml index 644cec0..f82bf53 100644 --- a/kell_creations_apps/packages/feature_orders/pubspec.yaml +++ b/kell_creations_apps/packages/feature_orders/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: "none" homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_policy/pubspec.yaml b/kell_creations_apps/packages/feature_policy/pubspec.yaml index b51b6de..aecf3ec 100644 --- a/kell_creations_apps/packages/feature_policy/pubspec.yaml +++ b/kell_creations_apps/packages/feature_policy/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: "none" homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_social/pubspec.yaml b/kell_creations_apps/packages/feature_social/pubspec.yaml index e5a9cf3..7fa255d 100644 --- a/kell_creations_apps/packages/feature_social/pubspec.yaml +++ b/kell_creations_apps/packages/feature_social/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/feature_wordpress/pubspec.yaml b/kell_creations_apps/packages/feature_wordpress/pubspec.yaml index 526e61a..fef4bf1 100644 --- a/kell_creations_apps/packages/feature_wordpress/pubspec.yaml +++ b/kell_creations_apps/packages/feature_wordpress/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: "none" homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: diff --git a/kell_creations_apps/packages/integrations/pubspec.yaml b/kell_creations_apps/packages/integrations/pubspec.yaml index 5dbdce0..7516eb5 100644 --- a/kell_creations_apps/packages/integrations/pubspec.yaml +++ b/kell_creations_apps/packages/integrations/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ^3.11.4 + sdk: ^3.11.0 flutter: ">=1.17.0" dependencies: