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.
- package/.github/workflows/deploy-dev-branches.yml +1 -1
- package/.github/workflows/increment-npm-version.yml +26 -0
- package/.github/workflows/npm-package.yml +1 -1
- package/.husky/pre-commit +0 -2
- package/build/294.bundle.js +1 -1
- package/build/294.bundle.js.map +1 -1
- package/build/715.bundle.js +1 -1
- package/build/715.bundle.js.map +1 -1
- package/build/76.bundle.js +2 -0
- package/build/76.bundle.js.map +1 -0
- package/build/862.bundle.js +2 -0
- package/build/862.bundle.js.map +1 -0
- package/build/955.bundle.js +2 -0
- package/build/955.bundle.js.map +1 -0
- package/build/App.d.ts +4 -3
- package/build/app.bundle.js +41 -41
- package/build/app.bundle.js.map +1 -1
- package/build/components/AppStateLoader.component.d.ts +7 -0
- package/build/components/types.d.ts +1 -0
- package/build/displayer/DisplayForm.component.d.ts +19 -0
- package/build/displayer/Empty.d.ts +1 -0
- package/build/displayer/components/DisplaySingleValue.component.d.ts +1 -0
- package/build/displayer/list/formatter.d.ts +112 -10
- package/build/displayer/types.d.ts +54 -2
- package/build/redux/slice/AppState.slice.d.ts +29 -0
- package/build/redux/slice/Application.slice.d.ts +6 -0
- package/build/redux/slice/Article.slice.d.ts +6 -0
- package/build/redux/slice/Model.slice.d.ts +6 -0
- package/build/redux/slice/Toast.slice.d.ts +6 -0
- package/build/redux/slice/User.slice.d.ts +6 -0
- package/build/redux/store.d.ts +8 -1
- package/build/utils/types.d.ts +14 -5
- package/package.json +6 -5
- package/src/App.tsx +41 -8
- package/src/components/AppStateLoader.component.tsx +54 -0
- package/src/components/index.tsx +10 -0
- package/src/components/types.ts +1 -0
- package/src/displayer/DisplayAttribute.component.tsx +1 -2
- package/src/displayer/DisplayForm.component.tsx +78 -0
- package/src/displayer/Empty.tsx +4 -0
- package/src/displayer/components/DisplaySingleValue.component.tsx +6 -0
- package/src/displayer/list/formatter.tsx +182 -14
- package/src/displayer/types.ts +60 -2
- package/src/displayer/useDisplay.ts +32 -15
- package/src/redux/slice/AppState.slice.ts +25 -0
- package/src/redux/store.tsx +27 -21
- 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
|
+
};
|
package/src/components/index.tsx
CHANGED
package/src/components/types.ts
CHANGED
|
@@ -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
|
+
});
|
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/displayer/types.ts
CHANGED
|
@@ -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
|
-
|
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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;
|