@homefile/components-v2 2.59.2 → 2.60.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 (169) hide show
  1. package/dist/assets/locales/en/index.json +5 -2
  2. package/dist/components/folderPanel/displayFiles/GridFile.js +9 -1
  3. package/dist/components/folderPanel/displayFiles/ListFile.js +9 -1
  4. package/dist/components/forms/dynamicForm/DynamicForm.js +1 -1
  5. package/dist/components/forms/dynamicForm/HiddenField.js +2 -2
  6. package/dist/components/forms/dynamicForm/fields/HomeInitialSetup.js +3 -1
  7. package/dist/components/forms/readOnly/ReadOnlyDynamicForm.js +21 -3
  8. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyAppliances.js +65 -22
  9. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyGuidelines.js +6 -3
  10. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyHistory.d.ts +2 -0
  11. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyHistory.js +35 -0
  12. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyItemRelated.js +8 -3
  13. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyOverview.d.ts +2 -0
  14. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyOverview.js +16 -0
  15. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyPurchaseDetails.d.ts +2 -0
  16. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyPurchaseDetails.js +30 -0
  17. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyWarrantyBanner.d.ts +2 -0
  18. package/dist/components/forms/readOnly/fields/homeItem/ReadOnlyWarrantyBanner.js +69 -0
  19. package/dist/components/forms/readOnly/fields/homeItem/index.d.ts +4 -0
  20. package/dist/components/forms/readOnly/fields/homeItem/index.js +4 -0
  21. package/dist/components/headers/TabsHeader.js +1 -1
  22. package/dist/components/history/AddDocuments.d.ts +7 -0
  23. package/dist/components/history/AddDocuments.js +8 -0
  24. package/dist/components/history/DatePennant.d.ts +7 -0
  25. package/dist/components/history/DatePennant.js +6 -0
  26. package/dist/components/history/HistoryCard.d.ts +19 -0
  27. package/dist/components/history/HistoryCard.js +13 -0
  28. package/dist/components/history/index.d.ts +3 -0
  29. package/dist/components/history/index.js +3 -0
  30. package/dist/components/homeBoard/AddToHomefile.js +1 -1
  31. package/dist/components/homeBoard/HomeCard.js +1 -1
  32. package/dist/components/homeItems/ViewingHomeItemBody.d.ts +2 -0
  33. package/dist/components/homeItems/ViewingHomeItemBody.js +41 -0
  34. package/dist/components/homeItems/ViewingHomeItemPanel.js +17 -19
  35. package/dist/components/homeItems/index.d.ts +1 -0
  36. package/dist/components/homeItems/index.js +1 -0
  37. package/dist/components/homi/HomiPresence.d.ts +2 -0
  38. package/dist/components/homi/HomiPresence.js +31 -0
  39. package/dist/components/homi/HomiVoice.d.ts +2 -0
  40. package/dist/components/homi/HomiVoice.js +6 -0
  41. package/dist/components/homi/ProgressBar.d.ts +2 -0
  42. package/dist/components/homi/ProgressBar.js +9 -0
  43. package/dist/components/homi/RevealValueCard.d.ts +2 -0
  44. package/dist/components/homi/RevealValueCard.js +23 -0
  45. package/dist/components/homi/StaggerReveal.d.ts +2 -0
  46. package/dist/components/homi/StaggerReveal.js +29 -0
  47. package/dist/components/homi/index.d.ts +5 -0
  48. package/dist/components/homi/index.js +5 -0
  49. package/dist/components/inboxTile/InboxForwardBanner.js +1 -1
  50. package/dist/components/index.d.ts +2 -0
  51. package/dist/components/index.js +2 -0
  52. package/dist/components/inputs/InlineEditField.d.ts +2 -0
  53. package/dist/components/inputs/InlineEditField.js +15 -0
  54. package/dist/components/inputs/RadioCard.d.ts +3 -1
  55. package/dist/components/inputs/RadioCard.js +3 -3
  56. package/dist/components/inputs/TextInput.d.ts +1 -1
  57. package/dist/components/inputs/TextInput.js +11 -4
  58. package/dist/components/inputs/index.d.ts +1 -0
  59. package/dist/components/inputs/index.js +1 -0
  60. package/dist/components/myHomes/HomeDetailsContent.js +1 -1
  61. package/dist/components/myHomes/IncludePublicRecords.d.ts +13 -1
  62. package/dist/components/myHomes/IncludePublicRecords.js +16 -6
  63. package/dist/components/myHomes/edithome/EditHomeDetails.js +12 -3
  64. package/dist/components/myHomes/steps/NewHomeDetails.d.ts +1 -1
  65. package/dist/components/myHomes/steps/NewHomeDetails.js +3 -3
  66. package/dist/components/myProfile/MyProfileBody.d.ts +1 -1
  67. package/dist/components/myProfile/MyProfileBody.js +2 -2
  68. package/dist/components/myProfile/details/HomiSms.d.ts +1 -1
  69. package/dist/components/myProfile/details/HomiSms.js +53 -23
  70. package/dist/components/myProfile/details/TwoFactorSetting.js +2 -1
  71. package/dist/components/rooms/RoomHeader.d.ts +1 -1
  72. package/dist/components/rooms/RoomHeader.js +3 -3
  73. package/dist/components/wizard/ControlledWizard.d.ts +1 -1
  74. package/dist/components/wizard/ControlledWizard.js +42 -3
  75. package/dist/components/wizard/WizardInitialOptions.js +3 -3
  76. package/dist/components/wizard/WizardSelectCard.d.ts +14 -0
  77. package/dist/components/wizard/WizardSelectCard.js +32 -0
  78. package/dist/components/wizard/WizardStepPrompt.d.ts +11 -0
  79. package/dist/components/wizard/WizardStepPrompt.js +20 -0
  80. package/dist/components/wizard/WizardSuccessHeader.d.ts +1 -1
  81. package/dist/components/wizard/WizardSuccessHeader.js +2 -2
  82. package/dist/components/wizard/index.d.ts +2 -0
  83. package/dist/components/wizard/index.js +2 -0
  84. package/dist/helpers/forms/dynamicForm.helper.js +4 -0
  85. package/dist/hooks/folderPanel/useDisplayFiles.js +11 -2
  86. package/dist/hooks/forms/useAddressStored.js +8 -2
  87. package/dist/hooks/index.d.ts +2 -0
  88. package/dist/hooks/index.js +2 -0
  89. package/dist/hooks/motion/index.d.ts +2 -0
  90. package/dist/hooks/motion/index.js +2 -0
  91. package/dist/hooks/motion/useCountUp.d.ts +10 -0
  92. package/dist/hooks/motion/useCountUp.js +39 -0
  93. package/dist/hooks/motion/usePrefersReducedMotion.d.ts +14 -0
  94. package/dist/hooks/motion/usePrefersReducedMotion.js +15 -0
  95. package/dist/hooks/rooms/useRoomHeader.d.ts +4 -1
  96. package/dist/hooks/rooms/useRoomHeader.js +9 -1
  97. package/dist/hooks/useDirtySave.d.ts +14 -0
  98. package/dist/hooks/useDirtySave.js +33 -0
  99. package/dist/index.d.ts +3 -3
  100. package/dist/index.js +3 -3
  101. package/dist/interfaces/forms/dynamicForm/DynamicForm.interface.d.ts +7 -3
  102. package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyOverview.interface.d.ts +2 -0
  103. package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyOverview.interface.js +1 -0
  104. package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyPurchaseDetails.interface.d.ts +5 -0
  105. package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyPurchaseDetails.interface.js +1 -0
  106. package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyWarrantyBanner.interface.d.ts +54 -0
  107. package/dist/interfaces/forms/readOnly/homeItem/ReadOnlyWarrantyBanner.interface.js +1 -0
  108. package/dist/interfaces/forms/readOnly/homeItem/index.d.ts +3 -0
  109. package/dist/interfaces/forms/readOnly/homeItem/index.js +3 -0
  110. package/dist/interfaces/homeItems/ViewingHomeItemPanel.interface.d.ts +1 -0
  111. package/dist/interfaces/homi/HomiPresence.interface.d.ts +9 -0
  112. package/dist/interfaces/homi/HomiPresence.interface.js +1 -0
  113. package/dist/interfaces/homi/ProgressBar.interface.d.ts +8 -0
  114. package/dist/interfaces/homi/ProgressBar.interface.js +1 -0
  115. package/dist/interfaces/homi/RevealValueCard.interface.d.ts +16 -0
  116. package/dist/interfaces/homi/RevealValueCard.interface.js +1 -0
  117. package/dist/interfaces/homi/StaggerReveal.interface.d.ts +9 -0
  118. package/dist/interfaces/homi/StaggerReveal.interface.js +1 -0
  119. package/dist/interfaces/homi/index.d.ts +4 -0
  120. package/dist/interfaces/homi/index.js +4 -0
  121. package/dist/interfaces/index.d.ts +1 -0
  122. package/dist/interfaces/index.js +1 -0
  123. package/dist/interfaces/inputs/InlineEditField.interface.d.ts +7 -0
  124. package/dist/interfaces/inputs/InlineEditField.interface.js +1 -0
  125. package/dist/interfaces/inputs/Input.interface.d.ts +6 -1
  126. package/dist/interfaces/inputs/index.d.ts +1 -0
  127. package/dist/interfaces/inputs/index.js +1 -0
  128. package/dist/interfaces/myHomes/AddHomeContent.interface.d.ts +3 -0
  129. package/dist/interfaces/myProfile/MyProfileBody.interface.d.ts +1 -0
  130. package/dist/interfaces/myProfile/details/HomiSms.interface.d.ts +11 -2
  131. package/dist/interfaces/rooms/RoomHeader.interface.d.ts +5 -0
  132. package/dist/interfaces/wizard/ControlledWizard.interface.d.ts +6 -0
  133. package/dist/interfaces/wizard/WizardStepPrompt.interface.d.ts +9 -0
  134. package/dist/interfaces/wizard/WizardStepPrompt.interface.js +1 -0
  135. package/dist/interfaces/wizard/index.d.ts +1 -0
  136. package/dist/interfaces/wizard/index.js +1 -0
  137. package/dist/stories/forms/readOnly/ReadOnlyAppliances.stories.d.ts +4 -0
  138. package/dist/stories/forms/readOnly/ReadOnlyAppliances.stories.js +33 -0
  139. package/dist/stories/forms/readOnly/ReadOnlyWarrantyBanner.stories.d.ts +4 -0
  140. package/dist/stories/forms/readOnly/ReadOnlyWarrantyBanner.stories.js +10 -0
  141. package/dist/stories/homeItems/ReadOnlyHistory.stories.d.ts +4 -0
  142. package/dist/stories/homeItems/ReadOnlyHistory.stories.js +46 -0
  143. package/dist/stories/homeItems/ReadOnlyOverview.stories.d.ts +5 -0
  144. package/dist/stories/homeItems/ReadOnlyOverview.stories.js +69 -0
  145. package/dist/stories/homeItems/ViewingHomeItemPanel.stories.d.ts +4 -0
  146. package/dist/stories/homeItems/ViewingHomeItemPanel.stories.js +204 -0
  147. package/dist/stories/homi/HomiPresence.stories.d.ts +18 -0
  148. package/dist/stories/homi/HomiPresence.stories.js +11 -0
  149. package/dist/stories/homi/HomiVoice.stories.d.ts +4 -0
  150. package/dist/stories/homi/HomiVoice.stories.js +8 -0
  151. package/dist/stories/homi/ProgressBar.stories.d.ts +10 -0
  152. package/dist/stories/homi/ProgressBar.stories.js +9 -0
  153. package/dist/stories/homi/RevealValueCard.stories.d.ts +19 -0
  154. package/dist/stories/homi/RevealValueCard.stories.js +22 -0
  155. package/dist/stories/homi/StaggerReveal.stories.d.ts +5 -0
  156. package/dist/stories/homi/StaggerReveal.stories.js +8 -0
  157. package/dist/stories/inputs/InlineEditField.stories.d.ts +10 -0
  158. package/dist/stories/inputs/InlineEditField.stories.js +11 -0
  159. package/dist/stories/myProfile/MyProfilePanel.stories.js +1 -1
  160. package/dist/stories/myProfile/details/HomiSms.stories.d.ts +3 -2
  161. package/dist/stories/myProfile/details/HomiSms.stories.js +3 -2
  162. package/dist/stories/wizard/ControlledWizard.stories.d.ts +9 -0
  163. package/dist/stories/wizard/ControlledWizard.stories.js +41 -60
  164. package/dist/theme/index.d.ts +3 -0
  165. package/dist/theme/index.js +7 -4
  166. package/dist/utils/Formaters.utils.d.ts +2 -0
  167. package/dist/utils/Formaters.utils.js +14 -0
  168. package/dist/utils/Weather.utils.js +11 -4
  169. package/package.json +1 -1
@@ -546,7 +546,7 @@
546
546
  "title": "You are viewing "
547
547
  },
548
548
  "details": {
549
- "header": "Homefile Summary",
549
+ "header": "Edit Home",
550
550
  "tabs": {
551
551
  "tab1": "Details",
552
552
  "tab2": "Projects",
@@ -558,6 +558,7 @@
558
558
  "title": "Let’s add your first home."
559
559
  },
560
560
  "form": {
561
+ "editHomeName": "Home Name",
561
562
  "externalLink": "Add optional external link",
562
563
  "homeName": "Name your home",
563
564
  "name": "Name or Project Number",
@@ -626,6 +627,7 @@
626
627
  "2FactorSubtitle": "Select how you would like to receive your 2FA codes. These codes keep your account secure.",
627
628
  "2FactorTitle": "2 Factor Authentication (2FA)",
628
629
  "homiSmsAlt": "Homi SMS",
630
+ "homiSmsConsent": "SMS messaging consent & terms",
629
631
  "homiSmsSubtitle": "Enable SMS messaging to interact with Homi without a mobile app. Text images, documents, and information directly into Homefile so Homi can capture and organize it for you automatically.",
630
632
  "homiSmsTitle": "Enable Homi SMS Messaging"
631
633
  },
@@ -1062,7 +1064,8 @@
1062
1064
  "title": "Reset your Password"
1063
1065
  },
1064
1066
  "roomHeader": {
1065
- "roomValue": "Est room value",
1067
+ "estValue": "Est. value",
1068
+ "roomValue": "Room value",
1066
1069
  "saveBtn": "Save"
1067
1070
  },
1068
1071
  "rooms": {
@@ -14,15 +14,23 @@ import { Button, Image, Text, Box, WrapItem, Stack } from '@chakra-ui/react';
14
14
  import { IconMenuWithMove, MoreHorizontal } from '../../../components';
15
15
  import { DocIcon } from '../../../assets/images';
16
16
  import { fileTypes, getFileExtension } from '../../../helpers';
17
+ import { useDisplayFiles } from '../../../hooks';
17
18
  export const GridFile = (_a) => {
18
19
  var { menu = [], onClick, onMove } = _a, props = __rest(_a, ["menu", "onClick", "onMove"]);
19
20
  const { _id, title, updatedAt, files = [], receipt, report } = props;
20
21
  const fileExtension = getFileExtension(files, !!receipt);
22
+ // Real image thumbnail (empty string for non-images); falls back to the icon.
23
+ const { imageUrl } = useDisplayFiles({ files, title });
24
+ const typeIcon = report && report.length > 0 && !receipt
25
+ ? DocIcon
26
+ : fileTypes[fileExtension] || DocIcon;
21
27
  const itemForm = {
22
28
  _id,
23
29
  name: title || '',
24
30
  updatedAt,
25
31
  icon: fileTypes[fileExtension] || DocIcon,
26
32
  };
27
- return (_jsxs(WrapItem, { maxW: "7rem", position: "relative", children: [_jsx(Box, { position: "absolute", top: "0", right: "1", zIndex: "docked", children: _jsx(IconMenuWithMove, { disabled: menu.length < 1 ? true : false, icon: _jsx(MoreHorizontal, { size: 28 }), itemForm: itemForm, menuItems: menu, onMove: () => onMove(itemForm) }) }), _jsx(Button, { variant: "folder", px: "base", pb: "base", pt: "6", onClick: () => onClick(props), children: _jsxs(Stack, { spacing: "1", align: "center", children: [_jsx(Image, { src: (report && report.length > 0 && !receipt) ? DocIcon : fileTypes[fileExtension] || DocIcon, w: "46px", h: "49px", alt: fileExtension, transition: "all 0.2s ease-in-out" }), _jsxs(Box, { children: [_jsx(Text, { isTruncated: true, maxWidth: "5rem", fontSize: "sm", children: title }), _jsx(Text, { variant: "date", children: updatedAt })] })] }) })] }));
33
+ return (_jsxs(WrapItem, { maxW: "7rem", position: "relative", children: [_jsx(Box, { position: "absolute", top: "0", right: "1", zIndex: "docked", children: _jsx(IconMenuWithMove, { disabled: menu.length < 1 ? true : false, icon: _jsx(MoreHorizontal, { size: 28 }), itemForm: itemForm, menuItems: menu, onMove: () => onMove(itemForm) }) }), _jsx(Button, { variant: "folder", px: "base", pb: "base", pt: "6", onClick: () => onClick(props), children: _jsxs(Stack, { spacing: "1", align: "center", children: [_jsx(Image, Object.assign({ src: imageUrl || typeIcon, fallbackSrc: typeIcon }, (imageUrl
34
+ ? { boxSize: '72px', objectFit: 'cover', borderRadius: 'base' }
35
+ : { w: '46px', h: '49px' }), { alt: fileExtension, transition: "all 0.2s ease-in-out" })), _jsxs(Box, { children: [_jsx(Text, { isTruncated: true, maxWidth: "5rem", fontSize: "sm", children: title }), _jsx(Text, { variant: "date", children: updatedAt })] })] }) })] }));
28
36
  };
@@ -14,15 +14,23 @@ import { Image, Text, Box, Flex } from '@chakra-ui/react';
14
14
  import { IconMenuWithMove, MoreHorizontal } from '../../../components';
15
15
  import { DocIcon } from '../../../assets/images';
16
16
  import { fileTypes, getFileExtension } from '../../../helpers';
17
+ import { useDisplayFiles } from '../../../hooks';
17
18
  export const ListFile = (_a) => {
18
19
  var { menu = [], onClick, onMove } = _a, props = __rest(_a, ["menu", "onClick", "onMove"]);
19
20
  const { _id, title, updatedAt, files = [], receipt, report } = props;
20
21
  const fileExtension = getFileExtension(files, !!receipt);
22
+ // Real image thumbnail (empty string for non-images); falls back to the icon.
23
+ const { imageUrl } = useDisplayFiles({ files, title });
24
+ const typeIcon = report && report.length > 0 && !receipt
25
+ ? DocIcon
26
+ : fileTypes[fileExtension] || DocIcon;
21
27
  const itemForm = {
22
28
  _id,
23
29
  name: title || '',
24
30
  updatedAt,
25
31
  icon: fileTypes[fileExtension] || DocIcon,
26
32
  };
27
- return (_jsx(Box, { bg: "neutral.white", w: "100%", transition: "all 0.2s ease-in-out", _hover: { bg: 'lightGreen.1' }, children: _jsxs(Flex, { children: [_jsxs(Flex, { gap: "base", p: "base", as: "button", flex: "1", onClick: () => onClick(props), children: [_jsx(Image, { src: (report && report.length > 0 && !receipt) ? DocIcon : fileTypes[fileExtension] || DocIcon, minW: "32px", h: "auto", alt: fileExtension, transition: "all 0.2s ease-in-out" }), _jsxs(Box, { mt: "-1", children: [_jsx(Text, { variant: "date", textTransform: "uppercase", textAlign: "left", children: fileExtension === '' ? 'Doc' : fileExtension }), _jsx(Text, { textOverflow: "ellipsis", noOfLines: 1, fontSize: "sm", textAlign: "left", children: title })] })] }), _jsxs(Flex, { align: "center", gap: "2", h: "fit-content", p: "1", pr: "base", children: [_jsx(Text, { variant: "date", w: "fit-content", children: updatedAt }), _jsx(IconMenuWithMove, { disabled: menu.length < 1 ? true : false, icon: _jsx(MoreHorizontal, { size: 28 }), itemForm: itemForm, menuItems: menu, onMove: () => onMove(itemForm) })] })] }) }));
33
+ return (_jsx(Box, { bg: "neutral.white", w: "100%", transition: "all 0.2s ease-in-out", _hover: { bg: 'lightGreen.1' }, children: _jsxs(Flex, { children: [_jsxs(Flex, { gap: "base", p: "base", as: "button", flex: "1", onClick: () => onClick(props), children: [_jsx(Image, Object.assign({ src: imageUrl || typeIcon, fallbackSrc: typeIcon }, (imageUrl
34
+ ? { boxSize: '40px', objectFit: 'cover', borderRadius: 'base' }
35
+ : { minW: '32px', h: 'auto' }), { alt: fileExtension, transition: "all 0.2s ease-in-out" })), _jsxs(Box, { mt: "-1", children: [_jsx(Text, { variant: "date", textTransform: "uppercase", textAlign: "left", children: fileExtension === '' ? 'Doc' : fileExtension }), _jsx(Text, { textOverflow: "ellipsis", noOfLines: 1, fontSize: "sm", textAlign: "left", children: title })] })] }), _jsxs(Flex, { align: "center", gap: "2", h: "fit-content", p: "1", pr: "base", children: [_jsx(Text, { variant: "date", w: "fit-content", children: updatedAt }), _jsx(IconMenuWithMove, { disabled: menu.length < 1 ? true : false, icon: _jsx(MoreHorizontal, { size: 28 }), itemForm: itemForm, menuItems: menu, onMove: () => onMove(itemForm) })] })] }) }));
28
36
  };
@@ -23,7 +23,7 @@ export const DynamicForm = (_a) => {
23
23
  if (searching) {
24
24
  return _jsx(SearchItemLoader, {});
25
25
  }
26
- return (_jsxs(Stack, { bg: "lightBlue.1", spacing: "0", h: "full", overflowX: "hidden", children: [showTitle && (_jsx(Box, { px: "base", pt: "4", pb: "2", borderBottom: "1px dashed", borderColor: "lightBlue.6", children: _jsx(Text, { fontFamily: "secondary", children: title !== null && title !== void 0 ? title : t('forms.itemDetails') }) })), _jsx(FormProvider, Object.assign({}, form, { children: _jsx(_Fragment, { children: visibleFields === null || visibleFields === void 0 ? void 0 : visibleFields.map((field) => {
26
+ return (_jsxs(Stack, { bg: props.style === 'wizard' ? 'transparent' : 'lightBlue.1', spacing: "0", h: "full", overflowX: props.style === 'wizard' ? 'visible' : 'hidden', children: [showTitle && (_jsx(Box, { px: "base", pt: "4", pb: "2", borderBottom: "1px dashed", borderColor: "lightBlue.6", children: _jsx(Text, { fontFamily: "secondary", children: title !== null && title !== void 0 ? title : t('forms.itemDetails') }) })), _jsx(FormProvider, Object.assign({}, form, { children: _jsx(_Fragment, { children: visibleFields === null || visibleFields === void 0 ? void 0 : visibleFields.map((field) => {
27
27
  const { comments, canBeHidden, children, description, icon, id, name, label, options, type, value, } = field;
28
28
  const baseProps = {
29
29
  id,
@@ -4,7 +4,7 @@ import { Plus } from '../../../components';
4
4
  import { colors } from '../../../theme/colors';
5
5
  export const HiddenField = ({ disabled, h = '80px', icon, id, isActive, name, onClick, showPlusIcon = false, }) => {
6
6
  const handleClick = () => onClick(id);
7
- return (_jsxs(Box, { position: "relative", children: [_jsxs(Center, { as: "button", disabled: disabled, bg: "neutral.white", borderRadius: "4px", w: "100%", h: h, boxShadow: "md", flexDirection: "column", onClick: handleClick, gap: "1", transition: "all .2s ease-in-out", border: "1px solid", borderColor: isActive ? 'blue.1' : 'transparent', _hover: {
7
+ return (_jsxs(Box, { position: "relative", children: [_jsxs(Center, { as: "button", disabled: disabled, bg: "neutral.white", borderRadius: "4px", w: "100%", h: h, boxShadow: "md", flexDirection: "column", onClick: handleClick, gap: "1", py: "2", px: "1", transition: "all .2s ease-in-out", border: "1px solid", borderColor: isActive ? 'blue.1' : 'transparent', _hover: {
8
8
  borderColor: 'blue.1',
9
9
  }, _disabled: {
10
10
  opacity: 0.5,
@@ -13,5 +13,5 @@ export const HiddenField = ({ disabled, h = '80px', icon, id, isActive, name, on
13
13
  },
14
14
  }, _active: {
15
15
  boxShadow: 'md',
16
- }, children: [_jsx(Image, { h: "22px", w: "auto", maxW: "28px", src: icon }), _jsx(Text, { textTransform: "uppercase", fontSize: "xs", noOfLines: 2, overflow: "hidden", lineHeight: "1", width: "100%", children: name })] }), showPlusIcon && (_jsx(Box, { position: "absolute", top: "1", left: "1", children: _jsx(Plus, { size: 14, stroke: colors.blue[3] }) }))] }));
16
+ }, children: [_jsx(Center, { flex: "1", children: _jsx(Image, { h: "28px", w: "auto", maxW: "36px", src: icon }) }), _jsx(Text, { fontFamily: "primary", color: "gray.3", textTransform: "uppercase", fontSize: "12px", noOfLines: 2, overflow: "hidden", lineHeight: "1", width: "100%", children: name })] }), showPlusIcon && (_jsx(Box, { position: "absolute", top: "1", left: "1", children: _jsx(Plus, { size: 14, stroke: colors.blue[3] }) }))] }));
17
17
  };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Stack } from '@chakra-ui/react';
3
- import { WizardTextBody, WizardInitialOptions, WizardStepOptions, } from '../../../../components';
3
+ import { WizardTextBody, WizardInitialOptions, WizardStepOptions, WizardSelectCard, } from '../../../../components';
4
4
  export const HomeInitialSetup = ({ fields, callback, style }) => {
5
5
  // const isWizard = style === 'wizard'
6
6
  return (_jsx(Stack, { bg: "neutral.white", spacing: { base: '6', md: '0' }, children: fields === null || fields === void 0 ? void 0 : fields.map(({ children = [], id, label = '', type, value, description }) => {
@@ -11,6 +11,8 @@ export const HomeInitialSetup = ({ fields, callback, style }) => {
11
11
  return (_jsx(WizardInitialOptions, { form: children, title: String(value), callback: callback, style: style }, id));
12
12
  case 'home-wizard-step-options':
13
13
  return (_jsx(WizardStepOptions, { form: children, title: String(value), callback: callback, style: style }, id));
14
+ case 'home-wizard-select-card':
15
+ return (_jsx(WizardSelectCard, { form: children, title: String(value), callback: callback, style: style }, id));
14
16
  default:
15
17
  return null;
16
18
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createElement as _createElement } from "react";
3
3
  import { Box } from '@chakra-ui/react';
4
- import { ReadOnlyAppliances, ReadOnlyCollapsible, ReadOnlyDate, ReadOnlyGrid, ReadOnlyGroup, ReadOnlyInput, ReadOnlyNotes, ReadOnlyRating, ReadOnlyTextArea, } from '../../../components';
4
+ import { ReadOnlyAppliances, ReadOnlyCollapsible, ReadOnlyDate, ReadOnlyGrid, ReadOnlyGroup, ReadOnlyInput, ReadOnlyHistory, ReadOnlyNotes, ReadOnlyOverview, ReadOnlyPurchaseDetails, ReadOnlyRating, ReadOnlyTextArea, ReadOnlyWarrantyBanner, } from '../../../components';
5
5
  import { fieldIcons } from '../../../helpers';
6
6
  /**
7
7
  * Coerce a field value into something safe to render. A malformed value that
@@ -26,7 +26,17 @@ function toSafeValue(value) {
26
26
  return value;
27
27
  }
28
28
  export const ReadOnlyDynamicForm = ({ form: fields, onClick, onEdit, }) => {
29
- return (_jsx(Box, { bg: "lightBlue.1", overflow: "scroll", children: fields === null || fields === void 0 ? void 0 : fields.map((field) => {
29
+ return (
30
+ // `overflow="auto"`, not `"scroll"`. `scroll` reserves a scrollbar gutter on
31
+ // BOTH axes whether or not there is anything to scroll — measured at 15px
32
+ // each in Chrome with classic (non-overlay) scrollbars. That gutter is
33
+ // inside the box, so every field rendered here stops 15px short of the
34
+ // panel's right edge and a dead 15px band runs along the bottom, which is
35
+ // what showed up as the item card and warranty banner "not filling the
36
+ // panel". Nothing in this form scrolls sideways, so the horizontal gutter
37
+ // was pure loss. `auto` shows each bar only when that axis actually
38
+ // overflows and reserves nothing otherwise.
39
+ _jsx(Box, { bg: "lightBlue.1", overflow: "auto", children: fields === null || fields === void 0 ? void 0 : fields.map((field) => {
30
40
  const { children = [], icon, id, name, type } = field;
31
41
  const value = toSafeValue(field.value);
32
42
  const baseProps = {
@@ -51,7 +61,7 @@ export const ReadOnlyDynamicForm = ({ form: fields, onClick, onEdit, }) => {
51
61
  case 'textarea':
52
62
  return (_createElement(ReadOnlyTextArea, Object.assign({}, baseProps, { key: `${id}-${name}-${value}-${type}` })));
53
63
  case 'grid':
54
- return (_jsx(ReadOnlyGrid, { onClick: onClick, children: children }, id));
64
+ return (_jsx(ReadOnlyGrid, { onClick: onClick, borderBottom: "none", children: children }, id));
55
65
  case 'group':
56
66
  return _jsx(ReadOnlyGroup, { children: children }, id);
57
67
  case 'appliances':
@@ -60,6 +70,14 @@ export const ReadOnlyDynamicForm = ({ form: fields, onClick, onEdit, }) => {
60
70
  return _createElement(ReadOnlyCollapsible, Object.assign({}, field, { key: id }));
61
71
  case 'notes':
62
72
  return _createElement(ReadOnlyNotes, Object.assign({}, field, { key: id }));
73
+ case 'warranty':
74
+ return _createElement(ReadOnlyWarrantyBanner, Object.assign({}, field, { key: id }));
75
+ case 'purchase':
76
+ return (_createElement(ReadOnlyPurchaseDetails, Object.assign({}, field, { key: id, onClick: onClick })));
77
+ case 'overview':
78
+ return _createElement(ReadOnlyOverview, Object.assign({}, field, { key: id }));
79
+ case 'history':
80
+ return _createElement(ReadOnlyHistory, Object.assign({}, field, { key: id }));
63
81
  default:
64
82
  return null;
65
83
  }
@@ -1,24 +1,67 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Text, Flex, Stack, IconButton, Box } from '@chakra-ui/react';
3
- import { ReadOnlyHomeItemRating, Edit, ReadOnlyHomeItemImages, ReadOnlyHomeItemText, } from '../../../../../components';
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Text, Flex, Stack, IconButton, Box, Image, SimpleGrid, Grid } from '@chakra-ui/react';
4
+ import { Edit } from '../../../../../components';
4
5
  import { colors } from '../../../../../theme/colors';
5
- export const ReadOnlyAppliances = ({ description, id, name, children, onEdit, value, }) => {
6
- const rating = children === null || children === void 0 ? void 0 : children.find(({ type }) => type === 'rating');
7
- const images = children === null || children === void 0 ? void 0 : children.find(({ type }) => type === 'images');
8
- return (_jsxs(Stack, { spacing: "base", p: "base", bg: "neutral.white", mt: "2px", children: [name ||
9
- (onEdit && (_jsxs(Flex, { justify: "space-between", children: [_jsx(Text, { fontSize: "xs", textTransform: "uppercase", children: name }), onEdit && (_jsx(IconButton, { "aria-label": "Edit appliance", variant: "ghost", w: "fit-content", icon: _jsx(Edit, { size: 18, stroke: colors.blue[3] }), onClick: () => onEdit(id) }))] }))), _jsxs(Flex, { gap: "base", children: [_jsx(ReadOnlyHomeItemImages, Object.assign({}, images)), _jsxs(Stack, { spacing: "4", flex: "1", children: [_jsx(ReadOnlyHomeItemRating, Object.assign({}, rating)), _jsxs(Stack, { spacing: "base", children: [_jsx(Text, { fontSize: "22px", fontWeight: "semibold", children: value }), _jsx(Box, { children: children === null || children === void 0 ? void 0 : children.map(({ id, name, value, type }) => {
10
- const baseProps = {
11
- key: `${id}-${name}-${value}-${type}`,
12
- id,
13
- value,
14
- type,
15
- name,
16
- };
17
- switch (type) {
18
- case 'text':
19
- return _jsx(ReadOnlyHomeItemText, Object.assign({}, baseProps));
20
- default:
21
- return null;
22
- }
23
- }) }), _jsx(Box, { flex: "1", borderBottom: "1px dashed", borderColor: "lightBlue.6", h: "1px" }), _jsx(Text, { fontFamily: "secondary", fontSize: "sm", children: description })] })] })] })] }));
6
+ import { formatCurrency } from '../../../../../utils';
7
+ // Dashed rule between sections (XD: light gray dashed).
8
+ const DashedRule = () => (_jsx(Box, { borderBottom: "1px dashed", borderColor: "lightGray.2" }));
9
+ // Brand / Model# / Serial# row — label #6C7679, value #464646 (XD).
10
+ //
11
+ // The label column sizes to its content with a floor, rather than taking a
12
+ // fixed 35%: a real label is "Brand/Manufacturer:" (a schema key in ~50 places
13
+ // across the platform, so it cannot simply be shortened), and 35% of a narrow
14
+ // drawer is not wide enough for it. The percentage broke it mid-word into
15
+ // "Brand/Manufact" + "urer:". `keep-all` stops the mid-word break outright,
16
+ // so a label that still outgrows the column wraps at a natural boundary.
17
+ // Values arrive from different extractors with inconsistent casing — a brand
18
+ // comes back "Maytag", a product type "front-load washing machine" — so the
19
+ // column reads as a ransom note. Capitalize the FIRST character only and leave
20
+ // the rest of the string untouched: that makes the column consistent without
21
+ // rewriting the data, and keeps model numbers like "NC501" or "DCC-3400"
22
+ // exactly as extracted (title-casing them would produce "Nc501").
23
+ const leadCap = (s) => s.length > 0 ? s[0].toUpperCase() + s.slice(1) : s;
24
+ // Renders as TWO grid cells rather than a self-contained row, so every label
25
+ // shares one column and every value starts on the same left edge.
26
+ //
27
+ // Each row previously sized its own label independently, so the value column
28
+ // was ragged — the longest label ("Avg Retail Price Range:") pushed its own
29
+ // value right while the others sat further left. A fixed percentage was the
30
+ // original spec and broke the opposite way, wrapping "Brand/Manufacturer:"
31
+ // mid-word. A shared `max-content` column solves both: it is exactly as wide
32
+ // as the longest label, so nothing wraps and everything aligns.
33
+ const DetailRow = ({ label, value }) => (_jsxs(_Fragment, { children: [_jsx(Text, { wordBreak: "keep-all", fontFamily: "secondary", fontSize: "14px", lineHeight: "18px", color: "#6C7679", children: label }), _jsx(Text, { fontFamily: "secondary", fontSize: "14px", lineHeight: "18px", color: "gray.3", children: value != null ? leadCap(String(value)) : '' })] }));
34
+ // A value-row amount is currency-formatted only when it IS a single number.
35
+ //
36
+ // Catalog-resolved prices arrive as RANGE strings ("$229-$249") because that is
37
+ // what can honestly be established for a model. formatCurrency runs Number() on
38
+ // its input, and Number("$229-$249") is NaN, so formatting one prints "$NaN" on
39
+ // the owner's card. A non-numeric amount is already human-readable — render it
40
+ // as-is rather than mangling it.
41
+ const formatValueAmount = (value) => {
42
+ if (value == null || value === '')
43
+ return '';
44
+ const numeric = typeof value === 'number' ? value : Number(String(value).replace(/[$,\s]/g, ''));
45
+ return Number.isFinite(numeric)
46
+ ? formatCurrency({ value: numeric })
47
+ : String(value);
48
+ };
49
+ // Retail Price / Est Replacement Cost — label left #6C7679, value right #464646.
50
+ const ValueRow = ({ label, value, bold, }) => (_jsxs(Flex, { justify: "space-between", children: [_jsx(Text, { fontFamily: "secondary", fontSize: "16px", lineHeight: "20px", color: "#6C7679", children: label }), _jsx(Text, { fontFamily: "secondary", fontSize: "16px", lineHeight: "20px", color: "gray.3", fontWeight: bold ? 'bold' : 'normal', children: formatValueAmount(value) })] }));
51
+ export const ReadOnlyAppliances = ({ description, id, name, children = [], onEdit, value, }) => {
52
+ var _a;
53
+ const imageField = children.find(({ type }) => type === 'images');
54
+ const images = (_a = imageField === null || imageField === void 0 ? void 0 : imageField.value) !== null && _a !== void 0 ? _a : [];
55
+ // The chosen thumbnail is stored; the displayed image is DERIVED from it.
56
+ //
57
+ // `useState(images[0])` captured the first image only on the first render —
58
+ // and images arrive after that render, so the card kept showing an empty
59
+ // <Image> (a broken-picture box) until something else forced a re-render.
60
+ // Deriving means there is no second copy of "which image" to go stale, and a
61
+ // selection that is no longer in the list falls back instead of blanking.
62
+ const [chosenImage, setChosenImage] = useState(undefined);
63
+ const mainImage = chosenImage && images.includes(chosenImage) ? chosenImage : images[0];
64
+ const detailFields = children.filter(({ type }) => type === 'text');
65
+ const valueFields = children.filter(({ type }) => type === 'currency');
66
+ return (_jsxs(Stack, { spacing: "base", p: "base", bg: "neutral.white", mt: "2px", children: [(name || onEdit) && (_jsxs(Flex, { justify: "space-between", align: "center", w: "full", children: [_jsx(Text, { fontFamily: "primary", fontSize: "12px", letterSpacing: "0", color: "#525458", textTransform: "uppercase", children: name }), onEdit && (_jsx(IconButton, { "aria-label": "Edit appliance", variant: "ghost", w: "fit-content", minW: "auto", h: "auto", p: "0", flexShrink: 0, icon: _jsx(Edit, { size: 18, stroke: colors.blue[3] }), onClick: () => onEdit(id) }))] })), _jsxs(Flex, { gap: "base", align: "stretch", children: [_jsxs(Flex, { direction: "column", gap: "base", flexShrink: 0, w: "174px", children: [_jsx(Image, { src: mainImage, alt: "home item", boxSize: "174px", objectFit: "cover" }), images.length > 0 && (_jsx(SimpleGrid, { columns: 4, spacing: "2", children: images.map((image) => (_jsx(Box, { as: "button", backgroundImage: image, backgroundSize: "cover", backgroundPosition: "center", boxSize: "34px", onClick: () => setChosenImage(image) }, image))) }))] }), _jsxs(Stack, { flex: "1", spacing: "base", children: [_jsxs(Stack, { spacing: "13px", children: [_jsx(Text, { fontFamily: "primary", fontWeight: "600", fontSize: "22px", lineHeight: "26px", letterSpacing: "0", color: "gray.3", children: value }), _jsx(Text, { fontFamily: "secondary", fontSize: "14px", lineHeight: "15px", color: "gray.3", children: description })] }), _jsx(DashedRule, {}), detailFields.length > 0 && (_jsx(Grid, { templateColumns: "max-content 1fr", columnGap: "base", rowGap: "0", alignItems: "start", children: detailFields.map((field) => (_jsx(DetailRow, { label: field.name ? `${field.name}:` : undefined, value: field.value }, `${field.id}-${field.name}`))) })), valueFields.length > 0 && (_jsxs(_Fragment, { children: [_jsx(DashedRule, {}), _jsx(Stack, { spacing: "0", children: valueFields.map((field, i) => (_jsx(ValueRow, { label: field.name, value: field.value, bold: i === valueFields.length - 1 }, `${field.id}-${field.name}`))) })] }))] })] })] }));
24
67
  };
@@ -1,11 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Text, Stack, Grid } from '@chakra-ui/react';
2
+ import { Text, Stack, Box, Flex } from '@chakra-ui/react';
3
3
  import { HiddenField } from '../../../../../components';
4
4
  import { fieldIcons } from '../../../../../helpers';
5
+ // Care & Guidelines column: hugs its content (content-width) so the Parts
6
+ // column can fill the remaining width to the right of the 13px separator.
7
+ // Tiles are fixed 75px squares (HiddenField's own `h` prop — not modified).
5
8
  export const ReadOnlyGuidelines = ({ name, children, onClick, }) => {
6
- return (_jsxs(Stack, { spacing: "base", flex: "1", width: "100%", children: [_jsx(Text, { fontSize: "xs", textTransform: "uppercase", children: name }), _jsx(Grid, { templateColumns: "repeat(auto-fit, minmax(0, 75px))", gap: "base", children: children === null || children === void 0 ? void 0 : children.map((child) => {
9
+ return (_jsxs(Stack, { spacing: "base", width: "fit-content", flexShrink: 0, children: [_jsx(Text, { fontFamily: "primary", fontSize: "12px", color: "gray.3", textTransform: "uppercase", children: name }), _jsx(Flex, { flexWrap: "wrap", gap: "base", children: children === null || children === void 0 ? void 0 : children.map((child) => {
7
10
  const { id, icon, value } = child;
8
11
  const btnIcon = icon ? fieldIcons[icon] : '';
9
- return (_jsx(HiddenField, { id: id, icon: btnIcon, name: value, onClick: () => onClick === null || onClick === void 0 ? void 0 : onClick(id) }, id));
12
+ return (_jsx(Box, { w: "75px", children: _jsx(HiddenField, { id: id, h: "75px", icon: btnIcon, name: value, onClick: () => onClick === null || onClick === void 0 ? void 0 : onClick(id) }) }, id));
10
13
  }) })] }));
11
14
  };
@@ -0,0 +1,2 @@
1
+ import { ReportI } from '../../../../../interfaces';
2
+ export declare const ReadOnlyHistory: ({ name, value, label, accentColor, children, }: ReportI) => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Flex, Stack, Text } from '@chakra-ui/react';
3
+ import { DatePennant, HistoryCard } from '../../../../../components';
4
+ import { fieldIcons } from '../../../../../helpers';
5
+ import { formatCurrency } from '../../../../../utils';
6
+ // label (left #6C7679) + value (right #464646) — Brand / Model# rows (XD "Card").
7
+ const Row = ({ label, value }) => (_jsxs(Flex, { gap: "base", children: [_jsx(Text, { minW: "70px", fontFamily: "secondary", fontSize: "14px", lineHeight: "20px", color: "#6C7679", children: label }), _jsx(Text, { fontFamily: "secondary", fontSize: "14px", lineHeight: "20px", color: "gray.3", children: value != null ? String(value) : '' })] }));
8
+ // A single history event on the timeline: a date pennant (color per event type)
9
+ // joined by a node to the core HistoryCard. `label` = the pennant date,
10
+ // `accentColor` = the pennant color. Body is derived from children: an optional
11
+ // bold heading (value, e.g. a service company), Brand/Model-style rows for named
12
+ // text, plain lines for unnamed (address), a currency child as the attachment,
13
+ // an images child as the logo.
14
+ export const ReadOnlyHistory = ({ name, value, label, accentColor, children = [], }) => {
15
+ var _a;
16
+ const image = (_a = children.find(({ type }) => type === 'images')) === null || _a === void 0 ? void 0 : _a.value;
17
+ const attach = children.find(({ type }) => type === 'currency');
18
+ const textFields = children.filter(({ type }) => type === 'text');
19
+ const heading = value != null && value !== '' ? String(value) : '';
20
+ const color = accentColor || '#1E6388';
21
+ // Honesty: nothing credible to show → render nothing.
22
+ if (!name && !heading && textFields.length === 0 && !attach)
23
+ return null;
24
+ const attachment = attach
25
+ ? {
26
+ // Data-driven file-type icon (e.g. 'pdf'); none if unspecified.
27
+ icon: attach.icon ? fieldIcons[attach.icon] : undefined,
28
+ label: attach.name,
29
+ amount: attach.value != null && attach.value !== ''
30
+ ? formatCurrency({ value: attach.value })
31
+ : undefined,
32
+ }
33
+ : undefined;
34
+ return (_jsx(Box, { pl: "base", pr: "base", pt: "base", _last: { pb: 'base' }, children: _jsxs(Flex, { align: "flex-start", children: [_jsxs(Flex, { align: "center", pt: "12px", flexShrink: 0, children: [_jsx(DatePennant, { date: label, color: color }), _jsx(Box, { boxSize: "10px", borderRadius: "full", bg: "neutral.white", border: "2px solid", borderColor: color, ml: "0", zIndex: 2 })] }), _jsx(Box, { flex: "1", ml: "6px", children: _jsx(HistoryCard, { title: name, image: image, imagePosition: heading ? 'right' : 'left', attachment: attachment, children: _jsxs(Stack, { spacing: "0", children: [heading && (_jsx(Text, { fontFamily: "secondary", fontWeight: "bold", fontSize: "16px", lineHeight: "22px", color: "gray.3", children: heading })), textFields.map((field) => field.name ? (_jsx(Row, { label: `${field.name}:`, value: field.value }, field.id)) : (_jsx(Text, { fontFamily: "secondary", fontSize: "14px", lineHeight: "18px", color: "#3E4045", children: field.value != null ? String(field.value) : '' }, field.id)))] }) }) })] }) }));
35
+ };
@@ -1,11 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Text, Stack, Grid, Box } from '@chakra-ui/react';
2
+ import { Text, Stack, Grid, Box, Flex, Link } from '@chakra-ui/react';
3
3
  import { HiddenField } from '../../../../../components';
4
4
  import { fieldIcons } from '../../../../../helpers';
5
+ // Parts & Accessories column: fills the width to the right of the separator
6
+ // (flex="1"), so "View All" sits at the right edge. The 13px on the LEFT of the
7
+ // separator is the parent Flex `gap="base"` (=13px); the 13px on the RIGHT is
8
+ // this box's `pl="13px"`. Tiles are fixed 75px squares, three across, wrapping
9
+ // in threes. Icons are data-driven (each part's `icon` → the appliance icon).
5
10
  export const ReadOnlyItemRelated = ({ name, children, onClick, }) => {
6
- return (_jsx(Box, { borderLeft: "1px solid", borderColor: "lightBlue.6", pl: "4", marginInlineStart: "auto", children: _jsxs(Stack, { spacing: "base", children: [_jsx(Text, { fontSize: "xs", textTransform: "uppercase", children: name }), _jsx(Grid, { templateColumns: "repeat(2, minmax(80px, 1fr))", gap: "base", children: children === null || children === void 0 ? void 0 : children.map((child) => {
11
+ return (_jsx(Box, { flex: "1", borderLeft: "1px solid", borderColor: "lightBlue.5", pl: "13px", children: _jsxs(Stack, { spacing: "base", children: [_jsxs(Flex, { justify: "space-between", align: "center", gap: "base", children: [_jsx(Text, { fontFamily: "primary", fontSize: "12px", color: "gray.3", textTransform: "uppercase", children: name }), _jsx(Link, { fontFamily: "secondary", fontSize: "14px", fontWeight: "normal", color: "#2487C5", flexShrink: 0, onClick: () => onClick === null || onClick === void 0 ? void 0 : onClick('view-all'), children: "View All" })] }), _jsx(Grid, { templateColumns: "repeat(4, 75px)", gap: "base", children: children === null || children === void 0 ? void 0 : children.map((child) => {
7
12
  const { id, icon, value } = child;
8
13
  const btnIcon = icon ? fieldIcons[icon] : '';
9
- return (_jsx(HiddenField, { id: id, icon: btnIcon, name: value, onClick: () => onClick === null || onClick === void 0 ? void 0 : onClick(id) }, id));
14
+ return (_jsx(HiddenField, { id: id, h: "75px", icon: btnIcon, name: value, onClick: () => onClick === null || onClick === void 0 ? void 0 : onClick(id) }, id));
10
15
  }) })] }) }));
11
16
  };
@@ -0,0 +1,2 @@
1
+ import { ReadOnlyOverviewI } from '../../../../../interfaces';
2
+ export declare const ReadOnlyOverview: ({ name, children }: ReadOnlyOverviewI) => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Accordion, AccordionButton, AccordionItem, AccordionPanel, Box, Flex, ListItem, Text, UnorderedList, } from '@chakra-ui/react';
3
+ import { ChevronRight } from '../../../../../components';
4
+ // A Specs collapsible section — XD assembled Specs artboard. Exact spec:
5
+ // white card, 3px radius, shadow rgba(0,0,0,.16) 0 3px 6px; header padded
6
+ // 13px (top/sides) and 6.5px (bottom); title Poppins Regular 12px #525458
7
+ // uppercase + a blue circle chevron; body = bulleted list (Assistant 12px
8
+ // #464646). Built directly on Accordion (the shared Collapsible can't express
9
+ // this padding/radius/shadow).
10
+ export const ReadOnlyOverview = ({ name = '', children = [] }) => {
11
+ const bullets = children.filter((c) => c.value != null && c.value !== '');
12
+ // Honesty: no bullets → render nothing (never fabricate specs).
13
+ if (bullets.length === 0)
14
+ return null;
15
+ return (_jsx(Accordion, { allowToggle: true, bg: "neutral.white", borderRadius: "3px", boxShadow: "0 3px 6px rgba(0, 0, 0, 0.16)", children: _jsx(AccordionItem, { border: "none", children: ({ isExpanded }) => (_jsxs(_Fragment, { children: [_jsx(AccordionButton, { p: "base", _hover: { bg: 'transparent' }, _focus: { outline: 'none' }, children: _jsxs(Flex, { flex: "1", align: "center", justify: "space-between", children: [_jsx(Text, { fontFamily: "primary", fontSize: "14px", color: "#525458", textTransform: "uppercase", children: name }), _jsx(Box, { rounded: "full", border: "1px solid", borderColor: "blue.3", padding: "1", transition: "all .2s ease-in-out", transform: isExpanded ? 'rotate(-90deg)' : 'rotate(0)', children: _jsx(ChevronRight, {}) })] }) }), _jsx(AccordionPanel, { px: "13px", pb: "13px", pt: "0", children: _jsx(UnorderedList, { spacing: "base", m: "0", pl: "1.1em", children: bullets.map((bullet) => (_jsx(ListItem, { fontFamily: "secondary", fontSize: "14px", lineHeight: "1.4", color: "gray.3", children: String(bullet.value) }, bullet.id))) }) })] })) }) }));
16
+ };
@@ -0,0 +1,2 @@
1
+ import { ReadOnlyPurchaseDetailsI } from '../../../../../interfaces';
2
+ export declare const ReadOnlyPurchaseDetails: ({ id, name, link, children, onClick, }: ReadOnlyPurchaseDetailsI) => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Text, Flex, Stack, Box, Image, Link } from '@chakra-ui/react';
3
+ import { formatCurrency } from '../../../../../utils';
4
+ // Purchase Details block — XD "Purchase Details" (500×178). Retailer logo
5
+ // (128px square) + retailer name, then a purchase column (Purchased / Payment)
6
+ // and, past a vertical dashed divider, a totals column (Price / Tax / Total).
7
+ // Fonts/colors from the XD character styles:
8
+ // header "PURCHASE DETAILS" Poppins Regular 12px #464646 uppercase
9
+ // "View Receipt" Assistant Regular 14px #2488C6
10
+ // retailer label Assistant Regular 14px #6C7679
11
+ // retailer name Poppins SemiBold 18px #464646
12
+ // row label Assistant Regular 14px #6C7679
13
+ // row value Assistant Regular 14px #464646 (Total: Bold)
14
+ // label (left, #6C7679) + value (right, #464646). `bold` for the Total value.
15
+ const Row = ({ label, value, bold, }) => (_jsxs(Flex, { justify: "space-between", gap: "base", children: [_jsx(Text, { fontFamily: "secondary", fontSize: "14px", lineHeight: "20px", color: "#6C7679", children: label }), _jsx(Text, { fontFamily: "secondary", fontSize: "14px", lineHeight: "20px", color: "gray.3", fontWeight: bold ? 'bold' : 'normal', children: value != null ? String(value) : '' })] }));
16
+ export const ReadOnlyPurchaseDetails = ({ id, name, link, children = [], onClick, }) => {
17
+ var _a, _b;
18
+ const logo = (_a = children.find(({ type }) => type === 'images')) === null || _a === void 0 ? void 0 : _a.value;
19
+ const retailer = children.find((c) => c.name === 'Retailer');
20
+ const detailFields = children.filter(({ type, name: n }) => (type === 'text' || type === 'date') && n !== 'Retailer');
21
+ const valueFields = children.filter(({ type }) => type === 'currency');
22
+ // Honesty: nothing credible to show → render nothing (never fabricate).
23
+ if (!retailer && !logo && valueFields.length === 0)
24
+ return null;
25
+ return (_jsxs(Stack, { spacing: "base", p: "base", bg: "neutral.white", mt: "2px", children: [_jsxs(Flex, { justify: "space-between", align: "center", w: "full", children: [_jsx(Text, { fontFamily: "primary", fontSize: "12px", letterSpacing: "0", color: "gray.3", textTransform: "uppercase", children: name }), link && (_jsx(Link, { fontFamily: "secondary", fontSize: "14px", fontWeight: "normal", color: "#2488C6", onClick: () => onClick === null || onClick === void 0 ? void 0 : onClick(id), children: "View Receipt" }))] }), _jsxs(Flex, { gap: "base", align: "stretch", children: [logo && (_jsx(Image, { src: logo, alt: (retailer === null || retailer === void 0 ? void 0 : retailer.value) || 'retailer', boxSize: "128px", objectFit: "contain", flexShrink: 0 })), _jsxs(Stack, { flex: "1", spacing: "13px", children: [retailer && (_jsx(Text, { fontFamily: "primary", fontWeight: "600", fontSize: "22px", lineHeight: "26px", letterSpacing: "0", color: "gray.3", mt: "30px", children: String((_b = retailer.value) !== null && _b !== void 0 ? _b : '') })), _jsxs(Flex, { gap: "base", align: "flex-start", children: [detailFields.length > 0 && (_jsx(Stack, { flex: "1", spacing: "0", children: detailFields.map((field) => (_jsx(Row, { label: field.name ? `${field.name}:` : undefined, value: field.value }, `${field.id}-${field.name}`))) })), valueFields.length > 0 && (_jsx(Box, { borderLeft: "1px dashed", borderColor: "lightGray.2", pl: "base", w: "170px", flexShrink: 0, children: _jsx(Stack, { spacing: "0", children: valueFields.map((field, i) => (_jsx(Row, { label: field.name ? `${field.name}:` : undefined, value: field.value != null && field.value !== ''
26
+ ? formatCurrency({
27
+ value: field.value,
28
+ })
29
+ : '', bold: i === valueFields.length - 1 }, `${field.id}-${field.name}`))) }) }))] })] })] })] }));
30
+ };
@@ -0,0 +1,2 @@
1
+ import { ReadOnlyWarrantyBannerI } from '../../../../../interfaces';
2
+ export declare const ReadOnlyWarrantyBanner: ({ icon, value, state, link, onAction, actionLabel, }: ReadOnlyWarrantyBannerI) => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { t } from 'i18next';
3
+ import { Box, Circle, Flex, Image, Link, Text } from '@chakra-ui/react';
4
+ import { ArrowLeft } from '../../../../../components';
5
+ import { fieldIcons } from '../../../../../helpers';
6
+ import { colors } from '../../../../../theme/colors';
7
+ /**
8
+ * Warranty-status banner. Built to the EXACT CSS from the "Read Only Item" XD
9
+ * (dev/spec mode), under-warranty artboard:
10
+ * container: height 76px, background #F2F9F2, no border, no radius
11
+ * badge circle: 45×45, #E0EBE0, full radius, 10px from the left / centered
12
+ * umbrella: 27×29
13
+ * text: Poppins Regular 16px/20px, letter-spacing 0, #464646, left
14
+ *
15
+ * Only the under-warranty state is designed in the XD. `expired` and `recall`
16
+ * palettes below are PROVISIONAL placeholders (awaiting their own artboards) —
17
+ * flagged so they get exact CSS before shipping, not invented values.
18
+ *
19
+ * HONESTY: `recall` must ONLY be used with REAL recall data (#357) — the banner
20
+ * renders nothing when `value` is empty and never fabricates an alert.
21
+ */
22
+ // Per-state visuals. `icon` is a `fieldIcons` key and is the SINGLE swap point
23
+ // for each state's glyph — drop the new expired/recall icons in here (one line
24
+ // each) once their assets are registered in `fieldIcons`. Colors reuse existing
25
+ // design-system variants (no new colors): under-warranty = exact XD; expired =
26
+ // neutral gray family; recall = the app's alert color (`error`, same variant as
27
+ // AlertBanner / CreditCardError). Exact XD for expired/recall is still TBD.
28
+ const STATE = {
29
+ underWarranty: { bg: '#F2F9F2', badge: '#E0EBE0', icon: 'warranty' },
30
+ expired: { bg: 'lightGray.1', badge: 'lightGray.2', icon: 'warranty' }, // TODO: swap to the expired icon
31
+ // EXACT CSS from the unknown-warranty XD artboard (Matt, 09-21) — no longer
32
+ // provisional. Light blue reads INFORMATIONAL: deliberately neither the
33
+ // green of "covered" nor the gray of "lapsed", because the point of the
34
+ // state is that we do not know which it is.
35
+ // container #DEEDF7 · badge #BFDCEF · "?" #5F7482 Poppins SemiBold 29px
36
+ unverified: { bg: '#DEEDF7', badge: '#BFDCEF', icon: 'warranty' },
37
+ recall: { bg: 'error.1', badge: 'error.2', icon: 'warranty' }, // TODO: swap to the recall icon
38
+ };
39
+ export const ReadOnlyWarrantyBanner = ({ icon, value,
40
+ // Defaults to the NEUTRAL state, not to "covered". The only production
41
+ // caller spreads field data (ReadOnlyDynamicForm), so a warranty field that
42
+ // arrives without an explicit state used to assert coverage by omission —
43
+ // the same fail-unsafe default as the STATE lookup below. Callers that mean
44
+ // "under warranty" must say so.
45
+ state = 'unverified', link, onAction, actionLabel, }) => {
46
+ var _a;
47
+ // No data → render nothing. Never fabricate a status (critical for recall).
48
+ if (!value)
49
+ return null;
50
+ // Fail SAFE on an unrecognised state: degrade to "we don't know", never to
51
+ // "you are covered". This is not hypothetical — a caller on a NEWER build
52
+ // than the installed package can send a state this copy has no key for (e.g.
53
+ // 'unverified' against 2.60.0), and a green fallback would assert coverage
54
+ // the data cannot support. Neutral is the only defensible default.
55
+ const s = (_a = STATE[state]) !== null && _a !== void 0 ? _a : STATE.unverified;
56
+ // Field-provided icon overrides; otherwise the state's own (swappable) icon.
57
+ const iconKey = icon !== null && icon !== void 0 ? icon : s.icon;
58
+ const bannerIcon = iconKey ? fieldIcons[iconKey] : '';
59
+ // States drawn as a typographic glyph rather than an image asset. Both are
60
+ // punctuation the brand face already carries, so neither needs an icon file.
61
+ const glyph = state === 'recall' ? '!' : state === 'unverified' ? '?' : '';
62
+ return (_jsxs(Flex, { align: "center", gap: "10px", minH: "76px", pl: "10px", pr: "base", bg: s.bg, mt: "2px", children: [_jsx(Flex, { align: "center", justify: "center", flexShrink: 0, boxSize: "45px", borderRadius: "full", bg: s.badge, children: glyph ? (
63
+ // Typographic glyph sized to the internal icon (29px): "!" for
64
+ // recall, "?" for unverified. Recall keeps extra-bold white on its
65
+ // saturated badge; unverified is exact XD — Poppins SemiBold (600)
66
+ // in #5F7482, a desaturated slate that reads on the pale badge
67
+ // without competing with the message.
68
+ _jsx(Text, { fontFamily: "primary", fontWeight: state === 'recall' ? 'extrabold' : '600', fontSize: "29px", lineHeight: "1", color: state === 'recall' ? 'neutral.white' : '#5F7482', children: glyph })) : (bannerIcon && (_jsx(Image, { src: bannerIcon, alt: "warranty status", w: "27px", h: "29px", objectFit: "contain" }))) }), link ? (_jsx(Link, { href: link, isExternal: true, fontFamily: "primary", fontSize: "16px", lineHeight: "20px", letterSpacing: "0", color: "gray.3", sx: { textWrap: 'balance' }, children: String(value) })) : (_jsx(Text, { fontFamily: "primary", fontSize: "16px", lineHeight: "20px", letterSpacing: "0", color: "gray.3", sx: { textWrap: 'balance' }, children: String(value) })), onAction && (_jsx(Circle, { as: "button", type: "button", onClick: onAction, "aria-label": actionLabel !== null && actionLabel !== void 0 ? actionLabel : t('buttons.forward'), size: "40px", bg: "neutral.white", flexShrink: 0, ml: "auto", transition: "all 0.2s ease-in-out", _hover: { boxShadow: 'lg' }, children: _jsx(Box, { transform: "rotate(180deg)", lineHeight: "0", children: _jsx(ArrowLeft, { stroke: colors.blue[2], size: 24 }) }) }))] }));
69
+ };
@@ -4,4 +4,8 @@ export * from './ReadOnlyGuidelines';
4
4
  export * from './ReadOnlyHomeItemImages';
5
5
  export * from './ReadOnlyHomeItemRating';
6
6
  export * from './ReadOnlyHomeItemText';
7
+ export * from './ReadOnlyHistory';
7
8
  export * from './ReadOnlyItemRelated';
9
+ export * from './ReadOnlyOverview';
10
+ export * from './ReadOnlyPurchaseDetails';
11
+ export * from './ReadOnlyWarrantyBanner';
@@ -4,4 +4,8 @@ export * from './ReadOnlyGuidelines';
4
4
  export * from './ReadOnlyHomeItemImages';
5
5
  export * from './ReadOnlyHomeItemRating';
6
6
  export * from './ReadOnlyHomeItemText';
7
+ export * from './ReadOnlyHistory';
7
8
  export * from './ReadOnlyItemRelated';
9
+ export * from './ReadOnlyOverview';
10
+ export * from './ReadOnlyPurchaseDetails';
11
+ export * from './ReadOnlyWarrantyBanner';
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Flex, Tab, TabList, TabPanel, TabPanels, Tabs, } from '@chakra-ui/react';
3
3
  export const TabsHeader = ({ onChange, tabList, tabIndex, defaultIndex, rightButton, rightTabList, }) => {
4
- return (_jsxs(Tabs, { zIndex: "base", variant: "unstyled", onChange: onChange, index: tabIndex, defaultIndex: defaultIndex, h: "100%", children: [_jsx(Box, { bg: "lightBlue.2", children: _jsxs(Flex, { justify: "space-between", children: [_jsx(TabList, { bg: "neutral.white", w: "full", children: tabList === null || tabList === void 0 ? void 0 : tabList.map(({ label }) => (_jsx(CustomTab, { label: label }, label))) }), _jsxs(Flex, { flexShrink: 0, children: [_jsx(TabList, { bg: "neutral.white", w: "full", children: rightTabList === null || rightTabList === void 0 ? void 0 : rightTabList.map(({ label }) => (_jsx(CustomTab, { label: label }, label))) }), rightButton] })] }) }), _jsx(TabPanels, { h: "100%", children: tabList === null || tabList === void 0 ? void 0 : tabList.map(({ label, component }) => (_jsx(TabPanel, { p: "0", h: "100%", children: component }, label))) })] }));
4
+ return (_jsxs(Tabs, { zIndex: "base", variant: "unstyled", onChange: onChange, index: tabIndex, defaultIndex: defaultIndex, h: "100%", children: [_jsx(Box, { bg: "lightBlue.2", children: _jsxs(Flex, { justify: "space-between", children: [_jsx(TabList, { bg: "neutral.white", w: "full", children: tabList === null || tabList === void 0 ? void 0 : tabList.map(({ label }) => (_jsx(CustomTab, { label: label }, label))) }), _jsxs(Flex, { flexShrink: 0, children: [_jsx(TabList, { bg: "neutral.white", w: "full", children: rightTabList === null || rightTabList === void 0 ? void 0 : rightTabList.map(({ label }) => (_jsx(CustomTab, { label: label }, label))) }), rightButton] })] }) }), _jsx(TabPanels, { h: "100%", children: [...(tabList !== null && tabList !== void 0 ? tabList : []), ...(rightTabList !== null && rightTabList !== void 0 ? rightTabList : [])].map(({ label, component }) => (_jsx(TabPanel, { p: "0", h: "100%", children: component }, label))) })] }));
5
5
  };
6
6
  const CustomTab = ({ label = '' }) => {
7
7
  return (_jsx(Tab, { minW: "1rem", px: "base", py: "0", lineHeight: "10", textTransform: "uppercase", fontWeight: "normal", fontSize: "sm", fontFamily: "primary", color: "blue.2", borderBottom: "3px solid transparent", _hover: { cursor: 'pointer' }, _focus: { outline: 'none' }, _selected: {
@@ -0,0 +1,7 @@
1
+ export interface AddDocumentsI {
2
+ title?: string;
3
+ message?: string;
4
+ /** "browse" affordance — the consumer wires the real file picker/dropzone. */
5
+ onBrowse?: () => void;
6
+ }
7
+ export declare const AddDocuments: ({ title, message, onBrowse, }: AddDocumentsI) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Flex, Link, Stack, Text } from '@chakra-ui/react';
3
+ import { Upload } from '../../components';
4
+ // Add-documents zone atop the History tab — XD "Assembled – 1".
5
+ // Section sits on the dark rail color (#E9EDEF); inside, a 72px dashed drop
6
+ // area (bg rgba(219,236,246,.6), 1px dashed #707070, radius 4px). The message
7
+ // is Assistant 16px #606060; "browse" is a Poppins Medium 16px #2487C5 link.
8
+ export const AddDocuments = ({ title = 'Add Notes, Documents and Service Records', message = 'Drag and drop media here or', onBrowse, }) => (_jsxs(Stack, { spacing: "base", p: "base", bg: "#E9EDEF", children: [_jsx(Text, { fontFamily: "secondary", fontSize: "16px", color: "#606060", children: title }), _jsxs(Flex, { h: "72px", align: "center", justify: "center", gap: "2", bg: "rgba(219, 236, 246, 0.6)", border: "1px dashed", borderColor: "rgba(112, 112, 112, 0.45)", borderRadius: "4px", children: [_jsx(Upload, {}), _jsx(Text, { fontFamily: "secondary", fontSize: "16px", color: "#606060", children: message }), _jsx(Link, { fontFamily: "secondary", fontSize: "16px", color: "#2487C5", onClick: onBrowse, children: "browse" })] })] }));
@@ -0,0 +1,7 @@
1
+ export interface DatePennantI {
2
+ /** Display date, e.g. "01 23 2017". */
3
+ date?: string;
4
+ /** Pennant fill — data-driven per event type (a legend maps color→type). */
5
+ color?: string;
6
+ }
7
+ export declare const DatePennant: ({ date, color }: DatePennantI) => import("react/jsx-runtime").JSX.Element;