@homefile/components-v2 2.9.3 → 2.10.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.js +4 -2
  2. package/dist/components/forms/dynamicForm/fields/GridField.d.ts +2 -2
  3. package/dist/components/forms/dynamicForm/fields/GridField.js +5 -3
  4. package/dist/components/forms/dynamicForm/fields/SingleImage.d.ts +2 -0
  5. package/dist/components/forms/dynamicForm/fields/SingleImage.js +17 -0
  6. package/dist/components/forms/dynamicForm/fields/index.d.ts +1 -0
  7. package/dist/components/forms/dynamicForm/fields/index.js +1 -0
  8. package/dist/components/icons/Camera.js +2 -2
  9. package/dist/components/inputs/MobileFileUploader.d.ts +1 -1
  10. package/dist/components/inputs/MobileFileUploader.js +2 -2
  11. package/dist/hooks/folderPanel/useFilesUploader.d.ts +1 -1
  12. package/dist/hooks/folderPanel/useFilesUploader.js +1 -1
  13. package/dist/interfaces/forms/dynamicForm/DynamicForm.interface.d.ts +1 -1
  14. package/dist/interfaces/forms/dynamicForm/fields/FileField.interface.d.ts +2 -1
  15. package/dist/interfaces/forms/dynamicForm/fields/GridField.interface.d.ts +6 -0
  16. package/dist/interfaces/forms/dynamicForm/fields/GridField.interface.js +1 -0
  17. package/dist/interfaces/forms/dynamicForm/fields/index.d.ts +1 -0
  18. package/dist/interfaces/forms/dynamicForm/fields/index.js +1 -0
  19. package/dist/interfaces/inputs/EditFileUploader.interface.d.ts +1 -0
  20. package/dist/interfaces/myProfile/details/TwoFactorDialog.interface.d.ts +1 -1
  21. package/dist/interfaces/pages/TwoFactor.interface.d.ts +1 -1
  22. package/dist/mocks/forms/dynamicForm.mock.js +30 -2
  23. package/dist/stories/forms/dynamicForm/DynamicForm.stories.js +1 -1
  24. package/package.json +1 -1
  25. package/src/components/forms/dynamicForm/DynamicForm.tsx +14 -1
  26. package/src/components/forms/dynamicForm/fields/GridField.tsx +14 -3
  27. package/src/components/forms/dynamicForm/fields/SingleImage.tsx +53 -0
  28. package/src/components/forms/dynamicForm/fields/index.ts +1 -0
  29. package/src/components/icons/Camera.tsx +29 -3
  30. package/src/components/inputs/MobileFileUploader.tsx +4 -1
  31. package/src/hooks/folderPanel/useFilesUploader.ts +2 -2
  32. package/src/interfaces/forms/dynamicForm/DynamicForm.interface.ts +2 -0
  33. package/src/interfaces/forms/dynamicForm/fields/FileField.interface.ts +2 -1
  34. package/src/interfaces/forms/dynamicForm/fields/GridField.interface.ts +7 -0
  35. package/src/interfaces/forms/dynamicForm/fields/index.ts +1 -0
  36. package/src/interfaces/inputs/EditFileUploader.interface.ts +1 -0
  37. package/src/interfaces/myProfile/details/TwoFactorDialog.interface.ts +1 -1
  38. package/src/interfaces/pages/TwoFactor.interface.ts +1 -1
  39. package/src/mocks/forms/dynamicForm.mock.ts +30 -2
  40. package/src/stories/forms/dynamicForm/DynamicForm.stories.tsx +1 -1
@@ -10,7 +10,7 @@ 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';
@@ -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: [_jsx(Text, { fontFamily: "secondary", children: description }), _jsx(GridField, Object.assign({}, baseProps, { onRemove: onRemoveImage, onUpload: (files) => handleFilesUpload({ id, files }), 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 }))));
@@ -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: "1", align: "center", flex: "auto1", children: [_jsx(MobileFileUploader, { handleInputChange: handleInputChange, "aria-label": "upload image" }), value && (_jsxs(Box, { position: "relative", w: "66px", 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
+ };
@@ -13,6 +13,7 @@ export * from './SwitchField';
13
13
  export * from './RatingField';
14
14
  export * from './SelectField';
15
15
  export * from './SingleFields';
16
+ export * from './SingleImage';
16
17
  export * from './TextAreaField';
17
18
  export * from './TextField';
18
19
  export * from './TileBodyFields';
@@ -13,6 +13,7 @@ export * from './SwitchField';
13
13
  export * from './RatingField';
14
14
  export * from './SelectField';
15
15
  export * from './SingleFields';
16
+ export * from './SingleImage';
16
17
  export * from './TextAreaField';
17
18
  export * from './TextField';
18
19
  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
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;
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;
@@ -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,6 @@
1
+ import { FolderFileI, ReportI } from '../../..';
2
+ export interface GridFieldI {
3
+ children: ReportI['children'];
4
+ onRemove?: (file: string) => void;
5
+ onUpload?: (files: FolderFileI[]) => void;
6
+ }
@@ -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
  }
@@ -4,5 +4,5 @@ export interface TwoFactorDialogI {
4
4
  onClose: () => void;
5
5
  onResend?: () => void;
6
6
  onReset: (code: string) => void;
7
- twoFactorMethod: 'sms' | 'email';
7
+ twoFactorMethod: 'sms' | 'email' | "";
8
8
  }
@@ -3,5 +3,5 @@ export interface TwoFactorI {
3
3
  onReset: (code: string) => void;
4
4
  isDialog?: boolean;
5
5
  isLoading: boolean;
6
- twoFactorMethod: 'sms' | 'email';
6
+ twoFactorMethod: 'sms' | 'email' | "";
7
7
  }
@@ -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',
@@ -26,7 +26,7 @@ export default {
26
26
  },
27
27
  };
28
28
  export const DynamicFormComponent = (args) => {
29
- return (_jsx(Box, { p: "base", bg: "neutral.white", w: ['full', '500px'], children: _jsx(DynamicForm, Object.assign({}, args, { form: formFieldsMock })) }));
29
+ return (_jsx(Box, { p: "base", bg: "neutral.white", w: ['full', '500px'], children: _jsx(DynamicForm, Object.assign({}, args, { form: formFieldsMock, showTitle: false })) }));
30
30
  };
31
31
  export const DynamicUIFormComponent = (args) => {
32
32
  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.3",
3
+ "version": "2.10.0",
4
4
  "author": "Homefile",
5
5
  "license": "UNLICENSED",
6
6
  "typings": "dist/index.d.ts",
@@ -77,7 +77,7 @@ export const DynamicForm = ({
77
77
  id,
78
78
  value,
79
79
  icon: icon ? (fieldIcons[icon] as IconTypes) : undefined,
80
- placeholder: (name && name !== "") ? name : description,
80
+ placeholder: Boolean(name) ? name : description,
81
81
  }
82
82
  const fieldWithDeleteBaseProps = {
83
83
  id,
@@ -140,6 +140,19 @@ export const DynamicForm = ({
140
140
  <RatingField {...baseProps} />
141
141
  </FieldWithDelete>
142
142
  )
143
+ case 'ai-grid':
144
+ return (
145
+ <Stack key={id} p="base" spacing="base" bg="lightBlue.2">
146
+ <Text fontFamily="secondary">{description}</Text>
147
+ <GridField
148
+ {...baseProps}
149
+ onRemove={onRemoveImage}
150
+ onUpload={(files) => handleFilesUpload({ id, files })}
151
+ >
152
+ {children}
153
+ </GridField>
154
+ </Stack>
155
+ )
143
156
  case 'grid':
144
157
  return (
145
158
  <FieldWithDelete {...fieldWithDeleteBaseProps} key={id}>
@@ -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,53 @@
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="1" align="center" flex="auto1">
25
+ <MobileFileUploader
26
+ handleInputChange={handleInputChange}
27
+ aria-label="upload image"
28
+ />
29
+ {value && (
30
+ <Box position="relative" w="66px">
31
+ <IconButton
32
+ variant="ghost"
33
+ aria-label="close"
34
+ maxW="fit-content"
35
+ onClick={() => onRemove?.(value)}
36
+ icon={<Close size={11} stroke={colors.error['2']} />}
37
+ disabled={uploading}
38
+ position="absolute"
39
+ bg="neutral.white"
40
+ p="0.5"
41
+ borderRadius="full"
42
+ top="-2"
43
+ right="-2"
44
+ zIndex="1"
45
+ />
46
+ <AspectRatio maxW="100%" ratio={1}>
47
+ <Image src={value} objectFit="cover" alt="image" />
48
+ </AspectRatio>
49
+ </Box>
50
+ )}
51
+ </Flex>
52
+ )
53
+ }
@@ -13,6 +13,7 @@ export * from './SwitchField'
13
13
  export * from './RatingField'
14
14
  export * from './SelectField'
15
15
  export * from './SingleFields'
16
+ export * from './SingleImage'
16
17
  export * from './TextAreaField'
17
18
  export * from './TextField'
18
19
  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,8 @@
1
1
  import { FolderFileI, MenuItemI, PartnerFooterI, TileCtaI } from '@/interfaces'
2
2
 
3
3
  export type KindTypes =
4
+ | 'ai-image'
5
+ | 'ai-grid'
4
6
  | 'checkbox'
5
7
  | 'checkbox-agreement'
6
8
  | 'checkbox-group'
@@ -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,7 @@
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
+ }
@@ -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
  }
@@ -4,5 +4,5 @@ export interface TwoFactorDialogI {
4
4
  onClose: () => void
5
5
  onResend?: () => void
6
6
  onReset: (code: string) => void
7
- twoFactorMethod: 'sms' | 'email'
7
+ twoFactorMethod: 'sms' | 'email' | ""
8
8
  }
@@ -3,5 +3,5 @@ export interface TwoFactorI {
3
3
  onReset: (code: string) => void
4
4
  isDialog?: boolean
5
5
  isLoading: boolean
6
- twoFactorMethod: 'sms' | 'email'
6
+ twoFactorMethod: 'sms' | 'email' | ""
7
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',
@@ -31,7 +31,7 @@ export default {
31
31
  export const DynamicFormComponent = (args: DynamicFormI) => {
32
32
  return (
33
33
  <Box p="base" bg="neutral.white" w={['full', '500px']}>
34
- <DynamicForm {...args} form={formFieldsMock} />
34
+ <DynamicForm {...args} form={formFieldsMock} showTitle={false} />
35
35
  </Box>
36
36
  )
37
37
  }