jamespot-react-core 1.1.12 → 1.1.13

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 (47) hide show
  1. package/.github/workflows/deploy-dev-branches.yml +1 -1
  2. package/.github/workflows/increment-npm-version.yml +26 -0
  3. package/.github/workflows/npm-package.yml +1 -1
  4. package/.husky/pre-commit +0 -2
  5. package/build/294.bundle.js +1 -1
  6. package/build/294.bundle.js.map +1 -1
  7. package/build/715.bundle.js +1 -1
  8. package/build/715.bundle.js.map +1 -1
  9. package/build/76.bundle.js +2 -0
  10. package/build/76.bundle.js.map +1 -0
  11. package/build/862.bundle.js +2 -0
  12. package/build/862.bundle.js.map +1 -0
  13. package/build/955.bundle.js +2 -0
  14. package/build/955.bundle.js.map +1 -0
  15. package/build/App.d.ts +4 -3
  16. package/build/app.bundle.js +41 -41
  17. package/build/app.bundle.js.map +1 -1
  18. package/build/components/AppStateLoader.component.d.ts +7 -0
  19. package/build/components/types.d.ts +1 -0
  20. package/build/displayer/DisplayForm.component.d.ts +19 -0
  21. package/build/displayer/Empty.d.ts +1 -0
  22. package/build/displayer/components/DisplaySingleValue.component.d.ts +1 -0
  23. package/build/displayer/list/formatter.d.ts +112 -10
  24. package/build/displayer/types.d.ts +54 -2
  25. package/build/redux/slice/AppState.slice.d.ts +29 -0
  26. package/build/redux/slice/Application.slice.d.ts +6 -0
  27. package/build/redux/slice/Article.slice.d.ts +6 -0
  28. package/build/redux/slice/Model.slice.d.ts +6 -0
  29. package/build/redux/slice/Toast.slice.d.ts +6 -0
  30. package/build/redux/slice/User.slice.d.ts +6 -0
  31. package/build/redux/store.d.ts +8 -1
  32. package/build/utils/types.d.ts +14 -5
  33. package/package.json +6 -5
  34. package/src/App.tsx +41 -8
  35. package/src/components/AppStateLoader.component.tsx +54 -0
  36. package/src/components/index.tsx +10 -0
  37. package/src/components/types.ts +1 -0
  38. package/src/displayer/DisplayAttribute.component.tsx +1 -2
  39. package/src/displayer/DisplayForm.component.tsx +78 -0
  40. package/src/displayer/Empty.tsx +4 -0
  41. package/src/displayer/components/DisplaySingleValue.component.tsx +6 -0
  42. package/src/displayer/list/formatter.tsx +182 -14
  43. package/src/displayer/types.ts +60 -2
  44. package/src/displayer/useDisplay.ts +32 -15
  45. package/src/redux/slice/AppState.slice.ts +25 -0
  46. package/src/redux/store.tsx +27 -21
  47. package/src/utils/types.ts +15 -5
@@ -0,0 +1,54 @@
1
+ import { JRCCardProps, JRCLoaderProps } from 'jamespot-react-components';
2
+ import React from 'react';
3
+ import { FormattedMessage, useIntl } from 'react-intl';
4
+ import { useSelector } from 'react-redux';
5
+ import { appStateActions } from 'redux/slice/AppState.slice';
6
+
7
+ const appStateAction = J.react.store.actions.appState;
8
+ const Loader = J.react.registry.getLazyComponent<JRCLoaderProps>('Loader');
9
+ const Card = J.react.registry.getLazyComponent<JRCCardProps>('Card');
10
+
11
+ export type AppStateLoaderProps = {
12
+ appId: string;
13
+ children: JSX.Element;
14
+ loader?: JSX.Element;
15
+ initPromises: Promise<any>[];
16
+ };
17
+
18
+ export const AppStateLoader = (props: AppStateLoaderProps) => {
19
+ const dispatch = J.react.store.useAppDispatch();
20
+ const intl = useIntl();
21
+
22
+ const { appState } = useSelector((state: any) => {
23
+ return {
24
+ appState: J.react.store.selectors.appState(state, props.appId),
25
+ };
26
+ });
27
+
28
+ // Init Function
29
+ React.useEffect(() => {
30
+ if (props.initPromises) {
31
+ dispatch(appStateActions.setLoading(props.appId));
32
+ Promise.all(props.initPromises)
33
+ .then(() => dispatch(appStateAction.setLoaded(props.appId)))
34
+ .catch(() => {
35
+ dispatch(
36
+ appStateAction.setError({
37
+ app: props.appId,
38
+ error: { message: 'Error' },
39
+ }),
40
+ );
41
+ dispatch(J.react.toasts.error({ label: intl.formatMessage({ id: 'GLOBAL_Error_occurred' }) }));
42
+ });
43
+ }
44
+ }, []);
45
+ if (appState && appState.error) {
46
+ return (
47
+ <Card variant="div" type="error">
48
+ <FormattedMessage id="GLOBAL_Error_occurred" />
49
+ </Card>
50
+ );
51
+ }
52
+
53
+ return <>{appState && appState.isLoading ? <Loader variant="skeleton" /> : <>{props.children}</>}</>;
54
+ };
@@ -19,3 +19,13 @@ Registry.registerLib(
19
19
  ],
20
20
  import('./IfAppIsActivated.component'),
21
21
  );
22
+
23
+ Registry.registerLib(
24
+ [
25
+ {
26
+ name: 'AppStateLoader',
27
+ module: 'AppStateLoader',
28
+ },
29
+ ],
30
+ import('./AppStateLoader.component'),
31
+ );
@@ -1,2 +1,3 @@
1
1
  export { ExtensionProviderProps } from './ExtensionProvider.component';
2
2
  export { IfAppIsActivatedProps } from './IfAppIsActivated.component';
3
+ export { AppStateLoaderProps } from './AppStateLoader.component';
@@ -1,5 +1,4 @@
1
1
  import * as React from 'react';
2
- import { FunctionComponent } from 'react';
3
2
  import { DisplayAttributesProps, RenderAttributeProps } from './types';
4
3
 
5
4
  /***
@@ -7,7 +6,7 @@ import { DisplayAttributesProps, RenderAttributeProps } from './types';
7
6
  */
8
7
  const RenderAttribute = (props: RenderAttributeProps) => {
9
8
  const { attribute } = props;
10
- const RenderComponent: FunctionComponent<RenderAttributeProps> = attribute.components.render;
9
+ const RenderComponent: React.FunctionComponent<RenderAttributeProps> = attribute.components.render;
11
10
  const WrapperComponent: React.FunctionComponent<any> = attribute.components.wrapper;
12
11
 
13
12
  return (
@@ -0,0 +1,78 @@
1
+ import * as React from 'react';
2
+ import { DisplayFormProps, DisplayFormRef, RenderInputProps } from './types';
3
+ import { useForm } from 'react-hook-form';
4
+ import type { JRCButtonProps } from 'jamespot-react-components';
5
+ import { useIntl } from 'react-intl';
6
+
7
+ /***
8
+ * Choose the component to render according to the type of the object
9
+ */
10
+ const RenderInput = (props: RenderInputProps) => <props.components.input {...props} />;
11
+
12
+ /****
13
+ * The jamespot model depend on the platform
14
+ * For example a user can have the size attribute only for a given platform
15
+ * The model specifics of a platform are stored in J.model
16
+ *
17
+ * This component display an array of attributes only if this attribute is activated on the platform
18
+ *
19
+ * @param props.object the object to display (ie the user)
20
+ * @param props.attributesName an array of string : the list of attributes we want to display
21
+ * @param props.componentsOverride we can override the default render of the attribute
22
+ * @param props.componentsOverride.render the whole component logic (we override @DisplayList or @DisplaySingleValue for exemple)
23
+ * @param props.componentsOverride.wrapper the attribute is wrapped inside an element. For a list it can be an UL
24
+ * @param props.componentsOverride.element the component just above the element value
25
+ * @param props.componentsOverride.input the component input
26
+ * Note :If we override the "render" you can't override "wrapper" and "element" because the render rewrite everything
27
+ */
28
+ export const DisplayForm = React.forwardRef<DisplayFormRef, DisplayFormProps>((props, ref) => {
29
+ const intl = useIntl();
30
+
31
+ const formRef = React.useRef<HTMLFormElement>(null);
32
+
33
+ const { attributesName, object, componentsOverride } = props;
34
+ const formConfig = J.react.useDisplay(attributesName || [], object.type);
35
+ const ValidationButton = J.react.registry.getLazyComponent<JRCButtonProps>('ValidationButton');
36
+
37
+ const { handleSubmit, control, reset } = useForm({
38
+ defaultValues: {
39
+ // initialize with empty array of checkbox, empty string otherwise
40
+ ...(Object as any).fromEntries(
41
+ formConfig.map((inputConfig) => [
42
+ inputConfig.accessor,
43
+ inputConfig.widget.type === 'checkbox' ? [] : '',
44
+ ]),
45
+ ),
46
+ },
47
+ criteriaMode: 'all',
48
+ });
49
+
50
+ React.useImperativeHandle(
51
+ ref,
52
+ () => ({
53
+ reset: () => {
54
+ // in case the form is scrollable
55
+ formRef?.current?.scrollTo(0, 0);
56
+ // reset react-hook-form form
57
+ reset();
58
+ },
59
+ }),
60
+ [],
61
+ );
62
+
63
+ return (
64
+ <form onSubmit={handleSubmit(props.onSubmit)} ref={formRef}>
65
+ {formConfig.map((inputConfig, index) => {
66
+ inputConfig.components = {
67
+ ...inputConfig.components,
68
+ ...componentsOverride?.[inputConfig.name],
69
+ };
70
+ return <RenderInput key={index} control={control} {...inputConfig} />;
71
+ })}
72
+
73
+ <ValidationButton type="submit">
74
+ {intl.formatMessage({ id: 'APP_ASEI_Modal_Creation_Title' })}
75
+ </ValidationButton>
76
+ </form>
77
+ );
78
+ });
@@ -0,0 +1,4 @@
1
+ import * as React from 'react';
2
+
3
+ // FIXME move to j-react-components + display error if dev mode
4
+ export const Empty = () => <></>;
@@ -1,5 +1,6 @@
1
1
  import { RenderAttributeProps } from 'displayer/types';
2
2
  import * as React from 'react';
3
+ import { JRCDateProps } from 'jamespot-react-components';
3
4
 
4
5
  export const DisplaySingleValue = (props: RenderAttributeProps) => {
5
6
  const { object, attribute } = props;
@@ -8,3 +9,8 @@ export const DisplaySingleValue = (props: RenderAttributeProps) => {
8
9
 
9
10
  return <ElementComponent>{object[attribute.accessor]}</ElementComponent>;
10
11
  };
12
+
13
+ export const DisplayDate = ({ object, attribute }: RenderAttributeProps) => {
14
+ const Date = J.react.registry.getLazyComponent<JRCDateProps>('Date');
15
+ return <Date date={object[attribute.name]} />;
16
+ };
@@ -1,63 +1,231 @@
1
1
  import { DisplayList } from 'displayer/components/DisplayList.component';
2
- import { DisplaySingleValue } from 'displayer/components/DisplaySingleValue.component';
2
+ import { DisplayDate, DisplaySingleValue } from 'displayer/components/DisplaySingleValue.component';
3
3
  import * as React from 'react';
4
- import { FieldConfiguration } from '../types';
4
+ import {
5
+ DisplayInputComponentProps,
6
+ FieldConfiguration,
7
+ WidgetCheckbox,
8
+ WidgetOrientedlinks,
9
+ WidgetUri,
10
+ } from '../types';
11
+ import jamespot from 'jamespot-user-api';
12
+ import { Model } from '../../utils/types';
13
+ import { useSelector } from 'react-redux';
14
+ import { JRCAutocompleteProps, JRCInputCheckboxProps, JRCInputFieldProps } from 'jamespot-react-components';
5
15
 
6
16
  const DefaultWrapper = ({ children }: { children: React.FunctionComponent<any> }) => <div>{children}</div>;
7
17
  const DefaultElement = ({ children }: { children: React.FunctionComponent<any> }) => <>{children}</>;
8
18
 
19
+ const RadioInput =
20
+ (checkboxMode: 'radio' | 'checkbox', options: Array<{ value: string; label: string }>) =>
21
+ ({ widget, ...props }: DisplayInputComponentProps) => {
22
+ const InputCheckbox = J.react.registry.getLazyComponent<JRCInputCheckboxProps<any>>('InputCheckbox');
23
+
24
+ const direction = options.length > 3 ? 'column' : 'row';
25
+
26
+ // {/* TODO multiple */}
27
+ return (
28
+ <>
29
+ {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
30
+ {/* @ts-ignore */}
31
+ <InputCheckbox
32
+ {...props}
33
+ checkboxMode={checkboxMode}
34
+ options={options}
35
+ direction={direction}
36
+ rules={{ required: props.mandatory }}
37
+ />
38
+ </>
39
+ );
40
+ };
41
+
42
+ const LinkInput =
43
+ (type = 'article') =>
44
+ ({ widget, ...props }: DisplayInputComponentProps) => {
45
+ const InputAutocomplete = J.react.registry.getLazyComponent<JRCAutocompleteProps<any>>('InputAutocomplete');
46
+
47
+ // {/* TODO multiple */}
48
+ // const multiple = (widget as WidgetOrientedlinks).params["jcomplete-multiple"] === "1"
49
+ const required = props.mandatory;
50
+ const model: Model<string> = useSelector((state: any) => J.react.store.selectors.model.selectById(state, type));
51
+ const mainType = model?.mainType;
52
+
53
+ if (!mainType) return <></>;
54
+
55
+ const orientedLinksApis = {
56
+ article: (inputValue: string) =>
57
+ jamespot.article
58
+ .list({
59
+ type: (widget as WidgetOrientedlinks).params.supportedTypes[0],
60
+ format: 'raw-view',
61
+ query: inputValue,
62
+ })
63
+ .then((res: any) => {
64
+ return res?.result?.data;
65
+ }),
66
+ user: (inputValue: string) =>
67
+ jamespot.user
68
+ .autocomplete(inputValue)
69
+ .then((res: any) => res.map((jUser: any) => ({ ...jUser.record, id: jUser.id }))),
70
+ };
71
+
72
+ const autocomplete = (inputValue: string) =>
73
+ Object.keys(orientedLinksApis).includes(mainType)
74
+ ? orientedLinksApis[mainType as 'user' | 'article'](inputValue)
75
+ : Promise.resolve([]);
76
+
77
+ return (
78
+ <InputAutocomplete
79
+ label={props.label}
80
+ description={props.description}
81
+ name={props.name}
82
+ control={props.control}
83
+ rules={{ required }}
84
+ searchable
85
+ // multiple={multiple}
86
+ asyncPromise={autocomplete}
87
+ />
88
+ );
89
+ };
90
+
91
+ const DefaultInput = (props: DisplayInputComponentProps) => {
92
+ const InputText = J.react.registry.getLazyComponent<JRCInputFieldProps<any>>('InputText');
93
+ return (
94
+ <InputText
95
+ label={props.label}
96
+ description={props.description}
97
+ name={props.name}
98
+ control={props.control}
99
+ rules={{ required: props.mandatory }}
100
+ />
101
+ );
102
+ };
103
+
104
+ export function formatDate<T extends string>(configuration: FieldConfiguration<T>) {
105
+ return {
106
+ ...configuration,
107
+ accessor: configuration.name,
108
+ components: {
109
+ render: DisplayDate,
110
+ wrapper: DefaultWrapper,
111
+ element: DefaultElement,
112
+ input: DefaultInput,
113
+ },
114
+ };
115
+ }
116
+
9
117
  export function formatString<T extends string>(configuration: FieldConfiguration<T>) {
10
118
  return {
11
- name: configuration.name,
12
- label: configuration.label,
119
+ ...configuration,
13
120
  accessor: configuration.name,
14
- widget: configuration.widget,
15
121
  components: {
16
122
  render: DisplaySingleValue,
17
123
  wrapper: DefaultWrapper,
18
124
  element: DefaultElement,
125
+ input: DefaultInput,
19
126
  },
20
127
  };
21
128
  }
22
129
 
23
130
  export function formatNumber<T extends string>(configuration: FieldConfiguration<T>) {
24
131
  return {
25
- name: configuration.name,
26
- label: configuration.label,
132
+ ...configuration,
27
133
  accessor: configuration.name,
28
- widget: configuration.widget,
29
134
  components: {
30
135
  render: DisplaySingleValue,
31
136
  wrapper: DefaultWrapper,
32
137
  element: DefaultElement,
138
+ input: DefaultInput,
33
139
  },
34
140
  };
35
141
  }
36
142
 
37
143
  export function formatTaxonomy<T extends string>(configuration: FieldConfiguration<T>) {
38
144
  return {
39
- name: configuration.name,
40
- label: configuration.label,
145
+ ...configuration,
146
+ accessor: configuration.name,
147
+ components: {
148
+ render: DisplayList,
149
+ wrapper: DefaultWrapper,
150
+ element: DefaultElement,
151
+ input: DefaultInput,
152
+ },
153
+ };
154
+ }
155
+
156
+ export function formatOrientedlinks<T extends string>(configuration: FieldConfiguration<T>) {
157
+ return {
158
+ ...configuration,
159
+ accessor: configuration.name,
160
+ components: {
161
+ render: DisplayList,
162
+ wrapper: DefaultWrapper,
163
+ element: DefaultElement,
164
+ input: LinkInput((configuration.widget as WidgetOrientedlinks).params.supportedTypes?.[0]),
165
+ },
166
+ };
167
+ }
168
+
169
+ export function formatUri<T extends string>(configuration: FieldConfiguration<T>) {
170
+ return {
171
+ ...configuration,
172
+ accessor: configuration.name,
173
+ components: {
174
+ render: DisplayList,
175
+ wrapper: DefaultWrapper,
176
+ element: DefaultElement,
177
+ input: LinkInput((configuration.widget as WidgetUri).params.type),
178
+ },
179
+ };
180
+ }
181
+
182
+ export function formatRadio<T extends string>(configuration: FieldConfiguration<T>) {
183
+ return {
184
+ ...configuration,
185
+ accessor: configuration.name,
186
+ components: {
187
+ render: DisplayList,
188
+ wrapper: DefaultWrapper,
189
+ element: DefaultElement,
190
+ input: RadioInput(
191
+ 'radio',
192
+ Object.entries((configuration.widget as WidgetCheckbox).options).map(([k, v]) => ({
193
+ label: v,
194
+ value: k,
195
+ })),
196
+ ),
197
+ },
198
+ };
199
+ }
200
+
201
+ export function formatCheckbox<T extends string>(configuration: FieldConfiguration<T>) {
202
+ return {
203
+ ...configuration,
41
204
  accessor: configuration.name,
42
- widget: configuration.widget,
43
205
  components: {
44
206
  render: DisplayList,
45
207
  wrapper: DefaultWrapper,
46
208
  element: DefaultElement,
209
+ input: RadioInput(
210
+ 'checkbox',
211
+ Object.entries((configuration.widget as WidgetCheckbox).options).map(([k, v]) => ({
212
+ label: v,
213
+ value: k,
214
+ })),
215
+ ),
47
216
  },
48
217
  };
49
218
  }
50
219
 
51
220
  export function formatDefault<T extends string>(configuration: FieldConfiguration<T>) {
52
221
  return {
53
- name: configuration.name,
54
- label: configuration.label,
222
+ ...configuration,
55
223
  accessor: configuration.name,
56
- widget: configuration.widget,
57
224
  components: {
58
225
  render: DisplaySingleValue,
59
226
  wrapper: DefaultWrapper,
60
227
  element: DefaultElement,
228
+ input: DefaultInput,
61
229
  },
62
230
  };
63
231
  }
@@ -1,3 +1,6 @@
1
+ import * as React from 'react';
2
+ import { UseFormReset } from 'react-hook-form';
3
+
1
4
  export type JObject<T extends string> = Record<T, any> & {
2
5
  title: string;
3
6
  id: string;
@@ -11,6 +14,7 @@ export type Fields<T extends string> = ReadonlyArray<T>;
11
14
  export type WidgetText = { type: 'text' };
12
15
  export type WidgetNumber = { type: 'number' };
13
16
  export type WidgetDate = { type: 'date'; format: string };
17
+ export type WidgetDatetime = { type: 'datetime' };
14
18
  export type WidgetSelect<U extends string = string> = {
15
19
  type: 'select';
16
20
  options: Record<U, string>;
@@ -19,38 +23,73 @@ export type WidgetSelect<U extends string = string> = {
19
23
  export type WidgetEmail = { type: 'email' };
20
24
  export type WidgetUrl = { type: 'url'; params: { target?: '_blank' } };
21
25
  export type WidgetRefUser = { type: 'refUser' };
26
+ export type WidgetRadio = { type: 'radio'; options: Record<string, string> };
22
27
  export type WidgetCheckbox = { type: 'checkbox'; options: Record<string, string> };
23
28
  export type WidgetTaxonomy = { type: 'taxonomy'; params: { idTaxonomy: string } };
29
+ export type WidgetOrientedlinks = {
30
+ type: 'orientedlinks';
31
+ params: {
32
+ 'jcomplete-multiple': '0' | '1';
33
+ showAdd: '0' | '1';
34
+ supportedTypes: Array<string>;
35
+ };
36
+ };
37
+ export type WidgetUri = {
38
+ type: 'uri';
39
+ params: {
40
+ class: string;
41
+ 'jcomplete-url': string;
42
+ type: string;
43
+ };
44
+ };
24
45
 
25
46
  export type Widget =
26
47
  | WidgetText
27
48
  | WidgetNumber
28
49
  | WidgetDate
50
+ | WidgetDatetime
29
51
  | WidgetSelect
30
52
  | WidgetEmail
31
53
  | WidgetUrl
32
54
  | WidgetRefUser
55
+ | WidgetRadio
33
56
  | WidgetCheckbox
34
- | WidgetTaxonomy;
57
+ | WidgetOrientedlinks
58
+ | WidgetTaxonomy
59
+ | WidgetUri;
35
60
 
36
61
  export type FieldConfiguration<T extends string> = {
37
62
  label: string;
63
+ description?: string;
38
64
  widget: Widget;
39
65
  name: T;
66
+ mandatory: boolean;
40
67
  };
41
68
 
42
69
  export type Configuration<T extends string> = FieldConfiguration<T>;
43
70
 
71
+ export type DisplayInputComponentProps = {
72
+ control: any;
73
+ label: string;
74
+ description?: string;
75
+ name: string;
76
+ widget: Widget;
77
+ mandatory: boolean;
78
+ };
79
+
44
80
  export type DisplayElementComponent = {
45
81
  render: React.FunctionComponent<RenderAttributeProps>;
46
82
  wrapper: React.FunctionComponent<any>;
47
83
  element: React.FunctionComponent<any>;
84
+ input: React.FunctionComponent<DisplayInputComponentProps>;
48
85
  };
49
86
 
50
87
  export type DisplayerElement = {
51
88
  name: string;
52
89
  label: string;
90
+ description?: string;
53
91
  accessor: string;
92
+ mandatory: boolean;
54
93
  widget: Widget;
55
94
  components: DisplayElementComponent;
56
95
  };
@@ -70,6 +109,10 @@ type DisplayAttributeComponentOverrideProps =
70
109
  | {
71
110
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
111
  element: React.FunctionComponent<any>;
112
+ }
113
+ | {
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ input: React.FunctionComponent<any>;
73
116
  };
74
117
 
75
118
  export type DisplayAttributesProps = {
@@ -83,4 +126,19 @@ export type RenderAttributeProps = {
83
126
  attribute: DisplayerElement;
84
127
  };
85
128
 
86
- export type Displayer = Array<DisplayerElement | undefined>;
129
+ export type DisplayFormProps = {
130
+ object: any;
131
+ componentsOverride?: Record<string, DisplayAttributeComponentOverrideProps>;
132
+ attributesName: string[];
133
+ onSubmit: any;
134
+ };
135
+
136
+ export type DisplayFormRef = {
137
+ reset: UseFormReset<any>;
138
+ };
139
+
140
+ export type RenderInputProps = DisplayerElement & {
141
+ control: any;
142
+ };
143
+
144
+ export type Displayer = Array<DisplayerElement>;
@@ -1,5 +1,5 @@
1
1
  import * as ListFormatter from './list/formatter';
2
- import { Fields, Displayer } from './types';
2
+ import { Fields, Displayer, Configuration } from './types';
3
3
  import { useSelector } from 'react-redux';
4
4
  import { Model } from '../utils/types';
5
5
 
@@ -14,19 +14,36 @@ export function useDisplay<T extends string>(fields: Fields<T>, type: string): D
14
14
  if (!model || !model.fields) return [];
15
15
 
16
16
  const configuration = Object.values(model.fields);
17
- return fields.map((field) => {
18
- const fieldConfiguration = configuration.find((conf) => conf.name === field);
19
- if (!fieldConfiguration) return;
20
17
 
21
- switch (fieldConfiguration.widget.type) {
22
- case 'text':
23
- return ListFormatter.formatString(fieldConfiguration);
24
- case 'number':
25
- return ListFormatter.formatNumber(fieldConfiguration);
26
- case 'taxonomy':
27
- return ListFormatter.formatTaxonomy(fieldConfiguration);
28
- default:
29
- return ListFormatter.formatDefault(fieldConfiguration);
30
- }
31
- });
18
+ return (
19
+ fields
20
+ .map((field) => {
21
+ return configuration.find((conf) => conf.name === field);
22
+ })
23
+ .filter((fieldConfiguration) => !!fieldConfiguration) as Array<Configuration<T>>
24
+ )
25
+ .map((fieldConfiguration) => {
26
+ switch (fieldConfiguration.widget.type) {
27
+ case 'date':
28
+ case 'datetime':
29
+ return ListFormatter.formatDate(fieldConfiguration);
30
+ case 'text':
31
+ return ListFormatter.formatString(fieldConfiguration);
32
+ case 'number':
33
+ return ListFormatter.formatNumber(fieldConfiguration);
34
+ case 'taxonomy':
35
+ return ListFormatter.formatTaxonomy(fieldConfiguration);
36
+ case 'orientedlinks':
37
+ return ListFormatter.formatOrientedlinks(fieldConfiguration);
38
+ case 'uri':
39
+ return ListFormatter.formatUri(fieldConfiguration);
40
+ case 'radio':
41
+ return ListFormatter.formatRadio(fieldConfiguration);
42
+ case 'checkbox':
43
+ return ListFormatter.formatCheckbox(fieldConfiguration);
44
+ default:
45
+ return ListFormatter.formatDefault(fieldConfiguration);
46
+ }
47
+ })
48
+ .filter((v) => !!v);
32
49
  }
@@ -0,0 +1,25 @@
1
+ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2
+ import { RootState } from '../store';
3
+
4
+ type SliceState = { [key: string]: { isLoading: boolean; error?: object } };
5
+
6
+ const initialState: SliceState = {};
7
+
8
+ export const appStateSlice = createSlice({
9
+ name: 'appState',
10
+ initialState,
11
+ reducers: {
12
+ setLoading: (state: SliceState, action: PayloadAction<string>) => {
13
+ state[action.payload] = { isLoading: true };
14
+ },
15
+ setLoaded: (state: SliceState, action: PayloadAction<string>) => {
16
+ state[action.payload] = { isLoading: false };
17
+ },
18
+ setError: (state: SliceState, action: PayloadAction<{ app: string; error: object }>) => {
19
+ state[action.payload.app].error = action.payload.error;
20
+ },
21
+ },
22
+ });
23
+
24
+ export const appStateSelector = (state: RootState, appName: string) => state.appState[appName];
25
+ export const appStateActions = appStateSlice.actions;