@mohasinac/appkit 4.21.0 → 4.23.0

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 (102) hide show
  1. package/dist/_internal/server/features/checkout/locked-lines.js +12 -2
  2. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  3. package/dist/_internal/server/features/stores/adapters.js +5 -0
  4. package/dist/_internal/server/jobs/core/auctionSettlement.js +10 -4
  5. package/dist/_internal/server/jobs/core/offerExpiry.js +10 -2
  6. package/dist/_internal/server/jobs/core/payoutBatch.js +15 -3
  7. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  8. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  9. package/dist/_internal/shared/history/index.d.ts +1 -1
  10. package/dist/_internal/shared/history/types.d.ts +16 -0
  11. package/dist/client.d.ts +7 -0
  12. package/dist/client.js +4 -0
  13. package/dist/constants/field-names.d.ts +12 -0
  14. package/dist/constants/field-names.js +12 -0
  15. package/dist/features/account/components/AddressForm.js +45 -29
  16. package/dist/features/admin/actions/admin-actions.js +13 -3
  17. package/dist/features/admin/components/AdminBidsView.js +7 -1
  18. package/dist/features/admin/components/AdminBundleEditorView.js +3 -0
  19. package/dist/features/admin/components/AdminScammerEditorView.d.ts +5 -1
  20. package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
  21. package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +5 -1
  22. package/dist/features/admin/components/AdminSupportTicketDetailView.js +3 -2
  23. package/dist/features/auctions/components/PlaceBidFormClient.js +3 -1
  24. package/dist/features/auctions/repository/bid.repository.d.ts +21 -8
  25. package/dist/features/auctions/repository/bid.repository.js +57 -33
  26. package/dist/features/auctions/schemas/firestore.d.ts +27 -0
  27. package/dist/features/auctions/schemas/firestore.js +21 -0
  28. package/dist/features/auth/schemas/index.d.ts +2 -2
  29. package/dist/features/catalogue/actions/approve-catalogue-listing.js +7 -6
  30. package/dist/features/catalogue/actions/list-from-catalogue.js +2 -2
  31. package/dist/features/catalogue/actions/submit-for-approval.js +1 -4
  32. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +5 -1
  33. package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -1
  34. package/dist/features/catalogue/repository/catalogue.repository.d.ts +23 -0
  35. package/dist/features/catalogue/repository/catalogue.repository.js +28 -1
  36. package/dist/features/catalogue/schemas/firestore.d.ts +19 -0
  37. package/dist/features/catalogue/schemas/firestore.js +12 -0
  38. package/dist/features/categories/schemas/index.d.ts +2 -2
  39. package/dist/features/layout/BottomActions.js +18 -1
  40. package/dist/features/layout/BottomActionsContext.d.ts +19 -0
  41. package/dist/features/layout/BottomActionsContext.js +37 -0
  42. package/dist/features/layout/hooks/useBottomActions.d.ts +10 -0
  43. package/dist/features/layout/hooks/useBottomActions.js +48 -11
  44. package/dist/features/layout/hooks/useFormBottomActions.d.ts +57 -0
  45. package/dist/features/layout/hooks/useFormBottomActions.js +89 -0
  46. package/dist/features/layout/index.d.ts +2 -0
  47. package/dist/features/layout/index.js +1 -0
  48. package/dist/features/messages/schemas/index.d.ts +6 -6
  49. package/dist/features/orders/schemas/index.d.ts +12 -12
  50. package/dist/features/payments/repository/payout.repository.d.ts +18 -5
  51. package/dist/features/payments/repository/payout.repository.js +45 -19
  52. package/dist/features/payments/schemas/firestore.d.ts +33 -1
  53. package/dist/features/payments/schemas/firestore.js +39 -0
  54. package/dist/features/payments/schemas/index.d.ts +12 -12
  55. package/dist/features/products/components/MakeOfferButton.js +5 -3
  56. package/dist/features/products/schemas/index.d.ts +2 -2
  57. package/dist/features/promotions/schemas/index.d.ts +12 -12
  58. package/dist/features/scams/repository/scammer.repository.d.ts +18 -1
  59. package/dist/features/scams/repository/scammer.repository.js +24 -2
  60. package/dist/features/scams/schemas/firestore.d.ts +24 -0
  61. package/dist/features/scams/schemas/firestore.js +30 -0
  62. package/dist/features/shell/QuickFormDrawer.js +15 -1
  63. package/dist/features/shell/SectionForm.d.ts +16 -2
  64. package/dist/features/shell/SectionForm.js +28 -2
  65. package/dist/features/shipments/components/AdminShipmentEditorView.js +5 -2
  66. package/dist/features/shipments/repository/shipments.repository.d.ts +18 -0
  67. package/dist/features/shipments/repository/shipments.repository.js +23 -1
  68. package/dist/features/shipments/schemas/firestore.d.ts +14 -0
  69. package/dist/features/shipments/schemas/firestore.js +19 -0
  70. package/dist/features/status-history/components/RecordStatusTimeline.d.ts +12 -0
  71. package/dist/features/status-history/components/RecordStatusTimeline.js +90 -0
  72. package/dist/features/status-history/index.d.ts +2 -0
  73. package/dist/features/status-history/index.js +1 -0
  74. package/dist/features/stores/repository/store.repository.d.ts +30 -2
  75. package/dist/features/stores/repository/store.repository.js +43 -4
  76. package/dist/features/stores/schemas/firestore.d.ts +29 -0
  77. package/dist/features/stores/schemas/firestore.js +31 -0
  78. package/dist/features/support/repository/support.repository.d.ts +47 -3
  79. package/dist/features/support/repository/support.repository.js +72 -12
  80. package/dist/features/support/schemas/firestore.d.ts +38 -1
  81. package/dist/features/support/schemas/firestore.js +34 -0
  82. package/dist/features/support/schemas/index.d.ts +2 -2
  83. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +90 -0
  84. package/dist/index.d.ts +7 -1
  85. package/dist/index.js +16 -1
  86. package/dist/schemas/registry.d.ts +32 -32
  87. package/dist/seed/bids-seed-data.js +8 -0
  88. package/dist/ui/components/Form.d.ts +22 -1
  89. package/dist/ui/components/Form.js +30 -2
  90. package/dist/ui/components/Modal.js +4 -3
  91. package/dist/ui/components/SideDrawer.js +5 -4
  92. package/dist/ui/components/overlay-context.d.ts +21 -0
  93. package/dist/ui/components/overlay-context.js +25 -0
  94. package/dist/ui/forms/FormErrorList.d.ts +29 -0
  95. package/dist/ui/forms/FormErrorList.js +36 -0
  96. package/dist/ui/forms/FormErrorSummary.d.ts +8 -0
  97. package/dist/ui/forms/FormErrorSummary.js +15 -8
  98. package/dist/ui/forms/FormShell.d.ts +25 -1
  99. package/dist/ui/forms/FormShell.js +39 -4
  100. package/dist/ui/forms/index.d.ts +2 -0
  101. package/dist/ui/forms/index.js +1 -0
  102. package/package.json +1 -1
@@ -1234,7 +1234,7 @@ export declare const SCHEMAS: {
1234
1234
  }, import("zod").ZodTypeAny, "passthrough"> | undefined;
1235
1235
  featuredPriority?: number | undefined;
1236
1236
  isBrand?: boolean | undefined;
1237
- createdByType?: "store" | "admin" | undefined;
1237
+ createdByType?: "admin" | "store" | undefined;
1238
1238
  createdByStoreId?: string | undefined;
1239
1239
  itemCode?: string | undefined;
1240
1240
  brandWebsite?: string | undefined;
@@ -1333,7 +1333,7 @@ export declare const SCHEMAS: {
1333
1333
  }, import("zod").ZodTypeAny, "passthrough"> | undefined;
1334
1334
  featuredPriority?: number | undefined;
1335
1335
  isBrand?: boolean | undefined;
1336
- createdByType?: "store" | "admin" | undefined;
1336
+ createdByType?: "admin" | "store" | undefined;
1337
1337
  createdByStoreId?: string | undefined;
1338
1338
  itemCode?: string | undefined;
1339
1339
  brandWebsite?: string | undefined;
@@ -1406,7 +1406,7 @@ export declare const SCHEMAS: {
1406
1406
  }>;
1407
1407
  }, "strip", import("zod").ZodTypeAny, {
1408
1408
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
1409
- scope: "admin" | "seller";
1409
+ scope: "seller" | "admin";
1410
1410
  name: string;
1411
1411
  discount: {
1412
1412
  value: number;
@@ -1425,7 +1425,7 @@ export declare const SCHEMAS: {
1425
1425
  description?: string | undefined;
1426
1426
  }, {
1427
1427
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
1428
- scope: "admin" | "seller";
1428
+ scope: "seller" | "admin";
1429
1429
  name: string;
1430
1430
  discount: {
1431
1431
  value: number;
@@ -1485,7 +1485,7 @@ export declare const SCHEMAS: {
1485
1485
  couponId: string;
1486
1486
  couponSnapshot: {
1487
1487
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
1488
- scope: "admin" | "seller";
1488
+ scope: "seller" | "admin";
1489
1489
  name: string;
1490
1490
  discount: {
1491
1491
  value: number;
@@ -1525,7 +1525,7 @@ export declare const SCHEMAS: {
1525
1525
  couponId: string;
1526
1526
  couponSnapshot: {
1527
1527
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
1528
- scope: "admin" | "seller";
1528
+ scope: "seller" | "admin";
1529
1529
  name: string;
1530
1530
  discount: {
1531
1531
  value: number;
@@ -1596,7 +1596,7 @@ export declare const SCHEMAS: {
1596
1596
  body: string;
1597
1597
  isRead: boolean;
1598
1598
  senderId: string;
1599
- senderRole: "seller" | "buyer";
1599
+ senderRole: "buyer" | "seller";
1600
1600
  sentAt: string | Date | import("zod").objectOutputType<{
1601
1601
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
1602
1602
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -1606,7 +1606,7 @@ export declare const SCHEMAS: {
1606
1606
  body: string;
1607
1607
  isRead: boolean;
1608
1608
  senderId: string;
1609
- senderRole: "seller" | "buyer";
1609
+ senderRole: "buyer" | "seller";
1610
1610
  sentAt: string | Date | import("zod").objectInputType<{
1611
1611
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
1612
1612
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -1644,7 +1644,7 @@ export declare const SCHEMAS: {
1644
1644
  body: string;
1645
1645
  isRead: boolean;
1646
1646
  senderId: string;
1647
- senderRole: "seller" | "buyer";
1647
+ senderRole: "buyer" | "seller";
1648
1648
  sentAt: string | Date | import("zod").objectOutputType<{
1649
1649
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
1650
1650
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -1677,7 +1677,7 @@ export declare const SCHEMAS: {
1677
1677
  body: string;
1678
1678
  isRead: boolean;
1679
1679
  senderId: string;
1680
- senderRole: "seller" | "buyer";
1680
+ senderRole: "buyer" | "seller";
1681
1681
  sentAt: string | Date | import("zod").objectInputType<{
1682
1682
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
1683
1683
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -1838,7 +1838,7 @@ export declare const SCHEMAS: {
1838
1838
  code: string;
1839
1839
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
1840
1840
  id: string;
1841
- scope: "admin" | "seller";
1841
+ scope: "seller" | "admin";
1842
1842
  name: string;
1843
1843
  createdAt: string | Date | import("zod").objectOutputType<{
1844
1844
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
@@ -1896,7 +1896,7 @@ export declare const SCHEMAS: {
1896
1896
  code: string;
1897
1897
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
1898
1898
  id: string;
1899
- scope: "admin" | "seller";
1899
+ scope: "seller" | "admin";
1900
1900
  name: string;
1901
1901
  createdAt: string | Date | import("zod").objectInputType<{
1902
1902
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
@@ -2372,14 +2372,14 @@ export declare const SCHEMAS: {
2372
2372
  code: string;
2373
2373
  type: "auto" | "coupon" | "deal";
2374
2374
  discountAmount: number;
2375
- scope?: "admin" | "seller" | undefined;
2375
+ scope?: "seller" | "admin" | undefined;
2376
2376
  storeId?: string | undefined;
2377
2377
  couponId?: string | undefined;
2378
2378
  }, {
2379
2379
  code: string;
2380
2380
  type: "auto" | "coupon" | "deal";
2381
2381
  discountAmount: number;
2382
- scope?: "admin" | "seller" | undefined;
2382
+ scope?: "seller" | "admin" | undefined;
2383
2383
  storeId?: string | undefined;
2384
2384
  couponId?: string | undefined;
2385
2385
  }>, "many">>;
@@ -2510,9 +2510,9 @@ export declare const SCHEMAS: {
2510
2510
  overrideReason: import("zod").ZodOptional<import("zod").ZodString>;
2511
2511
  }, "strip", import("zod").ZodTypeAny, {
2512
2512
  type: "full" | "partial";
2513
+ reason: string;
2513
2514
  amount: number;
2514
2515
  refundId: string;
2515
- reason: string;
2516
2516
  refundedAt: string | Date | import("zod").objectOutputType<{
2517
2517
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2518
2518
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -2525,9 +2525,9 @@ export declare const SCHEMAS: {
2525
2525
  overrideReason?: string | undefined;
2526
2526
  }, {
2527
2527
  type: "full" | "partial";
2528
+ reason: string;
2528
2529
  amount: number;
2529
2530
  refundId: string;
2530
- reason: string;
2531
2531
  refundedAt: string | Date | import("zod").objectInputType<{
2532
2532
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2533
2533
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -2668,7 +2668,7 @@ export declare const SCHEMAS: {
2668
2668
  code: string;
2669
2669
  type: "auto" | "coupon" | "deal";
2670
2670
  discountAmount: number;
2671
- scope?: "admin" | "seller" | undefined;
2671
+ scope?: "seller" | "admin" | undefined;
2672
2672
  storeId?: string | undefined;
2673
2673
  couponId?: string | undefined;
2674
2674
  }[] | undefined;
@@ -2700,9 +2700,9 @@ export declare const SCHEMAS: {
2700
2700
  paymentBatchId?: string | undefined;
2701
2701
  refunds?: {
2702
2702
  type: "full" | "partial";
2703
+ reason: string;
2703
2704
  amount: number;
2704
2705
  refundId: string;
2705
- reason: string;
2706
2706
  refundedAt: string | Date | import("zod").objectOutputType<{
2707
2707
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2708
2708
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -2816,7 +2816,7 @@ export declare const SCHEMAS: {
2816
2816
  code: string;
2817
2817
  type: "auto" | "coupon" | "deal";
2818
2818
  discountAmount: number;
2819
- scope?: "admin" | "seller" | undefined;
2819
+ scope?: "seller" | "admin" | undefined;
2820
2820
  storeId?: string | undefined;
2821
2821
  couponId?: string | undefined;
2822
2822
  }[] | undefined;
@@ -2848,9 +2848,9 @@ export declare const SCHEMAS: {
2848
2848
  paymentBatchId?: string | undefined;
2849
2849
  refunds?: {
2850
2850
  type: "full" | "partial";
2851
+ reason: string;
2851
2852
  amount: number;
2852
2853
  refundId: string;
2853
- reason: string;
2854
2854
  refundedAt: string | Date | import("zod").objectInputType<{
2855
2855
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2856
2856
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -2944,20 +2944,20 @@ export declare const SCHEMAS: {
2944
2944
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2945
2945
  }, import("zod").ZodTypeAny, "passthrough">>]>;
2946
2946
  }, "strip", import("zod").ZodTypeAny, {
2947
+ reason: string;
2947
2948
  orderId: string;
2948
2949
  refundId: string;
2949
2950
  refundedAmount: number;
2950
2951
  deductedAmount: number;
2951
- reason: string;
2952
2952
  appliedAt: string | Date | import("zod").objectOutputType<{
2953
2953
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2954
2954
  }, import("zod").ZodTypeAny, "passthrough">;
2955
2955
  }, {
2956
+ reason: string;
2956
2957
  orderId: string;
2957
2958
  refundId: string;
2958
2959
  refundedAmount: number;
2959
2960
  deductedAmount: number;
2960
- reason: string;
2961
2961
  appliedAt: string | Date | import("zod").objectInputType<{
2962
2962
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2963
2963
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -2999,6 +2999,9 @@ export declare const SCHEMAS: {
2999
2999
  platformFeeRate: number;
3000
3000
  paymentMethod: "bank_transfer" | "upi";
3001
3001
  orderIds: string[];
3002
+ processedAt?: string | Date | import("zod").objectOutputType<{
3003
+ toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3004
+ }, import("zod").ZodTypeAny, "passthrough"> | undefined;
3002
3005
  bankAccount?: {
3003
3006
  accountHolderName: string;
3004
3007
  accountNumberMasked: string;
@@ -3014,19 +3017,16 @@ export declare const SCHEMAS: {
3014
3017
  gstRate?: number | undefined;
3015
3018
  isAutomatic?: boolean | undefined;
3016
3019
  refundDeductions?: {
3020
+ reason: string;
3017
3021
  orderId: string;
3018
3022
  refundId: string;
3019
3023
  refundedAmount: number;
3020
3024
  deductedAmount: number;
3021
- reason: string;
3022
3025
  appliedAt: string | Date | import("zod").objectOutputType<{
3023
3026
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3024
3027
  }, import("zod").ZodTypeAny, "passthrough">;
3025
3028
  }[] | undefined;
3026
3029
  netAmount?: number | undefined;
3027
- processedAt?: string | Date | import("zod").objectOutputType<{
3028
- toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3029
- }, import("zod").ZodTypeAny, "passthrough"> | undefined;
3030
3030
  }, {
3031
3031
  currency: string;
3032
3032
  status: "paid" | "failed" | "pending" | "processing";
@@ -3049,6 +3049,9 @@ export declare const SCHEMAS: {
3049
3049
  platformFeeRate: number;
3050
3050
  paymentMethod: "bank_transfer" | "upi";
3051
3051
  orderIds: string[];
3052
+ processedAt?: string | Date | import("zod").objectInputType<{
3053
+ toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3054
+ }, import("zod").ZodTypeAny, "passthrough"> | undefined;
3052
3055
  bankAccount?: {
3053
3056
  accountHolderName: string;
3054
3057
  accountNumberMasked: string;
@@ -3064,19 +3067,16 @@ export declare const SCHEMAS: {
3064
3067
  gstRate?: number | undefined;
3065
3068
  isAutomatic?: boolean | undefined;
3066
3069
  refundDeductions?: {
3070
+ reason: string;
3067
3071
  orderId: string;
3068
3072
  refundId: string;
3069
3073
  refundedAmount: number;
3070
3074
  deductedAmount: number;
3071
- reason: string;
3072
3075
  appliedAt: string | Date | import("zod").objectInputType<{
3073
3076
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3074
3077
  }, import("zod").ZodTypeAny, "passthrough">;
3075
3078
  }[] | undefined;
3076
3079
  netAmount?: number | undefined;
3077
- processedAt?: string | Date | import("zod").objectInputType<{
3078
- toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3079
- }, import("zod").ZodTypeAny, "passthrough"> | undefined;
3080
3080
  }>;
3081
3081
  readonly reviews: import("zod").ZodObject<{
3082
3082
  createdAt: import("zod").ZodUnion<[import("zod").ZodDate, import("zod").ZodString, import("zod").ZodObject<{
@@ -4192,6 +4192,7 @@ export declare const SCHEMAS: {
4192
4192
  closedAt?: string | Date | import("zod").objectOutputType<{
4193
4193
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
4194
4194
  }, import("zod").ZodTypeAny, "passthrough"> | undefined;
4195
+ assignedToName?: string | undefined;
4195
4196
  relatedParties?: {
4196
4197
  storeId?: string | undefined;
4197
4198
  userId?: string | undefined;
@@ -4199,7 +4200,6 @@ export declare const SCHEMAS: {
4199
4200
  productId?: string | undefined;
4200
4201
  bidId?: string | undefined;
4201
4202
  } | undefined;
4202
- assignedToName?: string | undefined;
4203
4203
  internalNotes?: string | undefined;
4204
4204
  }, {
4205
4205
  status: "closed" | "open" | "resolved" | "in_progress" | "waiting_on_user";
@@ -4235,6 +4235,7 @@ export declare const SCHEMAS: {
4235
4235
  closedAt?: string | Date | import("zod").objectInputType<{
4236
4236
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
4237
4237
  }, import("zod").ZodTypeAny, "passthrough"> | undefined;
4238
+ assignedToName?: string | undefined;
4238
4239
  relatedParties?: {
4239
4240
  storeId?: string | undefined;
4240
4241
  userId?: string | undefined;
@@ -4242,7 +4243,6 @@ export declare const SCHEMAS: {
4242
4243
  productId?: string | undefined;
4243
4244
  bidId?: string | undefined;
4244
4245
  } | undefined;
4245
- assignedToName?: string | undefined;
4246
4246
  internalNotes?: string | undefined;
4247
4247
  }>;
4248
4248
  };
@@ -35,6 +35,14 @@ const BIDDER_NAMES = {
35
35
  "user-rohit-collector": "Mock User 14",
36
36
  "user-ananya-collector": "Mock User 9",
37
37
  };
38
+ /*
39
+ * Typed on the document, not `FirestoreDocument`. Once `BidDocument` gained
40
+ * `statusHistory?: StatusChangeEntry[]`, `Partial<BidDocument>` stopped being
41
+ * assignable to `FirestoreDocument` — an array of objects does not satisfy
42
+ * that index signature. The right fix is to narrow the parameter to what the
43
+ * caller actually passes rather than widen the document to keep a helper
44
+ * happy.
45
+ */
38
46
  function withBidDefaults(b) {
39
47
  return {
40
48
  ...b,
@@ -28,13 +28,34 @@ export interface FormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement
28
28
  * provider would shadow the parent's for everything rendered as `children`.
29
29
  */
30
30
  shellCtx?: FormShellContextValue;
31
+ /**
32
+ * Publish Save/Cancel and the mobile error sheet into the bottom-chrome
33
+ * tier — the same bar the cart uses.
34
+ *
35
+ * Opt-IN, unlike `<SectionForm>` where it is on by default. `<Form>` is the
36
+ * generic wrapper and is used inside `<Modal>` and `<SideDrawer>` footers,
37
+ * inside listing toolbars, and several times over on one page
38
+ * (`AdminSiteSettingsView` mounts twenty). Publishing route-level chrome
39
+ * from all of those by default would be wrong far more often than right.
40
+ * The hook additionally suppresses itself inside an overlay, so passing
41
+ * this in a modal is a no-op rather than a bug.
42
+ */
43
+ bottomBar?: FormBottomBarConfig;
31
44
  /** Cross-axis alignment of flex/grid children. Replaces raw `items-*` className. */
32
45
  align?: "start" | "center" | "end" | "stretch";
33
46
  /** Gap between children — use instead of raw `gap-*` className. */
34
47
  gap?: GapToken;
35
48
  }
36
49
  export type GapToken = "none" | "xs" | "sm" | "md" | "lg" | "xl";
37
- export declare function Form({ children, spacing, schema, shellCtx: shellCtxProp, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className, ...props }: FormProps): React.JSX.Element;
50
+ export interface FormBottomBarConfig {
51
+ onSubmit: () => void | Promise<void>;
52
+ onCancel?: () => void;
53
+ submitLabel?: string;
54
+ cancelLabel?: string;
55
+ isLoading?: boolean;
56
+ disabled?: boolean;
57
+ }
58
+ export declare function Form({ children, spacing, schema, shellCtx: shellCtxProp, bottomBar, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className, ...props }: FormProps): React.JSX.Element;
38
59
  export interface FormGroupProps {
39
60
  children: React.ReactNode;
40
61
  columns?: 1 | 2 | 3 | 4;
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React from "react";
3
4
  import { Row } from "./Layout";
4
5
  import { buildSurfaceClasses } from "./surface-tokens";
5
6
  import { FormShellContext, useFormShellState, } from "../forms/FormShell";
7
+ import { useFormBottomActions } from "../../features/layout/hooks/useFormBottomActions";
6
8
  const FORM_SPACING_MAP = {
7
9
  none: "",
8
10
  xs: "space-y-1",
@@ -43,9 +45,35 @@ const FORM_ALIGN_MAP = {
43
45
  end: "items-end",
44
46
  stretch: "items-stretch",
45
47
  };
46
- export function Form({ children, spacing, schema, shellCtx: shellCtxProp, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className = "", ...props }) {
48
+ export function Form({ children, spacing, schema, shellCtx: shellCtxProp, bottomBar, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className = "", ...props }) {
47
49
  const helpers = useFormShellState(schema);
48
50
  const effectiveCtx = shellCtxProp ?? helpers.shellCtx;
51
+ /*
52
+ * Marking the attempt on the NATIVE submit event covers both shapes this
53
+ * component is used in: the caller that calls `validate()` (which marks
54
+ * anyway) and the caller that hand-rolls its checks in the submit button's
55
+ * `onClick`. React fires `onClick` before `onSubmit`, so by the time the
56
+ * re-render happens the errors are already in the context.
57
+ *
58
+ * `effectiveCtx` may come from a parent's `useFormShellState`, so mark on
59
+ * whichever context is actually mounted — not on `helpers`, which is a
60
+ * second, ignored state instance in that case.
61
+ */
62
+ const { onSubmit: onSubmitProp } = props;
63
+ const handleSubmit = React.useCallback((e) => {
64
+ effectiveCtx.markSubmitAttempted();
65
+ onSubmitProp?.(e);
66
+ }, [effectiveCtx, onSubmitProp]);
67
+ useFormBottomActions({
68
+ onSubmit: bottomBar?.onSubmit ?? (() => { }),
69
+ onCancel: bottomBar?.onCancel,
70
+ submitLabel: bottomBar?.submitLabel,
71
+ cancelLabel: bottomBar?.cancelLabel,
72
+ isLoading: bottomBar?.isLoading,
73
+ disabled: bottomBar?.disabled,
74
+ enabled: bottomBar != null,
75
+ ctx: effectiveCtx,
76
+ });
49
77
  const content = typeof children === "function"
50
78
  ? children({ ...helpers, shellCtx: effectiveCtx })
51
79
  : children;
@@ -56,7 +84,7 @@ export function Form({ children, spacing, schema, shellCtx: shellCtxProp, align,
56
84
  gap ? GAP_MAP[gap] : "",
57
85
  buildSurfaceClasses({ surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow }),
58
86
  className,
59
- ].filter(Boolean).join(" "), ...props, children: content }) }));
87
+ ].filter(Boolean).join(" "), ...props, onSubmit: handleSubmit, children: content }) }));
60
88
  }
61
89
  export function FormGroup({ children, columns = 1, gap = "md", className = "", }) {
62
90
  const gridClasses = {
@@ -7,6 +7,7 @@ import { Heading } from "./Typography";
7
7
  import { Button } from "./Button";
8
8
  import { SPRING_GENTLE } from "../../tokens/motion";
9
9
  import { useHandMode } from "../../_internal/client/hand-mode";
10
+ import { OverlayContext } from "./overlay-context";
10
11
  const SIZE_CLASSES = {
11
12
  sm: "appkit-modal__panel--sm",
12
13
  md: "appkit-modal__panel--md",
@@ -69,9 +70,9 @@ export function Modal({ isOpen, open, onClose, title, children, size = "md", sho
69
70
  if (typeof document === "undefined") {
70
71
  return null;
71
72
  }
72
- return createPortal(_jsx(AnimatePresence, { children: visible && (_jsxs(motion.div, { className: "appkit-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, onKeyDown: handleKeyDown, initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, "data-section": "modal-div-561", children: [_jsx("div", { className: "appkit-modal__backdrop", "aria-hidden": "true", onClick: onClose }), _jsxs(motion.div, { ref: panelRef, tabIndex: -1, className: ["appkit-modal__panel", SIZE_CLASSES[size], className]
73
- .filter(Boolean)
74
- .join(" "), initial: reduced ? false : { opacity: 0, scale: 0.95, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.95, y: 8 }, transition: SPRING_GENTLE, "data-section": "modal-div-562", children: [(title || showCloseButton) && (_jsxs("div", { className: hand === "left" ? "appkit-modal__header flex-row-reverse" : "appkit-modal__header", "data-section": "modal-div-563", children: [title ? (_jsx(Heading, { level: 2, id: titleId, className: "appkit-modal__title", children: title })) : (_jsx("span", {})), showCloseButton ? (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "appkit-modal__close", "aria-label": "Close", children: _jsx("span", { className: "appkit-modal__close-icon", "aria-hidden": "true", children: "\u00D7" }) })) : null] })), _jsx("div", { className: "appkit-modal__body", "data-section": "modal-div-564", children: children }), actions ? _jsx(ModalFooter, { children: actions }) : null] })] })) }), document.body);
73
+ return createPortal(_jsx(OverlayContext.Provider, { value: true, children: _jsx(AnimatePresence, { children: visible && (_jsxs(motion.div, { className: "appkit-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, onKeyDown: handleKeyDown, initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, "data-section": "modal-div-561", children: [_jsx("div", { className: "appkit-modal__backdrop", "aria-hidden": "true", onClick: onClose }), _jsxs(motion.div, { ref: panelRef, tabIndex: -1, className: ["appkit-modal__panel", SIZE_CLASSES[size], className]
74
+ .filter(Boolean)
75
+ .join(" "), initial: reduced ? false : { opacity: 0, scale: 0.95, y: 8 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.95, y: 8 }, transition: SPRING_GENTLE, "data-section": "modal-div-562", children: [(title || showCloseButton) && (_jsxs("div", { className: hand === "left" ? "appkit-modal__header flex-row-reverse" : "appkit-modal__header", "data-section": "modal-div-563", children: [title ? (_jsx(Heading, { level: 2, id: titleId, className: "appkit-modal__title", children: title })) : (_jsx("span", {})), showCloseButton ? (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: "appkit-modal__close", "aria-label": "Close", children: _jsx("span", { className: "appkit-modal__close-icon", "aria-hidden": "true", children: "\u00D7" }) })) : null] })), _jsx("div", { className: "appkit-modal__body", "data-section": "modal-div-564", children: children }), actions ? _jsx(ModalFooter, { children: actions }) : null] })] })) }) }), document.body);
75
76
  }
76
77
  export function ModalFooter({ children, className = "", }) {
77
78
  return (_jsx("div", { className: [
@@ -10,6 +10,7 @@ import { useSwipe } from "../../react";
10
10
  import { Text } from "./Typography";
11
11
  import { SPRING_SNAPPY } from "../../tokens/motion";
12
12
  import { useHandMode } from "../../_internal/client/hand-mode";
13
+ import { OverlayContext } from "./overlay-context";
13
14
  const UI_SIDE_DRAWER = {
14
15
  backdrop: "appkit-side-drawer__backdrop",
15
16
  root: "appkit-side-drawer",
@@ -152,8 +153,8 @@ export function SideDrawer({ isOpen, onClose, title, children, footer, mode = "v
152
153
  const reduced = useReducedMotion();
153
154
  const positionClass = resolvedSide === "left" ? UI_SIDE_DRAWER.left : UI_SIDE_DRAWER.right;
154
155
  const slideX = resolvedSide === "left" ? "-100%" : "100%";
155
- return (_jsx(AnimatePresence, { children: isOpen && (_jsxs(_Fragment, { children: [_jsx(motion.div, { className: UI_SIDE_DRAWER.backdrop, onClick: attemptClose, "aria-hidden": "true", initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 } }), _jsxs(motion.div, { ref: drawerRef, className: [UI_SIDE_DRAWER.root, positionClass].join(" "), role: "dialog", "aria-modal": "true", "aria-labelledby": "drawer-title", initial: reduced ? false : { x: slideX }, animate: { x: 0 }, exit: { x: slideX }, transition: SPRING_SNAPPY, "data-section": "sidedrawer-div-598", children: [_jsxs(Row, { justify: "between", gap: "none", className: [
156
- UI_SIDE_DRAWER.header,
157
- UI_SIDE_DRAWER.headerMode[mode],
158
- ].join(" "), children: [_jsxs(Row, { gap: "3", className: "min-w-0", reverse: hand === "left", children: [_jsx(Button, { variant: "ghost", onClick: attemptClose, className: UI_SIDE_DRAWER.closeBtn, "aria-label": tActions("close"), children: _jsx("svg", { className: UI_SIDE_DRAWER.closeIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx(Heading, { level: 3, id: "drawer-title", className: "truncate", children: title })] }), mode === "delete" && (_jsx(Span, { className: UI_SIDE_DRAWER.deleteBadge, children: tActions("delete") }))] }), _jsx("div", { className: UI_SIDE_DRAWER.content, "data-section": "sidedrawer-div-599", children: children }), footer && _jsx("div", { className: UI_SIDE_DRAWER.footer, "data-section": "sidedrawer-div-600", children: footer })] }), showUnsavedWarning && (_jsxs(_Fragment, { children: [_jsx("div", { className: UI_SIDE_DRAWER.warnBackdrop, onClick: cancelClose }), _jsxs("div", { className: UI_SIDE_DRAWER.warnDialog, "data-section": "sidedrawer-div-601", children: [_jsxs("div", { className: UI_SIDE_DRAWER.warnHeader, "data-section": "sidedrawer-div-602", children: [_jsx("div", { className: UI_SIDE_DRAWER.warnIcon, "data-section": "sidedrawer-div-603", children: _jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }) }), _jsxs("div", { "data-section": "sidedrawer-div-604", children: [_jsx(Heading, { level: 4, className: "mb-1", children: tConfirm("unsavedChangesTitle") }), _jsx(Text, { className: UI_SIDE_DRAWER.warnText, children: tConfirm("unsavedChangesDescription") })] })] }), _jsxs("div", { className: UI_SIDE_DRAWER.warnActions, "data-section": "sidedrawer-div-605", children: [_jsx(Button, { variant: "outline", onClick: cancelClose, size: "sm", children: tActions("keepEditing") }), _jsx(Button, { variant: "danger", onClick: confirmClose, size: "sm", children: tActions("discardChanges") })] })] })] }))] })) }));
156
+ return (_jsx(OverlayContext.Provider, { value: true, children: _jsx(AnimatePresence, { children: isOpen && (_jsxs(_Fragment, { children: [_jsx(motion.div, { className: UI_SIDE_DRAWER.backdrop, onClick: attemptClose, "aria-hidden": "true", initial: reduced ? false : { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 } }), _jsxs(motion.div, { ref: drawerRef, className: [UI_SIDE_DRAWER.root, positionClass].join(" "), role: "dialog", "aria-modal": "true", "aria-labelledby": "drawer-title", initial: reduced ? false : { x: slideX }, animate: { x: 0 }, exit: { x: slideX }, transition: SPRING_SNAPPY, "data-section": "sidedrawer-div-598", children: [_jsxs(Row, { justify: "between", gap: "none", className: [
157
+ UI_SIDE_DRAWER.header,
158
+ UI_SIDE_DRAWER.headerMode[mode],
159
+ ].join(" "), children: [_jsxs(Row, { gap: "3", className: "min-w-0", reverse: hand === "left", children: [_jsx(Button, { variant: "ghost", onClick: attemptClose, className: UI_SIDE_DRAWER.closeBtn, "aria-label": tActions("close"), children: _jsx("svg", { className: UI_SIDE_DRAWER.closeIcon, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx(Heading, { level: 3, id: "drawer-title", className: "truncate", children: title })] }), mode === "delete" && (_jsx(Span, { className: UI_SIDE_DRAWER.deleteBadge, children: tActions("delete") }))] }), _jsx("div", { className: UI_SIDE_DRAWER.content, "data-section": "sidedrawer-div-599", children: children }), footer && _jsx("div", { className: UI_SIDE_DRAWER.footer, "data-section": "sidedrawer-div-600", children: footer })] }), showUnsavedWarning && (_jsxs(_Fragment, { children: [_jsx("div", { className: UI_SIDE_DRAWER.warnBackdrop, onClick: cancelClose }), _jsxs("div", { className: UI_SIDE_DRAWER.warnDialog, "data-section": "sidedrawer-div-601", children: [_jsxs("div", { className: UI_SIDE_DRAWER.warnHeader, "data-section": "sidedrawer-div-602", children: [_jsx("div", { className: UI_SIDE_DRAWER.warnIcon, "data-section": "sidedrawer-div-603", children: _jsx("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }) }), _jsxs("div", { "data-section": "sidedrawer-div-604", children: [_jsx(Heading, { level: 4, className: "mb-1", children: tConfirm("unsavedChangesTitle") }), _jsx(Text, { className: UI_SIDE_DRAWER.warnText, children: tConfirm("unsavedChangesDescription") })] })] }), _jsxs("div", { className: UI_SIDE_DRAWER.warnActions, "data-section": "sidedrawer-div-605", children: [_jsx(Button, { variant: "outline", onClick: cancelClose, size: "sm", children: tActions("keepEditing") }), _jsx(Button, { variant: "danger", onClick: confirmClose, size: "sm", children: tActions("discardChanges") })] })] })] }))] })) }) }));
159
160
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * "Am I rendering inside a Modal or a SideDrawer?"
3
+ *
4
+ * WHY: route-level chrome must not attach itself to content that is floating
5
+ * above the route. The concrete case is `useFormBottomActions` — a form on a
6
+ * page publishes its Save/Cancel row and its error sheet into the fixed
7
+ * `BottomChrome` tier, which is correct there and wrong inside an overlay:
8
+ * the overlay already owns a footer, and a bar pinned to the viewport bottom
9
+ * would sit *behind* the backdrop, below the very dialog it belongs to.
10
+ *
11
+ * Detection has to come from the overlay itself. There is no way to infer it
12
+ * from the DOM at render time (both portal elsewhere), and asking every form
13
+ * to declare where it is being rendered is the kind of convention that is one
14
+ * forgetful call site away from a bar appearing under a backdrop.
15
+ *
16
+ * Mounted by `Modal` and `SideDrawer`. Default `false`, so a component outside
17
+ * any overlay — the overwhelmingly common case — needs no provider at all.
18
+ */
19
+ export declare const OverlayContext: import("react").Context<boolean>;
20
+ /** True when the calling component is rendered inside a Modal or SideDrawer. */
21
+ export declare function useIsInsideOverlay(): boolean;
@@ -0,0 +1,25 @@
1
+ "use client";
2
+ import { createContext, useContext } from "react";
3
+ /**
4
+ * "Am I rendering inside a Modal or a SideDrawer?"
5
+ *
6
+ * WHY: route-level chrome must not attach itself to content that is floating
7
+ * above the route. The concrete case is `useFormBottomActions` — a form on a
8
+ * page publishes its Save/Cancel row and its error sheet into the fixed
9
+ * `BottomChrome` tier, which is correct there and wrong inside an overlay:
10
+ * the overlay already owns a footer, and a bar pinned to the viewport bottom
11
+ * would sit *behind* the backdrop, below the very dialog it belongs to.
12
+ *
13
+ * Detection has to come from the overlay itself. There is no way to infer it
14
+ * from the DOM at render time (both portal elsewhere), and asking every form
15
+ * to declare where it is being rendered is the kind of convention that is one
16
+ * forgetful call site away from a bar appearing under a backdrop.
17
+ *
18
+ * Mounted by `Modal` and `SideDrawer`. Default `false`, so a component outside
19
+ * any overlay — the overwhelmingly common case — needs no provider at all.
20
+ */
21
+ export const OverlayContext = createContext(false);
22
+ /** True when the calling component is rendered inside a Modal or SideDrawer. */
23
+ export function useIsInsideOverlay() {
24
+ return useContext(OverlayContext);
25
+ }
@@ -0,0 +1,29 @@
1
+ import { type FormShellContextValue } from "./FormShell";
2
+ export interface FormErrorListProps {
3
+ /**
4
+ * Read errors from this context value instead of the ambient one. The
5
+ * mobile bottom sheet needs it: `useFormBottomActions` builds the panel as
6
+ * a detached ReactNode that is rendered by `BottomActions`, high up in the
7
+ * layout tree and therefore OUTSIDE the form's own provider.
8
+ */
9
+ ctx?: FormShellContextValue;
10
+ className?: string;
11
+ }
12
+ /**
13
+ * The `<ul>` of current validation errors, with a jump link per entry when
14
+ * the form declares sections.
15
+ *
16
+ * WHY it is its own file: two surfaces render this exact list — the inline
17
+ * `<FormErrorSummary>` beside the submit button on desktop, and the mobile
18
+ * "Fix N issues" sheet in the bottom-chrome tier. Writing it twice would mean
19
+ * a jump-link fix, an empty-state fix or a wording fix landing in one and not
20
+ * the other, which is the bug-fix multiplier the Duplication Framework calls
21
+ * out. There are exactly two consumers and they are the same list, so this is
22
+ * a consolidation, not speculative abstraction.
23
+ *
24
+ * Renders nothing when there are no errors. It does NOT gate on
25
+ * `submitAttempted` — that is the *summary's* decision, not the list's, and
26
+ * the sheet needs to keep rendering while open.
27
+ */
28
+ export declare function FormErrorList({ ctx: ctxProp, className }: FormErrorListProps): import("react").JSX.Element | null;
29
+ export default FormErrorList;
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useContext } from "react";
4
+ import { FormShellContext } from "./FormShell";
5
+ import { Span } from "../components/Typography";
6
+ /**
7
+ * The `<ul>` of current validation errors, with a jump link per entry when
8
+ * the form declares sections.
9
+ *
10
+ * WHY it is its own file: two surfaces render this exact list — the inline
11
+ * `<FormErrorSummary>` beside the submit button on desktop, and the mobile
12
+ * "Fix N issues" sheet in the bottom-chrome tier. Writing it twice would mean
13
+ * a jump-link fix, an empty-state fix or a wording fix landing in one and not
14
+ * the other, which is the bug-fix multiplier the Duplication Framework calls
15
+ * out. There are exactly two consumers and they are the same list, so this is
16
+ * a consolidation, not speculative abstraction.
17
+ *
18
+ * Renders nothing when there are no errors. It does NOT gate on
19
+ * `submitAttempted` — that is the *summary's* decision, not the list's, and
20
+ * the sheet needs to keep rendering while open.
21
+ */
22
+ export function FormErrorList({ ctx: ctxProp, className }) {
23
+ const ambient = useContext(FormShellContext);
24
+ const ctx = ctxProp ?? ambient;
25
+ if (!ctx)
26
+ return null;
27
+ const entries = Object.entries(ctx.errors);
28
+ if (entries.length === 0)
29
+ return null;
30
+ return (_jsx("ul", { className: ["appkit-formshell__error-summary__list", className].filter(Boolean).join(" "), children: entries.map(([key, message]) => {
31
+ const stepIndex = ctx.fieldToStepIndex?.[key];
32
+ const stepLabel = stepIndex != null ? ctx.steps[stepIndex]?.label : undefined;
33
+ return (_jsx("li", { children: stepLabel ? (_jsxs("button", { type: "button", className: "appkit-formshell__error-summary__step-link", onClick: () => ctx.goToStep(stepIndex), children: [_jsxs(Span, { weight: "semibold", children: [stepLabel, ":"] }), " ", message] })) : (_jsx(Span, { children: message })) }, key));
34
+ }) }));
35
+ }
36
+ export default FormErrorList;
@@ -13,6 +13,14 @@ export interface FormErrorSummaryProps {
13
13
  * error immediately as fields change — not gated by `touched`, unlike the
14
14
  * inline per-field errors.
15
15
  *
16
+ * 🛑 It IS gated on `ctx.submitAttempted`, and must stay that way. The
17
+ * summary speaks about fields the user has not reached yet, so before a
18
+ * submit it reads as a list of accusations rather than of mistakes — and
19
+ * ~20 views run `validate(draft)` in a mount effect, so an untouched empty
20
+ * draft fails every `.min(1)` on first paint. Gate the DISPLAY only: the
21
+ * live validation underneath is what keeps this list shrinking as the user
22
+ * fixes things after a failed submit, which is the whole point of it.
23
+ *
16
24
  * For multi-step forms whose step engine populates
17
25
  * `FormShellContextValue.fieldToStepIndex`, each entry names its owning step
18
26
  * and is clickable to jump there. Falls back to a flat list when no step