kasy-cli 1.18.0 → 1.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/bin/kasy.js +3 -1
  2. package/lib/commands/new.js +99 -105
  3. package/lib/commands/run.js +34 -6
  4. package/lib/scaffold/backends/firebase/setup-from-scratch.js +79 -0
  5. package/lib/utils/brand.js +1 -1
  6. package/lib/utils/i18n/messages-en.js +6 -0
  7. package/lib/utils/i18n/messages-es.js +6 -0
  8. package/lib/utils/i18n/messages-pt.js +6 -0
  9. package/package.json +1 -2
  10. package/templates/firebase/lib/components/kasy_date_picker.dart +1670 -331
  11. package/templates/firebase/lib/components/kasy_tabs.dart +111 -72
  12. package/templates/firebase/lib/components/kasy_text_area.dart +9 -4
  13. package/templates/firebase/lib/components/kasy_text_field.dart +96 -36
  14. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -2
  15. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +88 -35
  16. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +7 -43
  17. package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +118 -16
  18. package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +14 -20
  19. package/templates/firebase/lib/core/security/secured_storage.dart +56 -15
  20. package/templates/firebase/lib/core/theme/providers/theme_provider.dart +3 -0
  21. package/templates/firebase/lib/core/theme/web_background_sync.dart +3 -0
  22. package/templates/firebase/lib/core/theme/web_background_sync_web.dart +18 -0
  23. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +3 -6
  24. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +6 -0
  25. package/templates/firebase/lib/features/home/home_components_page.dart +3 -2
  26. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +457 -73
  27. package/templates/firebase/lib/features/home/home_page.dart +17 -40
  28. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +1 -16
  29. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +0 -4
  30. package/templates/firebase/lib/main.dart +34 -34
  31. package/templates/firebase/pubspec.yaml +1 -0
  32. package/templates/firebase/storage.cors.json +8 -0
  33. package/templates/firebase/web/index.html +15 -2
  34. package/templates/firebase/lib/core/bottom_menu/kasy_bart_navigation.dart +0 -22
@@ -236,20 +236,28 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
236
236
  title: 'DatePicker',
237
237
  variants: [
238
238
  ComponentPreviewVariant(
239
- label: 'Without label',
240
- builder: _buildDatePickerBasic,
239
+ label: 'Presentations',
240
+ builder: _buildDatePickerPresentations,
241
241
  ),
242
242
  ComponentPreviewVariant(
243
- label: 'With label',
244
- builder: _buildDatePickerWithLabel,
243
+ label: 'Range selection',
244
+ builder: _buildDatePickerRange,
245
245
  ),
246
246
  ComponentPreviewVariant(
247
- label: 'With min & max',
248
- builder: _buildDatePickerMinMax,
247
+ label: 'Multi-month',
248
+ builder: _buildDatePickerMultiMonth,
249
249
  ),
250
250
  ComponentPreviewVariant(
251
- label: 'Disabled',
252
- builder: _buildDatePickerDisabled,
251
+ label: 'Date formats',
252
+ builder: _buildDatePickerFormats,
253
+ ),
254
+ ComponentPreviewVariant(
255
+ label: 'Variants',
256
+ builder: _buildDatePickerVariants,
257
+ ),
258
+ ComponentPreviewVariant(
259
+ label: 'Field states',
260
+ builder: _buildDatePickerFieldStates,
253
261
  ),
254
262
  ],
255
263
  );
@@ -677,71 +685,6 @@ class _SidebarPreviewState extends State<_SidebarPreview> {
677
685
 
678
686
 
679
687
  // ─────────────────────────────────────────────────────────────────────────────
680
- // DatePicker — interactive demos
681
- // ─────────────────────────────────────────────────────────────────────────────
682
-
683
- Widget _buildDatePickerBasic(BuildContext context) =>
684
- const _DatePickerPreview(variant: _DatePickerVariant.basic);
685
-
686
- Widget _buildDatePickerWithLabel(BuildContext context) =>
687
- const _DatePickerPreview(variant: _DatePickerVariant.withLabel);
688
-
689
- Widget _buildDatePickerMinMax(BuildContext context) =>
690
- const _DatePickerPreview(variant: _DatePickerVariant.minMax);
691
-
692
- Widget _buildDatePickerDisabled(BuildContext context) =>
693
- const _DatePickerPreview(variant: _DatePickerVariant.disabled);
694
-
695
- enum _DatePickerVariant { basic, withLabel, minMax, disabled }
696
-
697
- class _DatePickerPreview extends StatefulWidget {
698
- const _DatePickerPreview({required this.variant});
699
-
700
- final _DatePickerVariant variant;
701
-
702
- @override
703
- State<_DatePickerPreview> createState() => _DatePickerPreviewState();
704
- }
705
-
706
- class _DatePickerPreviewState extends State<_DatePickerPreview> {
707
- DateTime? _date;
708
-
709
- @override
710
- Widget build(BuildContext context) {
711
- final DateTime today = DateTime.now();
712
- final DateTime minDate = today.subtract(const Duration(days: 30));
713
- final DateTime maxDate = today.add(const Duration(days: 60));
714
-
715
- return Padding(
716
- padding: const EdgeInsets.all(KasySpacing.md),
717
- child: switch (widget.variant) {
718
- _DatePickerVariant.basic => KasyDatePicker(
719
- value: _date,
720
- onChanged: (d) => setState(() => _date = d),
721
- ),
722
- _DatePickerVariant.withLabel => KasyDatePicker(
723
- label: 'Date of birth',
724
- value: _date,
725
- onChanged: (d) => setState(() => _date = d),
726
- ),
727
- _DatePickerVariant.minMax => KasyDatePicker(
728
- label: 'Appointment date',
729
- value: _date,
730
- minDate: minDate,
731
- maxDate: maxDate,
732
- onChanged: (d) => setState(() => _date = d),
733
- ),
734
- _DatePickerVariant.disabled => KasyDatePicker(
735
- label: 'Locked date',
736
- value: today,
737
- enabled: false,
738
- onChanged: (d) => setState(() => _date = d),
739
- ),
740
- },
741
- );
742
- }
743
- }
744
-
745
688
  // ─────────────────────────────────────────────────────────────────────────────
746
689
  // Tabs — interactive demos
747
690
  // ─────────────────────────────────────────────────────────────────────────────
@@ -8607,3 +8550,444 @@ class _SwipePreviewTile extends StatelessWidget {
8607
8550
  );
8608
8551
  }
8609
8552
  }
8553
+
8554
+ // ─────────────────────────────────────────────────────────────────────────────
8555
+ // DatePicker — Presentations
8556
+ // ─────────────────────────────────────────────────────────────────────────────
8557
+
8558
+ /// Maps the running app's locale onto a [KasyDatePickerLocale] preset so the
8559
+ /// preview demonstrates the same regional defaults (month names, week start,
8560
+ /// date format) the user would see in their own app.
8561
+ KasyDatePickerLocale _datePickerLocaleForApp(BuildContext context) {
8562
+ switch (Localizations.localeOf(context).languageCode) {
8563
+ case 'es':
8564
+ return KasyDatePickerLocale.es;
8565
+ case 'pt':
8566
+ return KasyDatePickerLocale.pt;
8567
+ default:
8568
+ return KasyDatePickerLocale.en;
8569
+ }
8570
+ }
8571
+
8572
+ Widget _buildDatePickerPresentations(BuildContext context) =>
8573
+ const _DatePickerPresentationsPreview();
8574
+
8575
+ class _DatePickerPresentationsPreview extends StatefulWidget {
8576
+ const _DatePickerPresentationsPreview();
8577
+
8578
+ @override
8579
+ State<_DatePickerPresentationsPreview> createState() =>
8580
+ _DatePickerPresentationsPreviewState();
8581
+ }
8582
+
8583
+ class _DatePickerPresentationsPreviewState
8584
+ extends State<_DatePickerPresentationsPreview> {
8585
+ DateTime? _popoverDate;
8586
+ DateTime? _dialogDate;
8587
+ DateTime? _sheetDate;
8588
+
8589
+ @override
8590
+ Widget build(BuildContext context) {
8591
+ final KasyDatePickerLocale locale = _datePickerLocaleForApp(context);
8592
+ return Column(
8593
+ mainAxisSize: MainAxisSize.min,
8594
+ crossAxisAlignment: CrossAxisAlignment.stretch,
8595
+ children: [
8596
+ KasyDatePicker(
8597
+ label: 'Popover',
8598
+ placeholder: 'Choose a date',
8599
+ value: _popoverDate,
8600
+ onChanged: (d) => setState(() => _popoverDate = d),
8601
+ locale: locale,
8602
+ ),
8603
+ const SizedBox(height: KasySpacing.lg),
8604
+ KasyDatePicker(
8605
+ label: 'Dialog',
8606
+ placeholder: 'Choose a date',
8607
+ value: _dialogDate,
8608
+ onChanged: (d) => setState(() => _dialogDate = d),
8609
+ presentation: KasyDatePickerPresentation.dialog,
8610
+ locale: locale,
8611
+ ),
8612
+ const SizedBox(height: KasySpacing.lg),
8613
+ KasyDatePicker(
8614
+ label: 'Bottom sheet',
8615
+ placeholder: 'Choose a date',
8616
+ value: _sheetDate,
8617
+ onChanged: (d) => setState(() => _sheetDate = d),
8618
+ presentation: KasyDatePickerPresentation.bottomSheet,
8619
+ locale: locale,
8620
+ ),
8621
+ ],
8622
+ );
8623
+ }
8624
+ }
8625
+
8626
+ // ─────────────────────────────────────────────────────────────────────────────
8627
+ // DatePicker — Range selection (Airbnb-style start + end)
8628
+ // ─────────────────────────────────────────────────────────────────────────────
8629
+
8630
+ Widget _buildDatePickerRange(BuildContext context) =>
8631
+ const _DatePickerRangePreview();
8632
+
8633
+ class _DatePickerRangePreview extends StatefulWidget {
8634
+ const _DatePickerRangePreview();
8635
+
8636
+ @override
8637
+ State<_DatePickerRangePreview> createState() =>
8638
+ _DatePickerRangePreviewState();
8639
+ }
8640
+
8641
+ class _DatePickerRangePreviewState extends State<_DatePickerRangePreview> {
8642
+ KasyDateRange? _stay;
8643
+ KasyDateRange? _project;
8644
+
8645
+ @override
8646
+ Widget build(BuildContext context) {
8647
+ final KasyDatePickerLocale locale = _datePickerLocaleForApp(context);
8648
+ return Column(
8649
+ mainAxisSize: MainAxisSize.min,
8650
+ crossAxisAlignment: CrossAxisAlignment.stretch,
8651
+ children: [
8652
+ // Classic stay-style picker: tap a check-in date, then a check-out
8653
+ // date. The calendar keeps the start highlighted while waiting for
8654
+ // the end, then auto-closes after the second tap.
8655
+ KasyDatePicker(
8656
+ label: 'Stay',
8657
+ placeholder: 'Check-in -> Check-out',
8658
+ description: 'Tap a start date, then an end date.',
8659
+ selectionMode: KasyDateSelectionMode.range,
8660
+ range: _stay,
8661
+ onRangeChanged: (r) => setState(() => _stay = r),
8662
+ locale: locale,
8663
+ ),
8664
+ const SizedBox(height: KasySpacing.lg),
8665
+ // Dialog presentation of the same range mode — handy on smaller
8666
+ // screens where the popover may not have enough room.
8667
+ KasyDatePicker(
8668
+ label: 'Project window (dialog)',
8669
+ placeholder: 'Pick start -> end',
8670
+ selectionMode: KasyDateSelectionMode.range,
8671
+ range: _project,
8672
+ onRangeChanged: (r) => setState(() => _project = r),
8673
+ presentation: KasyDatePickerPresentation.dialog,
8674
+ locale: locale,
8675
+ ),
8676
+ ],
8677
+ );
8678
+ }
8679
+ }
8680
+
8681
+ // ─────────────────────────────────────────────────────────────────────────────
8682
+ // DatePicker — Multi-month (2 or 3 months side by side, Airbnb desktop look)
8683
+ // ─────────────────────────────────────────────────────────────────────────────
8684
+
8685
+ Widget _buildDatePickerMultiMonth(BuildContext context) =>
8686
+ const _DatePickerMultiMonthPreview();
8687
+
8688
+ class _DatePickerMultiMonthPreview extends StatefulWidget {
8689
+ const _DatePickerMultiMonthPreview();
8690
+
8691
+ @override
8692
+ State<_DatePickerMultiMonthPreview> createState() =>
8693
+ _DatePickerMultiMonthPreviewState();
8694
+ }
8695
+
8696
+ class _DatePickerMultiMonthPreviewState
8697
+ extends State<_DatePickerMultiMonthPreview> {
8698
+ KasyDateRange? _stay;
8699
+ DateTime? _single;
8700
+
8701
+ @override
8702
+ Widget build(BuildContext context) {
8703
+ final KasyDatePickerLocale locale = _datePickerLocaleForApp(context);
8704
+ return Column(
8705
+ mainAxisSize: MainAxisSize.min,
8706
+ crossAxisAlignment: CrossAxisAlignment.stretch,
8707
+ children: [
8708
+ // 3-month layout combined with range selection — the classic
8709
+ // Airbnb / Booking desktop pattern for picking a stay.
8710
+ KasyDatePicker(
8711
+ label: '3 months + range',
8712
+ placeholder: 'Check-in -> Check-out',
8713
+ description: 'Pass monthsToShow: 3 to opt in. Best on wide canvases.',
8714
+ selectionMode: KasyDateSelectionMode.range,
8715
+ range: _stay,
8716
+ onRangeChanged: (r) => setState(() => _stay = r),
8717
+ monthsToShow: 3,
8718
+ locale: locale,
8719
+ ),
8720
+ const SizedBox(height: KasySpacing.lg),
8721
+ // 2-month layout in single-date mode — useful when there's enough
8722
+ // horizontal room but you still want one tap to commit.
8723
+ KasyDatePicker(
8724
+ label: '2 months + single date',
8725
+ placeholder: 'Pick a date',
8726
+ value: _single,
8727
+ onChanged: (d) => setState(() => _single = d),
8728
+ monthsToShow: 2,
8729
+ locale: locale,
8730
+ ),
8731
+ ],
8732
+ );
8733
+ }
8734
+ }
8735
+
8736
+ // ─────────────────────────────────────────────────────────────────────────────
8737
+ // DatePicker — Variants (filled, no backdrop, no suffix, no focus border)
8738
+ // ─────────────────────────────────────────────────────────────────────────────
8739
+
8740
+ Widget _buildDatePickerVariants(BuildContext context) =>
8741
+ const _DatePickerVariantsPreview();
8742
+
8743
+ class _DatePickerVariantsPreview extends StatefulWidget {
8744
+ const _DatePickerVariantsPreview();
8745
+
8746
+ @override
8747
+ State<_DatePickerVariantsPreview> createState() =>
8748
+ _DatePickerVariantsPreviewState();
8749
+ }
8750
+
8751
+ class _DatePickerVariantsPreviewState
8752
+ extends State<_DatePickerVariantsPreview> {
8753
+ DateTime? _filledDate;
8754
+ DateTime? _noBarrierDate;
8755
+ DateTime? _noSuffixDate;
8756
+ DateTime? _noFocusDate;
8757
+
8758
+ @override
8759
+ Widget build(BuildContext context) {
8760
+ final KasyDatePickerLocale locale = _datePickerLocaleForApp(context);
8761
+ return Column(
8762
+ mainAxisSize: MainAxisSize.min,
8763
+ crossAxisAlignment: CrossAxisAlignment.stretch,
8764
+ children: [
8765
+ // Filled / flat surface — matches KasyTextField.secondary, useful on
8766
+ // backgrounds where the default drop shadow feels heavy.
8767
+ KasyDatePicker(
8768
+ label: 'Filled (no shadow)',
8769
+ placeholder: 'Choose a date',
8770
+ value: _filledDate,
8771
+ onChanged: (d) => setState(() => _filledDate = d),
8772
+ variant: KasyTextFieldVariant.secondary,
8773
+ locale: locale,
8774
+ ),
8775
+ const SizedBox(height: KasySpacing.lg),
8776
+ // Popover without the dimmed backdrop — feels lighter / tooltip-like.
8777
+ KasyDatePicker(
8778
+ label: 'No backdrop',
8779
+ placeholder: 'Choose a date',
8780
+ description: 'Popover floats over the page without a dimmed scrim.',
8781
+ value: _noBarrierDate,
8782
+ onChanged: (d) => setState(() => _noBarrierDate = d),
8783
+ showBarrier: false,
8784
+ locale: locale,
8785
+ ),
8786
+ const SizedBox(height: KasySpacing.lg),
8787
+ // No calendar icon in the suffix slot.
8788
+ KasyDatePicker(
8789
+ label: 'No suffix icon',
8790
+ placeholder: 'Choose a date',
8791
+ value: _noSuffixDate,
8792
+ onChanged: (d) => setState(() => _noSuffixDate = d),
8793
+ showSuffix: false,
8794
+ locale: locale,
8795
+ ),
8796
+ const SizedBox(height: KasySpacing.lg),
8797
+ // Disables the blue focus outline while the calendar is open — quieter
8798
+ // affordance for very dense forms.
8799
+ KasyDatePicker(
8800
+ label: 'No focus border',
8801
+ placeholder: 'Choose a date',
8802
+ value: _noFocusDate,
8803
+ onChanged: (d) => setState(() => _noFocusDate = d),
8804
+ focusBorder: false,
8805
+ locale: locale,
8806
+ ),
8807
+ ],
8808
+ );
8809
+ }
8810
+ }
8811
+
8812
+ // ─────────────────────────────────────────────────────────────────────────────
8813
+ // DatePicker — Date formats
8814
+ // ─────────────────────────────────────────────────────────────────────────────
8815
+
8816
+ Widget _buildDatePickerFormats(BuildContext context) =>
8817
+ const _DatePickerFormatsPreview();
8818
+
8819
+ const List<String> _kEnglishMonthsLong = [
8820
+ 'January', 'February', 'March', 'April', 'May', 'June',
8821
+ 'July', 'August', 'September', 'October', 'November', 'December',
8822
+ ];
8823
+
8824
+ String _longUsFormat(DateTime d) =>
8825
+ '${_kEnglishMonthsLong[d.month - 1]} ${d.day}, ${d.year}';
8826
+
8827
+ class _DatePickerFormatsPreview extends StatefulWidget {
8828
+ const _DatePickerFormatsPreview();
8829
+
8830
+ @override
8831
+ State<_DatePickerFormatsPreview> createState() =>
8832
+ _DatePickerFormatsPreviewState();
8833
+ }
8834
+
8835
+ // Each tab maps to one preset (or to the "Auto" / "Custom" entries). The
8836
+ // `format` field is null for the two non-preset entries; the build method
8837
+ // branches on those.
8838
+ class _DatePickerFormatOption {
8839
+ const _DatePickerFormatOption({
8840
+ required this.label,
8841
+ required this.description,
8842
+ this.format,
8843
+ this.useAppLocale = false,
8844
+ this.customFormat,
8845
+ });
8846
+
8847
+ final String label;
8848
+ final String description;
8849
+ final KasyDateFormat? format;
8850
+ final bool useAppLocale;
8851
+ final String Function(DateTime)? customFormat;
8852
+ }
8853
+
8854
+ const List<_DatePickerFormatOption> _kDatePickerFormatOptions = [
8855
+ _DatePickerFormatOption(
8856
+ label: 'Auto',
8857
+ description: 'Follows the active app language (US, South American, …).',
8858
+ useAppLocale: true,
8859
+ ),
8860
+ _DatePickerFormatOption(
8861
+ label: 'US',
8862
+ description: 'MM / DD / YYYY — United States.',
8863
+ format: KasyDateFormat.us,
8864
+ ),
8865
+ _DatePickerFormatOption(
8866
+ label: 'South American',
8867
+ description: 'DD / MM / YYYY — South America, UK, Spain, Portugal.',
8868
+ format: KasyDateFormat.southAmerican,
8869
+ ),
8870
+ _DatePickerFormatOption(
8871
+ label: 'European',
8872
+ description: 'DD.MM.YYYY — Germany, Austria, Switzerland.',
8873
+ format: KasyDateFormat.european,
8874
+ ),
8875
+ _DatePickerFormatOption(
8876
+ label: 'ISO 8601',
8877
+ description: 'YYYY-MM-DD — international standard.',
8878
+ format: KasyDateFormat.iso,
8879
+ ),
8880
+ _DatePickerFormatOption(
8881
+ label: 'East Asian',
8882
+ description: 'YYYY / MM / DD — Japan, China, Korea.',
8883
+ format: KasyDateFormat.eastAsian,
8884
+ ),
8885
+ _DatePickerFormatOption(
8886
+ label: 'Custom',
8887
+ description: 'Any layout via the formatDate callback (long form here).',
8888
+ customFormat: _longUsFormat,
8889
+ ),
8890
+ ];
8891
+
8892
+ class _DatePickerFormatsPreviewState extends State<_DatePickerFormatsPreview> {
8893
+ int _tab = 0;
8894
+ DateTime _date = DateTime(2026, 5, 12);
8895
+
8896
+ @override
8897
+ Widget build(BuildContext context) {
8898
+ final _DatePickerFormatOption option = _kDatePickerFormatOptions[_tab];
8899
+ final KasyDatePickerLocale appLocale = _datePickerLocaleForApp(context);
8900
+
8901
+ return Column(
8902
+ mainAxisSize: MainAxisSize.min,
8903
+ crossAxisAlignment: CrossAxisAlignment.stretch,
8904
+ children: [
8905
+ // Secondary (underline) variant keeps the format switcher visually
8906
+ // light on phones — pill tabs were too bulky for a switch with many
8907
+ // labels and hug mode lets the row scroll horizontally when needed.
8908
+ KasyTabs(
8909
+ tabs: [
8910
+ for (final o in _kDatePickerFormatOptions) o.label,
8911
+ ],
8912
+ selectedIndex: _tab,
8913
+ onTabSelected: (i) => setState(() => _tab = i),
8914
+ variant: KasyTabsVariant.secondary,
8915
+ ),
8916
+ const SizedBox(height: KasySpacing.lg),
8917
+ KasyDatePicker(
8918
+ label: option.label,
8919
+ description: option.description,
8920
+ value: _date,
8921
+ onChanged: (d) => setState(() => _date = d),
8922
+ locale: option.useAppLocale ? appLocale : KasyDatePickerLocale.en,
8923
+ dateFormat: option.format,
8924
+ formatDate: option.customFormat,
8925
+ ),
8926
+ ],
8927
+ );
8928
+ }
8929
+ }
8930
+
8931
+ // ─────────────────────────────────────────────────────────────────────────────
8932
+ // DatePicker — Field states
8933
+ // ─────────────────────────────────────────────────────────────────────────────
8934
+
8935
+ Widget _buildDatePickerFieldStates(BuildContext context) =>
8936
+ const _DatePickerFieldStatesPreview();
8937
+
8938
+ const List<String> _kEnglishMonthAbbrev = [
8939
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
8940
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
8941
+ ];
8942
+
8943
+ String _abbrevFormat(DateTime d) =>
8944
+ '${_kEnglishMonthAbbrev[d.month - 1]} ${d.day}, ${d.year}';
8945
+
8946
+ class _DatePickerFieldStatesPreview extends StatefulWidget {
8947
+ const _DatePickerFieldStatesPreview();
8948
+
8949
+ @override
8950
+ State<_DatePickerFieldStatesPreview> createState() =>
8951
+ _DatePickerFieldStatesPreviewState();
8952
+ }
8953
+
8954
+ class _DatePickerFieldStatesPreviewState
8955
+ extends State<_DatePickerFieldStatesPreview> {
8956
+ DateTime? _deadline;
8957
+ DateTime? _shipDate;
8958
+ final DateTime _disabledDate = DateTime(2026, 6, 15);
8959
+
8960
+ @override
8961
+ Widget build(BuildContext context) {
8962
+ return Column(
8963
+ mainAxisSize: MainAxisSize.min,
8964
+ crossAxisAlignment: CrossAxisAlignment.stretch,
8965
+ children: [
8966
+ KasyDatePicker(
8967
+ label: 'Deadline',
8968
+ placeholder: 'Select a deadline',
8969
+ showRequiredIndicator: true,
8970
+ description: 'Required for the project timeline.',
8971
+ value: _deadline,
8972
+ onChanged: (d) => setState(() => _deadline = d),
8973
+ ),
8974
+ const SizedBox(height: KasySpacing.lg),
8975
+ KasyDatePicker(
8976
+ label: 'Ship date',
8977
+ placeholder: 'Choose a date',
8978
+ errorText: 'Please select a valid ship date.',
8979
+ value: _shipDate,
8980
+ onChanged: (d) => setState(() => _shipDate = d),
8981
+ ),
8982
+ const SizedBox(height: KasySpacing.lg),
8983
+ KasyDatePicker(
8984
+ label: 'Disabled date',
8985
+ enabled: false,
8986
+ value: _disabledDate,
8987
+ formatDate: _abbrevFormat,
8988
+ description: 'The date cannot be changed when the order is locked.',
8989
+ ),
8990
+ ],
8991
+ );
8992
+ }
8993
+ }
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
2
2
  import 'package:flutter_riverpod/flutter_riverpod.dart';
3
3
  import 'package:kasy_kit/components/kasy_app_bar.dart';
4
4
  import 'package:kasy_kit/core/bottom_menu/bart_inner_paths.dart';
5
- import 'package:kasy_kit/core/bottom_menu/kasy_bart_navigation.dart';
6
5
  import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
7
6
  import 'package:kasy_kit/core/states/components/maybe_ask_rating.dart';
8
7
  import 'package:kasy_kit/core/states/components/maybe_show_update_bottom_sheet.dart';
@@ -60,13 +59,9 @@ class HomePage extends ConsumerWidget {
60
59
  gradient: _featuresGradient(context, isDark),
61
60
  onOpen: () {
62
61
  KasyHaptics.medium(context);
63
- Navigator.of(context)
64
- .pushNamed(bartInnerPath('home', 'features'))
65
- .then((_) {
66
- if (context.mounted) {
67
- kasyShowBottomBar(context);
68
- }
69
- });
62
+ Navigator.of(context).pushNamed(
63
+ bartInnerPath('home', 'features'),
64
+ );
70
65
  },
71
66
  );
72
67
  final componentsCard = _DashboardGradientCard(
@@ -79,15 +74,9 @@ class HomePage extends ConsumerWidget {
79
74
  gradient: _componentsGradient(context, isDark),
80
75
  onOpen: () {
81
76
  KasyHaptics.medium(context);
82
- Navigator.of(context)
83
- .pushNamed(
84
- bartInnerPath('home', 'components'),
85
- )
86
- .then((_) {
87
- if (context.mounted) {
88
- kasyShowBottomBar(context);
89
- }
90
- });
77
+ Navigator.of(context).pushNamed(
78
+ bartInnerPath('home', 'components'),
79
+ );
91
80
  },
92
81
  );
93
82
  if (isWide) {
@@ -135,10 +124,7 @@ class HomePage extends ConsumerWidget {
135
124
  }
136
125
 
137
126
  /// Soft product-style gradients (calm blues / lilac); same geometry, distinct hue.
138
- static LinearGradient _componentsGradient(
139
- BuildContext context,
140
- bool isDark,
141
- ) {
127
+ static LinearGradient _componentsGradient(BuildContext context, bool isDark) {
142
128
  if (isDark) {
143
129
  return LinearGradient(
144
130
  begin: Alignment.topLeft,
@@ -153,11 +139,7 @@ class HomePage extends ConsumerWidget {
153
139
  return const LinearGradient(
154
140
  begin: Alignment.topLeft,
155
141
  end: Alignment.bottomRight,
156
- colors: [
157
- Color(0xFFDCE6FF),
158
- Color(0xFFF0F6FF),
159
- Color(0xFFE2E8F6),
160
- ],
142
+ colors: [Color(0xFFDCE6FF), Color(0xFFF0F6FF), Color(0xFFE2E8F6)],
161
143
  );
162
144
  }
163
145
 
@@ -176,11 +158,7 @@ class HomePage extends ConsumerWidget {
176
158
  return const LinearGradient(
177
159
  begin: Alignment.topLeft,
178
160
  end: Alignment.bottomRight,
179
- colors: [
180
- Color(0xFFE2D8FC),
181
- Color(0xFFF5ECFA),
182
- Color(0xFFDCE8FF),
183
- ],
161
+ colors: [Color(0xFFE2D8FC), Color(0xFFF5ECFA), Color(0xFFDCE8FF)],
184
162
  );
185
163
  }
186
164
  }
@@ -205,22 +183,23 @@ class _DashboardGradientCard extends StatelessWidget {
205
183
  @override
206
184
  Widget build(BuildContext context) {
207
185
  final bool isDark = Theme.of(context).brightness == Brightness.dark;
208
- final Color titleColor =
209
- isDark ? Colors.white : const Color(0xFF1A1D26);
186
+ final Color titleColor = isDark ? Colors.white : const Color(0xFF1A1D26);
210
187
  final Color subtitleColor = titleColor.withValues(alpha: 0.62);
211
188
  final Color arrowCircleColor = isDark
212
189
  ? Colors.black.withValues(alpha: 0.38)
213
190
  : Colors.white.withValues(alpha: 0.82);
214
- final Color arrowIconColor =
215
- isDark ? Colors.white : const Color(0xFF1A1D26);
191
+ final Color arrowIconColor = isDark
192
+ ? Colors.white
193
+ : const Color(0xFF1A1D26);
216
194
  final Color countBadgeFill = isDark
217
195
  ? Colors.white.withValues(alpha: 0.14)
218
196
  : Colors.white.withValues(alpha: 0.38);
219
197
  final Color countBadgeBorder = isDark
220
198
  ? Colors.white.withValues(alpha: 0.24)
221
199
  : Colors.black.withValues(alpha: 0.06);
222
- final Color countBadgeLabelColor =
223
- isDark ? Colors.white.withValues(alpha: 0.94) : countBadgeTextColor;
200
+ final Color countBadgeLabelColor = isDark
201
+ ? Colors.white.withValues(alpha: 0.94)
202
+ : countBadgeTextColor;
224
203
  final Color cardOutlineColor = context.colors.outline.withValues(
225
204
  alpha: isDark ? 0.34 : 0.30,
226
205
  );
@@ -246,9 +225,7 @@ class _DashboardGradientCard extends StatelessWidget {
246
225
  child: DecoratedBox(
247
226
  decoration: BoxDecoration(
248
227
  color: countBadgeFill,
249
- borderRadius: BorderRadius.circular(
250
- KasyRadius.full,
251
- ),
228
+ borderRadius: BorderRadius.circular(KasyRadius.full),
252
229
  border: Border.all(color: countBadgeBorder),
253
230
  ),
254
231
  child: Padding(