@homefile/components-v2 2.9.4 → 2.11.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 (40) hide show
  1. package/dist/components/forms/dynamicForm/DynamicForm.d.ts +1 -1
  2. package/dist/components/forms/dynamicForm/DynamicForm.js +6 -4
  3. package/dist/components/forms/dynamicForm/fields/AIGridField.d.ts +2 -0
  4. package/dist/components/forms/dynamicForm/fields/AIGridField.js +37 -0
  5. package/dist/components/forms/dynamicForm/fields/GridField.d.ts +2 -2
  6. package/dist/components/forms/dynamicForm/fields/GridField.js +5 -3
  7. package/dist/components/forms/dynamicForm/fields/SingleImage.d.ts +2 -0
  8. package/dist/components/forms/dynamicForm/fields/SingleImage.js +17 -0
  9. package/dist/components/forms/dynamicForm/fields/index.d.ts +2 -0
  10. package/dist/components/forms/dynamicForm/fields/index.js +2 -0
  11. package/dist/components/icons/Camera.js +2 -2
  12. package/dist/components/inputs/MobileFileUploader.d.ts +1 -1
  13. package/dist/components/inputs/MobileFileUploader.js +2 -2
  14. package/dist/hooks/folderPanel/useFilesUploader.d.ts +1 -1
  15. package/dist/hooks/folderPanel/useFilesUploader.js +1 -1
  16. package/dist/interfaces/forms/dynamicForm/DynamicForm.interface.d.ts +3 -2
  17. package/dist/interfaces/forms/dynamicForm/fields/FileField.interface.d.ts +2 -1
  18. package/dist/interfaces/forms/dynamicForm/fields/GridField.interface.d.ts +14 -0
  19. package/dist/interfaces/forms/dynamicForm/fields/GridField.interface.js +1 -0
  20. package/dist/interfaces/forms/dynamicForm/fields/index.d.ts +1 -0
  21. package/dist/interfaces/forms/dynamicForm/fields/index.js +1 -0
  22. package/dist/interfaces/inputs/EditFileUploader.interface.d.ts +1 -0
  23. package/dist/mocks/forms/dynamicForm.mock.js +30 -2
  24. package/dist/stories/forms/dynamicForm/DynamicForm.stories.js +2 -1
  25. package/package.json +1 -1
  26. package/src/components/forms/dynamicForm/DynamicForm.tsx +19 -1
  27. package/src/components/forms/dynamicForm/fields/AIGridField.tsx +72 -0
  28. package/src/components/forms/dynamicForm/fields/GridField.tsx +14 -3
  29. package/src/components/forms/dynamicForm/fields/SingleImage.tsx +54 -0
  30. package/src/components/forms/dynamicForm/fields/index.ts +2 -0
  31. package/src/components/icons/Camera.tsx +29 -3
  32. package/src/components/inputs/MobileFileUploader.tsx +4 -1
  33. package/src/hooks/folderPanel/useFilesUploader.ts +2 -2
  34. package/src/interfaces/forms/dynamicForm/DynamicForm.interface.ts +10 -1
  35. package/src/interfaces/forms/dynamicForm/fields/FileField.interface.ts +2 -1
  36. package/src/interfaces/forms/dynamicForm/fields/GridField.interface.ts +16 -0
  37. package/src/interfaces/forms/dynamicForm/fields/index.ts +1 -0
  38. package/src/interfaces/inputs/EditFileUploader.interface.ts +1 -0
  39. package/src/mocks/forms/dynamicForm.mock.ts +30 -2
  40. package/src/stories/forms/dynamicForm/DynamicForm.stories.tsx +2 -1
@@ -1,2 +1,2 @@
1
1
  import { DynamicFormI } from '../../../interfaces';
2
- export declare const DynamicForm: ({ callback, displayImages, form: fields, menuItems, onRemoveImage, onUpload, showTitle, title, uploadingFieldId, ...props }: DynamicFormI) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const DynamicForm: ({ callback, displayImages, form: fields, menuItems, onAISend, onRemoveImage, onUpload, showTitle, title, uploadingFieldId, ...props }: DynamicFormI) => import("react/jsx-runtime").JSX.Element;
@@ -10,15 +10,15 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { createElement as _createElement } from "react";
13
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
14
  import { FormProvider } from 'react-hook-form';
15
15
  import { t } from 'i18next';
16
16
  import { Box, Stack, Text } from '@chakra-ui/react';
17
- import { HiddenFieldSection, GroupField, TextField, TextAreaField, SelectField, RatingField, GridField, FieldWithDelete, FileField, SwitchField, DateField, NumberField, CurrencyField, TileBody, CheckboxGroupField, CheckboxAgreement, } from '../..';
17
+ import { HiddenFieldSection, GroupField, TextField, TextAreaField, SelectField, RatingField, GridField, FieldWithDelete, FileField, SwitchField, DateField, NumberField, CurrencyField, TileBody, CheckboxGroupField, CheckboxAgreement, AIGridField, } from '../..';
18
18
  import { useDynamicForm } from '../../../hooks';
19
19
  import { fieldIcons } from '../../../helpers';
20
20
  export const DynamicForm = (_a) => {
21
- var { callback, displayImages, form: fields, menuItems, onRemoveImage, onUpload, showTitle = true, title, uploadingFieldId } = _a, props = __rest(_a, ["callback", "displayImages", "form", "menuItems", "onRemoveImage", "onUpload", "showTitle", "title", "uploadingFieldId"]);
21
+ var { callback, displayImages, form: fields, menuItems, onAISend, onRemoveImage, onUpload, showTitle = true, title, uploadingFieldId } = _a, props = __rest(_a, ["callback", "displayImages", "form", "menuItems", "onAISend", "onRemoveImage", "onUpload", "showTitle", "title", "uploadingFieldId"]);
22
22
  const { form, visibleFields, hiddenFields, handleAddAll, handleAdd, handleFilesUpload, handleRemove, } = useDynamicForm({ fields, onUpload });
23
23
  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) => {
24
24
  const { canBeHidden, children, description, icon, id, name, options, type, value, } = field;
@@ -26,7 +26,7 @@ export const DynamicForm = (_a) => {
26
26
  id,
27
27
  value,
28
28
  icon: icon ? fieldIcons[icon] : undefined,
29
- placeholder: (name && name !== "") ? name : description,
29
+ placeholder: Boolean(name) ? name : description,
30
30
  };
31
31
  const fieldWithDeleteBaseProps = {
32
32
  id,
@@ -62,6 +62,8 @@ export const DynamicForm = (_a) => {
62
62
  case 'rating':
63
63
  return (_createElement(FieldWithDelete, Object.assign({}, fieldWithDeleteBaseProps, { key: id }),
64
64
  _jsx(RatingField, Object.assign({}, baseProps))));
65
+ case 'ai-grid':
66
+ return (_jsxs(Stack, { p: "base", spacing: "base", bg: "lightBlue.2", children: [description && (_jsx(Text, { fontFamily: "secondary", children: description })), _jsx(AIGridField, Object.assign({}, baseProps, { onRemove: onRemoveImage, onUpload: (files) => handleFilesUpload({ id, files }), onAISend: onAISend, children: children }))] }, id));
65
67
  case 'grid':
66
68
  return (_createElement(FieldWithDelete, Object.assign({}, fieldWithDeleteBaseProps, { key: id }),
67
69
  _jsx(GridField, Object.assign({}, baseProps, { children: children }))));
@@ -0,0 +1,2 @@
1
+ import { AIGridFieldI } from '../../../../interfaces';
2
+ export declare const AIGridField: ({ children, onAISend, onRemove, onUpload, }: AIGridFieldI) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useFormContext } from 'react-hook-form';
3
+ import { Flex, IconButton, Text } from '@chakra-ui/react';
4
+ import { TextField, SingleImage, Plus } from '../../..';
5
+ import { fieldIcons } from '../../../../helpers';
6
+ import { colors } from '../../../../theme/colors';
7
+ export const AIGridField = ({ children, onAISend, onRemove, onUpload, }) => {
8
+ var _a, _b, _c, _d, _e, _f;
9
+ const { watch } = useFormContext();
10
+ const textChild = children === null || children === void 0 ? void 0 : children.find(({ type }) => type === 'text' || type === 'string');
11
+ const textProps = {
12
+ id: (_a = textChild === null || textChild === void 0 ? void 0 : textChild.id) !== null && _a !== void 0 ? _a : '',
13
+ value: (_b = textChild === null || textChild === void 0 ? void 0 : textChild.value) !== null && _b !== void 0 ? _b : '',
14
+ placeholder: (_c = textChild === null || textChild === void 0 ? void 0 : textChild.name) !== null && _c !== void 0 ? _c : textChild === null || textChild === void 0 ? void 0 : textChild.description,
15
+ icon: (textChild === null || textChild === void 0 ? void 0 : textChild.icon)
16
+ ? fieldIcons[textChild.icon]
17
+ : undefined,
18
+ };
19
+ const imageField = children === null || children === void 0 ? void 0 : children.find(({ type }) => type === 'ai-image');
20
+ const imageProps = {
21
+ id: (_d = imageField === null || imageField === void 0 ? void 0 : imageField.id) !== null && _d !== void 0 ? _d : '',
22
+ value: (_e = imageField === null || imageField === void 0 ? void 0 : imageField.value) !== null && _e !== void 0 ? _e : '',
23
+ placeholder: (_f = imageField === null || imageField === void 0 ? void 0 : imageField.name) !== null && _f !== void 0 ? _f : imageField === null || imageField === void 0 ? void 0 : imageField.description,
24
+ icon: (imageField === null || imageField === void 0 ? void 0 : imageField.icon)
25
+ ? fieldIcons[imageField.icon]
26
+ : undefined,
27
+ };
28
+ const code = watch(String(textChild === null || textChild === void 0 ? void 0 : textChild.id));
29
+ const handleAISend = () => {
30
+ const form = {
31
+ code,
32
+ image: imageField === null || imageField === void 0 ? void 0 : imageField.value,
33
+ };
34
+ onAISend === null || onAISend === void 0 ? void 0 : onAISend(form);
35
+ };
36
+ return (_jsxs(Flex, { align: "center", gap: "base", children: [_jsx(SingleImage, Object.assign({}, imageProps, { onUpload: onUpload, onRemove: onRemove, value: imageField === null || imageField === void 0 ? void 0 : imageField.value })), _jsx(Text, { fontFamily: "secondary", textAlign: "center", children: "OR" }), _jsx(TextField, Object.assign({}, textProps)), _jsx(IconButton, { "aria-label": "Add new address line", variant: "iconOutlined", icon: _jsx(Plus, { size: 28, stroke: colors.blue[3] }), onClick: handleAISend, h: "input.md", disabled: !(code === null || code === void 0 ? void 0 : code.length) && !(imageField === null || imageField === void 0 ? void 0 : imageField.value) })] }));
37
+ };
@@ -1,2 +1,2 @@
1
- import { ReportI } from '../../../../interfaces';
2
- export declare const GridField: ({ children }: Pick<ReportI, "children">) => import("react/jsx-runtime").JSX.Element;
1
+ import { GridFieldI } from '../../../../interfaces';
2
+ export declare const GridField: ({ children, onRemove, onUpload }: GridFieldI) => import("react/jsx-runtime").JSX.Element;
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createElement as _createElement } from "react";
3
3
  import { Flex } from '@chakra-ui/react';
4
- import { DateField, NumberField, TextField, SelectField, CurrencyField, } from '../../..';
4
+ import { DateField, NumberField, TextField, SelectField, CurrencyField, SingleImage, } from '../../..';
5
5
  import { fieldIcons } from '../../../../helpers';
6
- export const GridField = ({ children }) => {
6
+ export const GridField = ({ children, onRemove, onUpload }) => {
7
7
  return (_jsx(Flex, { align: "stretch", gap: "base", children: children === null || children === void 0 ? void 0 : children.map(({ id, description, name, value, type, options = [], icon }) => {
8
8
  const baseProps = {
9
9
  id,
10
10
  value,
11
- placeholder: (name && name !== "") ? name : description,
11
+ placeholder: name && name !== '' ? name : description,
12
12
  icon: icon ? fieldIcons[icon] : undefined,
13
13
  };
14
14
  switch (type) {
@@ -27,6 +27,8 @@ export const GridField = ({ children }) => {
27
27
  return _createElement(SelectField, Object.assign({}, baseProps, { key: id, options: options }));
28
28
  case 'telephone':
29
29
  return _createElement(TextField, Object.assign({}, baseProps, { key: id, type: "tel" }));
30
+ case 'ai-image':
31
+ return (_createElement(SingleImage, Object.assign({}, baseProps, { key: id, onUpload: onUpload, onRemove: onRemove, value: value })));
30
32
  default:
31
33
  return null;
32
34
  }
@@ -0,0 +1,2 @@
1
+ import { FileFieldI } from '../../../../interfaces';
2
+ export declare const SingleImage: ({ onRemove, onUpload, uploading, value, }: FileFieldI) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { AspectRatio, Box, Flex, IconButton, Image } from '@chakra-ui/react';
3
+ import { colors } from '../../../../theme/colors';
4
+ import { Close, MobileFileUploader } from '../../..';
5
+ import { handleMapFile } from '../../../../utils';
6
+ export const SingleImage = ({ onRemove, onUpload, uploading = false, value, }) => {
7
+ const handleInputChange = (event) => {
8
+ var _a;
9
+ const fileList = (_a = event.target.files) !== null && _a !== void 0 ? _a : [];
10
+ const files = Array.from(fileList);
11
+ if (files.length > 0) {
12
+ const selectedFiles = handleMapFile({ files });
13
+ onUpload === null || onUpload === void 0 ? void 0 : onUpload(selectedFiles);
14
+ }
15
+ };
16
+ return (_jsxs(Flex, { gap: "2", align: "center", h: "input.md", children: [_jsx(MobileFileUploader, { handleInputChange: handleInputChange, "aria-label": "upload image", minW: "72px" }), value && (_jsxs(Box, { position: "relative", minW: "30px", children: [_jsx(IconButton, { variant: "ghost", "aria-label": "close", maxW: "fit-content", onClick: () => onRemove === null || onRemove === void 0 ? void 0 : onRemove(value), icon: _jsx(Close, { size: 11, stroke: colors.error['2'] }), disabled: uploading, position: "absolute", bg: "neutral.white", p: "0.5", borderRadius: "full", top: "-2", right: "-2", zIndex: "1" }), _jsx(AspectRatio, { maxW: "100%", ratio: 1, children: _jsx(Image, { src: value, objectFit: "cover", alt: "image" }) })] }))] }));
17
+ };
@@ -1,3 +1,4 @@
1
+ export * from './AIGridField';
1
2
  export * from './CheckboxAgreement';
2
3
  export * from './CheckboxGroupField';
3
4
  export * from './CurrencyField';
@@ -13,6 +14,7 @@ export * from './SwitchField';
13
14
  export * from './RatingField';
14
15
  export * from './SelectField';
15
16
  export * from './SingleFields';
17
+ export * from './SingleImage';
16
18
  export * from './TextAreaField';
17
19
  export * from './TextField';
18
20
  export * from './TileBodyFields';
@@ -1,3 +1,4 @@
1
+ export * from './AIGridField';
1
2
  export * from './CheckboxAgreement';
2
3
  export * from './CheckboxGroupField';
3
4
  export * from './CurrencyField';
@@ -13,6 +14,7 @@ export * from './SwitchField';
13
14
  export * from './RatingField';
14
15
  export * from './SelectField';
15
16
  export * from './SingleFields';
17
+ export * from './SingleImage';
16
18
  export * from './TextAreaField';
17
19
  export * from './TextField';
18
20
  export * from './TileBodyFields';
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { colors } from '../../theme/colors';
3
3
  export const Camera = ({ stroke = colors.blue[3], size = 24 }) => {
4
- return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, fill: stroke, viewBox: "0 0 256 256", children: _jsx("path", { d: "M208,56H180.28L166.65,35.56A8,8,0,0,0,160,32H96a8,8,0,0,0-6.65,3.56L75.71,56H48A24,24,0,0,0,24,80V192a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V80A24,24,0,0,0,208,56Zm-44,76a36,36,0,1,1-36-36A36,36,0,0,1,164,132Z" }) }));
4
+ return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 26.734 22.146", children: _jsxs("g", { id: "Icon_feather-camera", "data-name": "Icon feather-camera", transform: "translate(-0.75 -3.75)", children: [_jsx("path", { id: "Caminho_17678", "data-name": "Caminho 17678", d: "M26.735,22.852a2.294,2.294,0,0,1-2.294,2.294H3.794A2.294,2.294,0,0,1,1.5,22.852V10.235A2.294,2.294,0,0,1,3.794,7.941H8.382L10.676,4.5h6.882l2.294,3.441H24.44a2.294,2.294,0,0,1,2.294,2.294Z", transform: "translate(0 0)", fill: "none", stroke: stroke, strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5" }), _jsx("path", { id: "Caminho_17679", "data-name": "Caminho 17679", d: "M21.176,18.088A4.588,4.588,0,1,1,16.588,13.5,4.588,4.588,0,0,1,21.176,18.088Z", transform: "translate(-2.471 -2.118)", fill: "none", stroke: stroke, strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.5" })] }) }));
5
5
  };
@@ -1,2 +1,2 @@
1
1
  import { EditFileUploaderI } from '../../interfaces';
2
- export declare const MobileFileUploader: ({ handleInputChange, ...props }: EditFileUploaderI) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const MobileFileUploader: ({ handleInputChange, multiple, ...props }: EditFileUploaderI) => import("react/jsx-runtime").JSX.Element;
@@ -15,7 +15,7 @@ import { t } from 'i18next';
15
15
  import { IconButton } from '@chakra-ui/react';
16
16
  import { Camera } from '..';
17
17
  export const MobileFileUploader = (_a) => {
18
- var { handleInputChange } = _a, props = __rest(_a, ["handleInputChange"]);
18
+ var { handleInputChange, multiple = true } = _a, props = __rest(_a, ["handleInputChange", "multiple"]);
19
19
  const hiddenFileInput = useRef(null);
20
- return (_jsxs(_Fragment, { children: [_jsx(IconButton, Object.assign({}, props, { variant: "iconOutlined", h: "2.3rem", icon: _jsx(Camera, {}), onClick: () => { var _a; return (_a = hiddenFileInput === null || hiddenFileInput === void 0 ? void 0 : hiddenFileInput.current) === null || _a === void 0 ? void 0 : _a.click(); } })), _jsx("input", { style: { display: 'none' }, accept: "image/*", type: "file", name: "imageUpload", ref: hiddenFileInput, placeholder: t('addMedia.placeholder'), onChange: handleInputChange })] }));
20
+ return (_jsxs(_Fragment, { children: [_jsx(IconButton, Object.assign({}, props, { variant: "iconOutlined", minH: "40px", h: "full", icon: _jsx(Camera, {}), onClick: () => { var _a; return (_a = hiddenFileInput === null || hiddenFileInput === void 0 ? void 0 : hiddenFileInput.current) === null || _a === void 0 ? void 0 : _a.click(); } })), _jsx("input", { style: { display: 'none' }, accept: "image/*", type: "file", name: "imageUpload", ref: hiddenFileInput, placeholder: t('addMedia.placeholder'), onChange: handleInputChange, multiple: multiple })] }));
21
21
  };
@@ -1,6 +1,6 @@
1
1
  import { FolderFileI } from '../../interfaces';
2
2
  interface FilesUploaderHookI {
3
- onUpload: (files: FolderFileI[]) => void;
3
+ onUpload?: (files: FolderFileI[]) => void;
4
4
  uploading: boolean;
5
5
  displayImages?: string[];
6
6
  }
@@ -35,7 +35,7 @@ export const useFilesUploader = ({ onUpload, uploading, displayImages = [], }) =
35
35
  }
36
36
  }, [hasError]);
37
37
  useEffect(() => {
38
- onUpload(acceptedFiles);
38
+ onUpload === null || onUpload === void 0 ? void 0 : onUpload(acceptedFiles);
39
39
  }, [acceptedFiles]);
40
40
  useEffect(() => {
41
41
  setIsUploading(uploading);
@@ -1,5 +1,5 @@
1
- import { FolderFileI, MenuItemI, PartnerFooterI, TileCtaI } from '../..';
2
- export type KindTypes = 'checkbox' | 'checkbox-agreement' | 'checkbox-group' | 'currency' | 'date' | 'email' | 'file' | 'grid' | 'group' | 'hidden' | 'notes' | 'number' | 'radio' | 'rating' | 'select' | 'string' | 'switch' | 'telephone' | 'text' | 'textarea' | 'default' | UIKindTypes | HomeItemTypes;
1
+ import { AIGridFieldI, FolderFileI, MenuItemI, PartnerFooterI, TileCtaI } from '../..';
2
+ export type KindTypes = 'ai-image' | 'ai-grid' | 'checkbox' | 'checkbox-agreement' | 'checkbox-group' | 'currency' | 'date' | 'email' | 'file' | 'grid' | 'group' | 'hidden' | 'notes' | 'number' | 'radio' | 'rating' | 'select' | 'string' | 'switch' | 'telephone' | 'text' | 'textarea' | 'default' | UIKindTypes | HomeItemTypes;
3
3
  export type UIKindTypes = 'tile-body' | 'tile-body-logo' | 'tile-body-header' | 'tile-body-section' | 'tile-body-section-grid' | 'tile-form' | 'tile-body-action' | 'vertical-icon' | 'primary-cta' | 'secondary-cta';
4
4
  export type HomeItemTypes = 'appliances' | 'images' | 'guidelines' | 'item-related' | 'item-icon-btn';
5
5
  export type IconTypes = 'barcode' | 'battery' | 'billing' | 'book' | 'book-opened' | 'calc' | 'calendar' | 'check' | 'co2' | 'contact' | 'date' | 'default' | 'detector' | 'electricity' | 'goldbars' | 'heart' | 'image' | 'notes' | 'palette' | 'people' | 'price' | 'rating' | 'receipt' | 'registry' | 'sprinkler' | 'tools' | 'wind' | '68' | 'calendar2' | 'water' | 'calendar-drop' | 'umbrella' | 'heater' | 'roof' | 'foundation' | 'solar-panel' | 'pool' | 'drop' | 'mobile-drop' | 'light' | 'plate' | 'pressure-washer' | 'house' | 'target' | 'title' | 'company' | UIIconTypes;
@@ -25,6 +25,7 @@ export interface DynamicFormI extends Partial<PartnerFooterI> {
25
25
  displayImages?: string[];
26
26
  form: ReportI[];
27
27
  menuItems?: MenuItemI[];
28
+ onAISend?: AIGridFieldI['onAISend'];
28
29
  onRemoveImage?: (file: string) => void;
29
30
  onUpload?: (filesByFieldId: Record<string, FolderFileI[]>) => void;
30
31
  showTitle?: boolean;
@@ -1,7 +1,8 @@
1
1
  import { FolderFileI, ReportI } from '../../..';
2
2
  export interface FileFieldI extends Pick<ReportI, 'description' | 'icon'> {
3
3
  onRemove?: (file: string) => void;
4
- onUpload: (files: FolderFileI[]) => void;
4
+ onUpload?: (files: FolderFileI[]) => void;
5
5
  uploading?: boolean;
6
6
  displayImages?: string[];
7
+ value?: string;
7
8
  }
@@ -0,0 +1,14 @@
1
+ import { FolderFileI, ReportI } from '../../..';
2
+ export interface GridFieldI {
3
+ children: ReportI['children'];
4
+ onRemove?: (file: string) => void;
5
+ onUpload?: (files: FolderFileI[]) => void;
6
+ }
7
+ export interface AIGridFieldI extends GridFieldI {
8
+ onAISend?: (form?: AIFormI) => void;
9
+ }
10
+ interface AIFormI {
11
+ code?: string;
12
+ image?: string;
13
+ }
14
+ export {};
@@ -2,6 +2,7 @@ export * from './DateField.interface';
2
2
  export * from './FieldDescription.interface';
3
3
  export * from './FieldWithDelete.interface';
4
4
  export * from './FileField.interface';
5
+ export * from './GridField.interface';
5
6
  export * from './GroupField.interface';
6
7
  export * from './RatingField.interface';
7
8
  export * from './SelectField.interface';
@@ -2,6 +2,7 @@ export * from './DateField.interface';
2
2
  export * from './FieldDescription.interface';
3
3
  export * from './FieldWithDelete.interface';
4
4
  export * from './FileField.interface';
5
+ export * from './GridField.interface';
5
6
  export * from './GroupField.interface';
6
7
  export * from './RatingField.interface';
7
8
  export * from './SelectField.interface';
@@ -2,4 +2,5 @@ import { ChangeEvent } from 'react';
2
2
  import { IconButtonProps } from '@chakra-ui/react';
3
3
  export interface EditFileUploaderI extends IconButtonProps {
4
4
  handleInputChange: (event: ChangeEvent<HTMLInputElement>) => void;
5
+ multiple?: boolean;
5
6
  }
@@ -9,6 +9,34 @@ export const formFieldsMock = [
9
9
  type: 'checkbox-agreement',
10
10
  visible: true,
11
11
  },
12
+ {
13
+ id: faker.database.mongodbObjectId(),
14
+ name: 'ai form',
15
+ description: 'Add item details',
16
+ value: false,
17
+ type: 'ai-grid',
18
+ visible: true,
19
+ children: [
20
+ {
21
+ id: faker.database.mongodbObjectId(),
22
+ name: 'Model or Serial',
23
+ description: faker.lorem.sentence(),
24
+ comments: faker.lorem.sentence(),
25
+ value: '',
26
+ type: 'text',
27
+ visible: true,
28
+ },
29
+ {
30
+ id: faker.database.mongodbObjectId(),
31
+ name: 'uploder',
32
+ description: faker.lorem.sentence(),
33
+ comments: faker.lorem.sentence(),
34
+ value: faker.image.urlPicsumPhotos(),
35
+ type: 'ai-image',
36
+ visible: true,
37
+ },
38
+ ],
39
+ },
12
40
  {
13
41
  id: faker.database.mongodbObjectId(),
14
42
  name: '',
@@ -137,7 +165,7 @@ export const formFieldsMock = [
137
165
  name: 'Purchase Date',
138
166
  description: faker.lorem.sentence(),
139
167
  comments: faker.lorem.sentence(),
140
- value: faker.date.past().toLocaleDateString(),
168
+ value: '',
141
169
  type: 'date',
142
170
  visible: true,
143
171
  },
@@ -240,7 +268,7 @@ export const formFieldsMock = [
240
268
  name: 'Due Date',
241
269
  description: faker.lorem.sentence(),
242
270
  comments: faker.lorem.sentence(),
243
- value: faker.date.future().toLocaleDateString(),
271
+ value: '',
244
272
  type: 'date',
245
273
  visible: true,
246
274
  icon: 'date',
@@ -19,6 +19,7 @@ export default {
19
19
  faker.image.urlPicsumPhotos(),
20
20
  faker.image.urlPicsumPhotos(),
21
21
  ],
22
+ onAiSend: action('onAiSend'),
22
23
  onUpload: action('onUpload'),
23
24
  menuItems: menuMock,
24
25
  socialLinks: socialLinksMock,
@@ -26,7 +27,7 @@ export default {
26
27
  },
27
28
  };
28
29
  export const DynamicFormComponent = (args) => {
29
- return (_jsx(Box, { p: "base", bg: "neutral.white", w: ['full', '500px'], children: _jsx(DynamicForm, Object.assign({}, args, { form: formFieldsMock })) }));
30
+ return (_jsx(Box, { p: "base", bg: "neutral.white", w: ['full', '500px'], children: _jsx(DynamicForm, Object.assign({}, args, { form: formFieldsMock, showTitle: false })) }));
30
31
  };
31
32
  export const DynamicUIFormComponent = (args) => {
32
33
  return (_jsx(Box, { m: "base", w: ['full', '320px'], children: _jsx(DynamicForm, Object.assign({}, args, { form: tileUIMock, showTitle: false, callback: action('callback') })) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homefile/components-v2",
3
- "version": "2.9.4",
3
+ "version": "2.11.0",
4
4
  "author": "Homefile",
5
5
  "license": "UNLICENSED",
6
6
  "typings": "dist/index.d.ts",
@@ -19,6 +19,7 @@ import {
19
19
  TileBody,
20
20
  CheckboxGroupField,
21
21
  CheckboxAgreement,
22
+ AIGridField,
22
23
  } from '@/components'
23
24
  import { useDynamicForm } from '@/hooks'
24
25
  import { fieldIcons } from '@/helpers'
@@ -28,6 +29,7 @@ export const DynamicForm = ({
28
29
  displayImages,
29
30
  form: fields,
30
31
  menuItems,
32
+ onAISend,
31
33
  onRemoveImage,
32
34
  onUpload,
33
35
  showTitle = true,
@@ -77,7 +79,7 @@ export const DynamicForm = ({
77
79
  id,
78
80
  value,
79
81
  icon: icon ? (fieldIcons[icon] as IconTypes) : undefined,
80
- placeholder: (name && name !== "") ? name : description,
82
+ placeholder: Boolean(name) ? name : description,
81
83
  }
82
84
  const fieldWithDeleteBaseProps = {
83
85
  id,
@@ -140,6 +142,22 @@ export const DynamicForm = ({
140
142
  <RatingField {...baseProps} />
141
143
  </FieldWithDelete>
142
144
  )
145
+ case 'ai-grid':
146
+ return (
147
+ <Stack key={id} p="base" spacing="base" bg="lightBlue.2">
148
+ {description && (
149
+ <Text fontFamily="secondary">{description}</Text>
150
+ )}
151
+ <AIGridField
152
+ {...baseProps}
153
+ onRemove={onRemoveImage}
154
+ onUpload={(files) => handleFilesUpload({ id, files })}
155
+ onAISend={onAISend}
156
+ >
157
+ {children}
158
+ </AIGridField>
159
+ </Stack>
160
+ )
143
161
  case 'grid':
144
162
  return (
145
163
  <FieldWithDelete {...fieldWithDeleteBaseProps} key={id}>
@@ -0,0 +1,72 @@
1
+ import { useFormContext } from 'react-hook-form'
2
+ import { Flex, IconButton, Text } from '@chakra-ui/react'
3
+ import { IconTypes, AIGridFieldI } from '@/interfaces'
4
+ import { TextField, SingleImage, Plus } from '@/components'
5
+ import { fieldIcons } from '@/helpers'
6
+ import { colors } from '@/theme/colors'
7
+
8
+ export const AIGridField = ({
9
+ children,
10
+ onAISend,
11
+ onRemove,
12
+ onUpload,
13
+ }: AIGridFieldI) => {
14
+ const { watch } = useFormContext()
15
+
16
+ const textChild = children?.find(
17
+ ({ type }) => type === 'text' || type === 'string'
18
+ )
19
+
20
+ const textProps = {
21
+ id: textChild?.id ?? '',
22
+ value: textChild?.value ?? '',
23
+ placeholder: textChild?.name ?? textChild?.description,
24
+ icon: textChild?.icon
25
+ ? (fieldIcons[textChild.icon] as IconTypes)
26
+ : undefined,
27
+ }
28
+
29
+ const imageField = children?.find(({ type }) => type === 'ai-image')
30
+
31
+ const imageProps = {
32
+ id: imageField?.id ?? '',
33
+ value: imageField?.value ?? '',
34
+ placeholder: imageField?.name ?? imageField?.description,
35
+ icon: imageField?.icon
36
+ ? (fieldIcons[imageField.icon] as IconTypes)
37
+ : undefined,
38
+ }
39
+
40
+ const code = watch(String(textChild?.id))
41
+
42
+ const handleAISend = () => {
43
+ const form = {
44
+ code,
45
+ image: imageField?.value as string,
46
+ }
47
+ onAISend?.(form)
48
+ }
49
+
50
+ return (
51
+ <Flex align="center" gap="base">
52
+ <SingleImage
53
+ {...imageProps}
54
+ onUpload={onUpload}
55
+ onRemove={onRemove}
56
+ value={imageField?.value as string}
57
+ />
58
+ <Text fontFamily="secondary" textAlign="center">
59
+ OR
60
+ </Text>
61
+ <TextField {...textProps} />
62
+ <IconButton
63
+ aria-label="Add new address line"
64
+ variant="iconOutlined"
65
+ icon={<Plus size={28} stroke={colors.blue[3]} />}
66
+ onClick={handleAISend}
67
+ h="input.md"
68
+ disabled={!code?.length && !imageField?.value}
69
+ />
70
+ </Flex>
71
+ )
72
+ }
@@ -1,15 +1,16 @@
1
1
  import { Flex } from '@chakra-ui/react'
2
- import { IconTypes, ReportI } from '@/interfaces'
2
+ import { IconTypes, GridFieldI } from '@/interfaces'
3
3
  import {
4
4
  DateField,
5
5
  NumberField,
6
6
  TextField,
7
7
  SelectField,
8
8
  CurrencyField,
9
+ SingleImage,
9
10
  } from '@/components'
10
11
  import { fieldIcons } from '@/helpers'
11
12
 
12
- export const GridField = ({ children }: Pick<ReportI, 'children'>) => {
13
+ export const GridField = ({ children, onRemove, onUpload }: GridFieldI) => {
13
14
  return (
14
15
  <Flex align="stretch" gap="base">
15
16
  {children?.map(
@@ -17,7 +18,7 @@ export const GridField = ({ children }: Pick<ReportI, 'children'>) => {
17
18
  const baseProps = {
18
19
  id,
19
20
  value,
20
- placeholder: (name && name !== "") ? name : description,
21
+ placeholder: name && name !== '' ? name : description,
21
22
  icon: icon ? (fieldIcons[icon] as IconTypes) : undefined,
22
23
  }
23
24
 
@@ -45,6 +46,16 @@ export const GridField = ({ children }: Pick<ReportI, 'children'>) => {
45
46
  return <SelectField {...baseProps} key={id} options={options} />
46
47
  case 'telephone':
47
48
  return <TextField {...baseProps} key={id} type="tel" />
49
+ case 'ai-image':
50
+ return (
51
+ <SingleImage
52
+ {...baseProps}
53
+ key={id}
54
+ onUpload={onUpload}
55
+ onRemove={onRemove}
56
+ value={value as string}
57
+ />
58
+ )
48
59
  default:
49
60
  return null
50
61
  }
@@ -0,0 +1,54 @@
1
+ import { ChangeEvent } from 'react'
2
+ import { AspectRatio, Box, Flex, IconButton, Image } from '@chakra-ui/react'
3
+ import { colors } from '@/theme/colors'
4
+ import { Close, MobileFileUploader } from '@/components'
5
+ import { FileFieldI } from '@/interfaces'
6
+ import { handleMapFile } from '@/utils'
7
+
8
+ export const SingleImage = ({
9
+ onRemove,
10
+ onUpload,
11
+ uploading = false,
12
+ value,
13
+ }: FileFieldI) => {
14
+ const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
15
+ const fileList = event.target.files ?? []
16
+ const files = Array.from(fileList)
17
+ if (files.length > 0) {
18
+ const selectedFiles = handleMapFile({ files })
19
+ onUpload?.(selectedFiles)
20
+ }
21
+ }
22
+
23
+ return (
24
+ <Flex gap="2" align="center" h="input.md">
25
+ <MobileFileUploader
26
+ handleInputChange={handleInputChange}
27
+ aria-label="upload image"
28
+ minW="72px"
29
+ />
30
+ {value && (
31
+ <Box position="relative" minW="30px">
32
+ <IconButton
33
+ variant="ghost"
34
+ aria-label="close"
35
+ maxW="fit-content"
36
+ onClick={() => onRemove?.(value)}
37
+ icon={<Close size={11} stroke={colors.error['2']} />}
38
+ disabled={uploading}
39
+ position="absolute"
40
+ bg="neutral.white"
41
+ p="0.5"
42
+ borderRadius="full"
43
+ top="-2"
44
+ right="-2"
45
+ zIndex="1"
46
+ />
47
+ <AspectRatio maxW="100%" ratio={1}>
48
+ <Image src={value} objectFit="cover" alt="image" />
49
+ </AspectRatio>
50
+ </Box>
51
+ )}
52
+ </Flex>
53
+ )
54
+ }
@@ -1,3 +1,4 @@
1
+ export * from './AIGridField'
1
2
  export * from './CheckboxAgreement'
2
3
  export * from './CheckboxGroupField'
3
4
  export * from './CurrencyField'
@@ -13,6 +14,7 @@ export * from './SwitchField'
13
14
  export * from './RatingField'
14
15
  export * from './SelectField'
15
16
  export * from './SingleFields'
17
+ export * from './SingleImage'
16
18
  export * from './TextAreaField'
17
19
  export * from './TextField'
18
20
  export * from './TileBodyFields'
@@ -6,10 +6,36 @@ export const Camera = ({ stroke = colors.blue[3], size = 24 }) => {
6
6
  xmlns="http://www.w3.org/2000/svg"
7
7
  width={size}
8
8
  height={size}
9
- fill={stroke}
10
- viewBox="0 0 256 256"
9
+ viewBox="0 0 26.734 22.146"
11
10
  >
12
- <path d="M208,56H180.28L166.65,35.56A8,8,0,0,0,160,32H96a8,8,0,0,0-6.65,3.56L75.71,56H48A24,24,0,0,0,24,80V192a24,24,0,0,0,24,24H208a24,24,0,0,0,24-24V80A24,24,0,0,0,208,56Zm-44,76a36,36,0,1,1-36-36A36,36,0,0,1,164,132Z"></path>
11
+ <g
12
+ id="Icon_feather-camera"
13
+ data-name="Icon feather-camera"
14
+ transform="translate(-0.75 -3.75)"
15
+ >
16
+ <path
17
+ id="Caminho_17678"
18
+ data-name="Caminho 17678"
19
+ d="M26.735,22.852a2.294,2.294,0,0,1-2.294,2.294H3.794A2.294,2.294,0,0,1,1.5,22.852V10.235A2.294,2.294,0,0,1,3.794,7.941H8.382L10.676,4.5h6.882l2.294,3.441H24.44a2.294,2.294,0,0,1,2.294,2.294Z"
20
+ transform="translate(0 0)"
21
+ fill="none"
22
+ stroke={stroke}
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ strokeWidth="1.5"
26
+ />
27
+ <path
28
+ id="Caminho_17679"
29
+ data-name="Caminho 17679"
30
+ d="M21.176,18.088A4.588,4.588,0,1,1,16.588,13.5,4.588,4.588,0,0,1,21.176,18.088Z"
31
+ transform="translate(-2.471 -2.118)"
32
+ fill="none"
33
+ stroke={stroke}
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ strokeWidth="1.5"
37
+ />
38
+ </g>
13
39
  </svg>
14
40
  )
15
41
  }
@@ -6,6 +6,7 @@ import { EditFileUploaderI } from '@/interfaces'
6
6
 
7
7
  export const MobileFileUploader = ({
8
8
  handleInputChange,
9
+ multiple = true,
9
10
  ...props
10
11
  }: EditFileUploaderI) => {
11
12
  const hiddenFileInput = useRef<HTMLInputElement | null>(null)
@@ -14,7 +15,8 @@ export const MobileFileUploader = ({
14
15
  <IconButton
15
16
  {...props}
16
17
  variant="iconOutlined"
17
- h="2.3rem"
18
+ minH="40px"
19
+ h="full"
18
20
  icon={<Camera />}
19
21
  onClick={() => hiddenFileInput?.current?.click()}
20
22
  />
@@ -26,6 +28,7 @@ export const MobileFileUploader = ({
26
28
  ref={hiddenFileInput}
27
29
  placeholder={t('addMedia.placeholder')}
28
30
  onChange={handleInputChange}
31
+ multiple={multiple}
29
32
  />
30
33
  </>
31
34
  )
@@ -5,7 +5,7 @@ import { useEffect, useState } from 'react'
5
5
  import { useDropzone } from 'react-dropzone'
6
6
 
7
7
  interface FilesUploaderHookI {
8
- onUpload: (files: FolderFileI[]) => void
8
+ onUpload?: (files: FolderFileI[]) => void
9
9
  uploading: boolean
10
10
  displayImages?: string[]
11
11
  }
@@ -55,7 +55,7 @@ export const useFilesUploader = ({
55
55
  }, [hasError])
56
56
 
57
57
  useEffect(() => {
58
- onUpload(acceptedFiles)
58
+ onUpload?.(acceptedFiles)
59
59
  }, [acceptedFiles])
60
60
 
61
61
  useEffect(() => {
@@ -1,6 +1,14 @@
1
- import { FolderFileI, MenuItemI, PartnerFooterI, TileCtaI } from '@/interfaces'
1
+ import {
2
+ AIGridFieldI,
3
+ FolderFileI,
4
+ MenuItemI,
5
+ PartnerFooterI,
6
+ TileCtaI,
7
+ } from '@/interfaces'
2
8
 
3
9
  export type KindTypes =
10
+ | 'ai-image'
11
+ | 'ai-grid'
4
12
  | 'checkbox'
5
13
  | 'checkbox-agreement'
6
14
  | 'checkbox-group'
@@ -125,6 +133,7 @@ export interface DynamicFormI extends Partial<PartnerFooterI> {
125
133
  displayImages?: string[]
126
134
  form: ReportI[]
127
135
  menuItems?: MenuItemI[]
136
+ onAISend?: AIGridFieldI['onAISend']
128
137
  onRemoveImage?: (file: string) => void
129
138
  onUpload?: (filesByFieldId: Record<string, FolderFileI[]>) => void
130
139
  showTitle?: boolean
@@ -2,7 +2,8 @@ import { FolderFileI, ReportI } from '@/interfaces'
2
2
 
3
3
  export interface FileFieldI extends Pick<ReportI, 'description' | 'icon'> {
4
4
  onRemove?: (file: string) => void
5
- onUpload: (files: FolderFileI[]) => void
5
+ onUpload?: (files: FolderFileI[]) => void
6
6
  uploading?: boolean
7
7
  displayImages?: string[]
8
+ value?: string
8
9
  }
@@ -0,0 +1,16 @@
1
+ import { FolderFileI, ReportI } from '@/interfaces'
2
+
3
+ export interface GridFieldI {
4
+ children: ReportI['children']
5
+ onRemove?: (file: string) => void
6
+ onUpload?: (files: FolderFileI[]) => void
7
+ }
8
+
9
+ export interface AIGridFieldI extends GridFieldI {
10
+ onAISend?: (form?: AIFormI) => void
11
+ }
12
+
13
+ interface AIFormI {
14
+ code?: string
15
+ image?: string
16
+ }
@@ -2,6 +2,7 @@ export * from './DateField.interface'
2
2
  export * from './FieldDescription.interface'
3
3
  export * from './FieldWithDelete.interface'
4
4
  export * from './FileField.interface'
5
+ export * from './GridField.interface'
5
6
  export * from './GroupField.interface'
6
7
  export * from './RatingField.interface'
7
8
  export * from './SelectField.interface'
@@ -3,4 +3,5 @@ import { IconButtonProps } from '@chakra-ui/react'
3
3
 
4
4
  export interface EditFileUploaderI extends IconButtonProps {
5
5
  handleInputChange: (event: ChangeEvent<HTMLInputElement>) => void
6
+ multiple?: boolean
6
7
  }
@@ -12,6 +12,34 @@ export const formFieldsMock: ReportI[] = [
12
12
  type: 'checkbox-agreement',
13
13
  visible: true,
14
14
  },
15
+ {
16
+ id: faker.database.mongodbObjectId(),
17
+ name: 'ai form',
18
+ description: 'Add item details',
19
+ value: false,
20
+ type: 'ai-grid',
21
+ visible: true,
22
+ children: [
23
+ {
24
+ id: faker.database.mongodbObjectId(),
25
+ name: 'Model or Serial',
26
+ description: faker.lorem.sentence(),
27
+ comments: faker.lorem.sentence(),
28
+ value: '',
29
+ type: 'text',
30
+ visible: true,
31
+ },
32
+ {
33
+ id: faker.database.mongodbObjectId(),
34
+ name: 'uploder',
35
+ description: faker.lorem.sentence(),
36
+ comments: faker.lorem.sentence(),
37
+ value: faker.image.urlPicsumPhotos(),
38
+ type: 'ai-image',
39
+ visible: true,
40
+ },
41
+ ],
42
+ },
15
43
  {
16
44
  id: faker.database.mongodbObjectId(),
17
45
  name: '',
@@ -140,7 +168,7 @@ export const formFieldsMock: ReportI[] = [
140
168
  name: 'Purchase Date',
141
169
  description: faker.lorem.sentence(),
142
170
  comments: faker.lorem.sentence(),
143
- value: faker.date.past().toLocaleDateString(),
171
+ value: '',
144
172
  type: 'date',
145
173
  visible: true,
146
174
  },
@@ -243,7 +271,7 @@ export const formFieldsMock: ReportI[] = [
243
271
  name: 'Due Date',
244
272
  description: faker.lorem.sentence(),
245
273
  comments: faker.lorem.sentence(),
246
- value: faker.date.future().toLocaleDateString(),
274
+ value: '',
247
275
  type: 'date',
248
276
  visible: true,
249
277
  icon: 'date',
@@ -21,6 +21,7 @@ export default {
21
21
  faker.image.urlPicsumPhotos(),
22
22
  faker.image.urlPicsumPhotos(),
23
23
  ],
24
+ onAiSend: action('onAiSend'),
24
25
  onUpload: action('onUpload'),
25
26
  menuItems: menuMock,
26
27
  socialLinks: socialLinksMock,
@@ -31,7 +32,7 @@ export default {
31
32
  export const DynamicFormComponent = (args: DynamicFormI) => {
32
33
  return (
33
34
  <Box p="base" bg="neutral.white" w={['full', '500px']}>
34
- <DynamicForm {...args} form={formFieldsMock} />
35
+ <DynamicForm {...args} form={formFieldsMock} showTitle={false} />
35
36
  </Box>
36
37
  )
37
38
  }