@panneau/app 1.0.1-alpha.0 → 1.0.3-alpha.2

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 (75) hide show
  1. package/assets/css/styles.css +7 -0
  2. package/es/index.js +1810 -5
  3. package/lib/index.js +1841 -15
  4. package/package.json +66 -54
  5. package/scss/styles.scss +3 -0
  6. package/es/Panneau.js +0 -318
  7. package/es/actions/AuthActions.js +0 -16
  8. package/es/actions/LayoutActions.js +0 -14
  9. package/es/components/App.js +0 -129
  10. package/es/components/Layout.js +0 -68
  11. package/es/components/Panneau.js +0 -79
  12. package/es/components/index.js +0 -3
  13. package/es/components/pages/Account.js +0 -38
  14. package/es/components/pages/Home.js +0 -12
  15. package/es/components/pages/ResourceCreate.js +0 -44
  16. package/es/components/pages/ResourceDelete.js +0 -11
  17. package/es/components/pages/ResourceEdit.js +0 -28
  18. package/es/components/pages/ResourceForm.js +0 -343
  19. package/es/components/pages/ResourceIndex.js +0 -261
  20. package/es/components/pages/ResourceShow.js +0 -15
  21. package/es/components/partials/ResourceFormHeader.js +0 -111
  22. package/es/components/partials/ResourceIndexHeader.js +0 -71
  23. package/es/defaults/routes.json +0 -10
  24. package/es/lib/createStore.js +0 -30
  25. package/es/reducers/AuthReducer.js +0 -32
  26. package/es/reducers/index.js +0 -4
  27. package/lib/Panneau.js +0 -340
  28. package/lib/actions/AuthActions.js +0 -25
  29. package/lib/actions/LayoutActions.js +0 -25
  30. package/lib/components/App.js +0 -159
  31. package/lib/components/Layout.js +0 -87
  32. package/lib/components/Panneau.js +0 -103
  33. package/lib/components/index.js +0 -23
  34. package/lib/components/pages/Account.js +0 -54
  35. package/lib/components/pages/Home.js +0 -23
  36. package/lib/components/pages/ResourceCreate.js +0 -65
  37. package/lib/components/pages/ResourceDelete.js +0 -22
  38. package/lib/components/pages/ResourceEdit.js +0 -44
  39. package/lib/components/pages/ResourceForm.js +0 -376
  40. package/lib/components/pages/ResourceIndex.js +0 -289
  41. package/lib/components/pages/ResourceShow.js +0 -28
  42. package/lib/components/partials/ResourceFormHeader.js +0 -137
  43. package/lib/components/partials/ResourceIndexHeader.js +0 -87
  44. package/lib/defaults/routes.json +0 -10
  45. package/lib/lib/createStore.js +0 -46
  46. package/lib/reducers/AuthReducer.js +0 -43
  47. package/lib/reducers/index.js +0 -15
  48. package/src/Panneau.js +0 -274
  49. package/src/actions/AuthActions.js +0 -17
  50. package/src/actions/LayoutActions.js +0 -12
  51. package/src/components/App.jsx +0 -121
  52. package/src/components/Layout.jsx +0 -51
  53. package/src/components/Panneau.jsx +0 -95
  54. package/src/components/index.js +0 -7
  55. package/src/components/pages/Account.jsx +0 -34
  56. package/src/components/pages/Home.jsx +0 -19
  57. package/src/components/pages/ResourceCreate.jsx +0 -50
  58. package/src/components/pages/ResourceDelete.jsx +0 -18
  59. package/src/components/pages/ResourceEdit.jsx +0 -39
  60. package/src/components/pages/ResourceForm.jsx +0 -361
  61. package/src/components/pages/ResourceIndex.jsx +0 -256
  62. package/src/components/pages/ResourceShow.jsx +0 -23
  63. package/src/components/partials/ResourceFormHeader.jsx +0 -137
  64. package/src/components/partials/ResourceIndexHeader.jsx +0 -84
  65. package/src/defaults/routes.json +0 -10
  66. package/src/index.js +0 -9
  67. package/src/lib/createStore.js +0 -21
  68. package/src/reducers/AuthReducer.js +0 -23
  69. package/src/reducers/index.js +0 -5
  70. package/src/styles/layout.scss +0 -5
  71. package/src/styles/pages/resource-form.scss +0 -42
  72. package/src/styles/pages/resource-index.scss +0 -43
  73. package/src/styles/partials/resource-form-header.scss +0 -7
  74. package/src/styles/partials/resource-index-header.scss +0 -13
  75. package/src/styles/vendor.global.scss +0 -5
@@ -1,361 +0,0 @@
1
- /* eslint-disable react/jsx-props-no-spreading */
2
- import React, { useCallback, useState, useEffect, useMemo } from 'react';
3
- import PropTypes from 'prop-types';
4
- import isObject from 'lodash/isObject';
5
- import classNames from 'classnames';
6
- import { defineMessages } from 'react-intl';
7
- import { PropTypes as PanneauPropTypes, useResourceApi } from '@panneau/core';
8
- import { getErrorsFromResponseError } from '@panneau/core/requests';
9
- import { Loading, Errors } from '@panneau/core/components';
10
- import { useComponent } from '@panneau/core/contexts';
11
-
12
- import ResourceFormHeader from '../partials/ResourceFormHeader';
13
-
14
- import styles from '../../styles/pages/resource-form.scss';
15
-
16
- export const messages = defineMessages({
17
- cancel: {
18
- id: 'app.buttons.resources.cancel',
19
- description: 'The label of the "cancel" button',
20
- defaultMessage: 'Cancel',
21
- },
22
- save: {
23
- id: 'app.buttons.resources.save',
24
- description: 'The label of the "save" button',
25
- defaultMessage: 'Save',
26
- },
27
- title: {
28
- id: 'app.titles.resources.default',
29
- description: 'The title of the resource form',
30
- defaultMessage: '{name}',
31
- },
32
- titleTyped: {
33
- id: 'app.titles.resources.typed',
34
- description: 'The title of the typed resource form',
35
- defaultMessage: '{name} <small class="text-muted">({type})</small>',
36
- },
37
- successNotice: {
38
- id: 'app.notices.resources.success',
39
- description: 'The text of the "success" form notice',
40
- defaultMessage: 'Success!',
41
- },
42
- errorNotice: {
43
- id: 'app.notices.resources.error',
44
- description: 'The text of the "error" form notice',
45
- defaultMessage: 'Failed. The form contains errors.',
46
- },
47
- });
48
-
49
- const propTypes = {
50
- action: PropTypes.string,
51
- resource: PanneauPropTypes.resource.isRequired,
52
- resourceApi: PanneauPropTypes.resourceApi.isRequired,
53
- itemId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
54
- item: PropTypes.shape({
55
- id: PropTypes.number,
56
- type: PropTypes.string,
57
- }),
58
- query: PropTypes.shape({
59
- type: PropTypes.string,
60
- }),
61
- successNoticeLabel: PanneauPropTypes.label,
62
- errorNoticeLabel: PanneauPropTypes.label,
63
- buttons: PanneauPropTypes.buttons,
64
- saveButtonLabel: PanneauPropTypes.message,
65
- confirmSwitchTypeMessage: PanneauPropTypes.message,
66
- errors: PropTypes.arrayOf(PropTypes.string),
67
- formValue: PropTypes.shape({}),
68
- formErrors: PropTypes.objectOf(PropTypes.array),
69
- readOnly: PropTypes.bool,
70
- title: PanneauPropTypes.label,
71
- titleTyped: PanneauPropTypes.label,
72
- gotoResourceAction: PropTypes.func.isRequired,
73
- onFormComplete: PropTypes.func,
74
- };
75
-
76
- const defaultProps = {
77
- action: 'create',
78
- title: messages.title,
79
- titleTyped: messages.titleTyped,
80
- query: null,
81
- itemId: null,
82
- item: null,
83
- errors: null,
84
- formValue: null,
85
- formErrors: null,
86
- readOnly: false,
87
- successNoticeLabel: messages.successNotice,
88
- errorNoticeLabel: messages.errorNotice,
89
- confirmSwitchTypeMessage: messages.confirmSwitchType,
90
- buttons: [
91
- {
92
- id: 'cancel',
93
- type: 'button',
94
- label: messages.cancel,
95
- className: 'btn-link btn-lg',
96
- },
97
- {
98
- id: 'submit',
99
- type: 'submit',
100
- label: messages.save,
101
- className: 'btn-primary btn-lg',
102
- },
103
- ],
104
- saveButtonLabel: null,
105
- onFormComplete: null,
106
- };
107
-
108
- const ResourceForm = ({
109
- resource,
110
- item: currentItem,
111
- itemId,
112
- query,
113
- action,
114
- readOnly,
115
- errors: currentErrors,
116
- formValue: initialFormValue,
117
- formErrors: initialFormErrors,
118
- title,
119
- titleTyped,
120
- buttons,
121
- saveButtonLabel,
122
- successNoticeLabel,
123
- errorNoticeLabel,
124
- onFormComplete: customOnFormComplete,
125
- gotoResourceAction,
126
- }) => {
127
- const [item, setItem] = useState(currentItem);
128
- const [isLoading, setIsLoading] = useState(false);
129
- const [errors, setErrors] = useState(currentErrors || item);
130
- const [formValue, setFormValue] = useState(initialFormValue || item);
131
- const [formErrors, setFormErrors] = useState(initialFormErrors || item);
132
- const [formSuccess, setFormSuccess] = useState(initialFormErrors !== null ? false : null);
133
- const resourceApi = useResourceApi(resource);
134
- const isTyped = resource.type() === 'typed';
135
- const waitingItem = action === 'edit' && item === null;
136
-
137
- // Load item if needed
138
- useEffect(() => {
139
- if (isLoading) {
140
- return;
141
- }
142
- const onItemLoaded = newItem => {
143
- setItem(newItem);
144
- };
145
- const onItemLoadError = newErrors => {
146
- setErrors(newErrors);
147
- };
148
- const itemChanged = item !== null && `${item.id}` !== `${itemId}`;
149
- const itemShouldReload = item === null || itemChanged;
150
- if ((action === 'edit' || action === 'show') && itemShouldReload) {
151
- setIsLoading(true);
152
- resourceApi
153
- .show(itemId)
154
- .then(onItemLoaded)
155
- .catch(onItemLoadError)
156
- .then(() => {
157
- setIsLoading(false);
158
- });
159
- }
160
- }, [action, item, itemId, isLoading]);
161
-
162
- // Get current type
163
- const currentType = useMemo(() => {
164
- if (!isTyped || waitingItem) {
165
- return null;
166
- }
167
-
168
- const types = resource.types();
169
- const itemTypeId = item !== null ? item.type || null : null;
170
- const { type: queryTypeId = null } = query || {};
171
- const typeId = itemTypeId || queryTypeId;
172
- const definedType = typeId !== null ? types.find(it => it.id === typeId) || null : null;
173
- if (definedType !== null) {
174
- return definedType;
175
- }
176
-
177
- return types.length > 0
178
- ? types.find(({ default: isDefault = false }) => isDefault) || types[0] || null
179
- : null;
180
- }, [item, query, resource]);
181
-
182
- const onClickCancel = useCallback(
183
- e => {
184
- e.preventDefault();
185
- gotoResourceAction('index');
186
- },
187
- [gotoResourceAction],
188
- );
189
-
190
- const formButtons = useMemo(
191
- () =>
192
- readOnly
193
- ? []
194
- : buttons.map(button => {
195
- if (button.id === 'save' && saveButtonLabel !== null) {
196
- return {
197
- ...button,
198
- label: saveButtonLabel,
199
- };
200
- }
201
- if (button.id === 'cancel' && typeof button.onClick === 'undefined') {
202
- return {
203
- ...button,
204
- onClick: onClickCancel,
205
- };
206
- }
207
- return button;
208
- }),
209
- [buttons, readOnly],
210
- );
211
-
212
- const submitForm = useCallback(() => {
213
- const data = isTyped
214
- ? {
215
- type: currentType !== null ? currentType.id : null,
216
- ...(formValue || item),
217
- }
218
- : formValue;
219
-
220
- setFormSuccess(null);
221
-
222
- return action === 'create'
223
- ? resourceApi.store(data)
224
- : resourceApi.update(item.id, data || item);
225
- }, [resourceApi, action, isTyped, currentType]);
226
-
227
- const onFormChange = useCallback(value => {
228
- setFormValue(value);
229
- setFormSuccess(null);
230
- }, []);
231
-
232
- const onFormComplete = useCallback(
233
- newItem => {
234
- setItem(newItem);
235
- setFormValue(null);
236
- setFormErrors(null);
237
- setFormSuccess(true);
238
- if (customOnFormComplete !== null) {
239
- customOnFormComplete(item);
240
- }
241
- },
242
- [customOnFormComplete],
243
- );
244
-
245
- const onFormErrors = useCallback(error => {
246
- const newErrors = getErrorsFromResponseError(error);
247
- setFormErrors(newErrors);
248
- setFormSuccess(false);
249
- }, []);
250
-
251
- // Get form definition
252
- const { type: formType = null, fullscreen, className = null, ...formProps } = useMemo(() => {
253
- const { fields = [], ...form } = resource.form(action);
254
- let finalFields;
255
- if (waitingItem) {
256
- finalFields = null;
257
- } else if (currentType !== null && isObject(fields)) {
258
- finalFields = fields[currentType.id] || fields.default || fields;
259
- } else {
260
- finalFields = fields;
261
- }
262
- return {
263
- type: 'normal',
264
- fullscreen: false,
265
- fields: finalFields,
266
- ...form,
267
- };
268
- }, [waitingItem, resource, currentType]);
269
-
270
- const FormComponent = useComponent(formType, 'forms');
271
- const form =
272
- !waitingItem && FormComponent !== null ? (
273
- <FormComponent
274
- {...formProps}
275
- className={classNames([
276
- styles.form,
277
- {
278
- [className]: className !== null,
279
- },
280
- ])}
281
- readOnly={readOnly}
282
- buttons={formButtons}
283
- value={formValue || item}
284
- errors={formErrors}
285
- notice={
286
- formSuccess !== null
287
- ? {
288
- type: formSuccess ? 'success' : 'error',
289
- label: formSuccess ? successNoticeLabel : errorNoticeLabel,
290
- }
291
- : null
292
- }
293
- submitForm={submitForm}
294
- onChange={onFormChange}
295
- onComplete={onFormComplete}
296
- onErrors={onFormErrors}
297
- />
298
- ) : null;
299
-
300
- const header = (
301
- <ResourceFormHeader
302
- resource={resource}
303
- type={currentType}
304
- action={action}
305
- fullscreen={fullscreen}
306
- valueHasChanged={formValue !== null}
307
- title={title}
308
- titleTyped={titleTyped}
309
- />
310
- );
311
-
312
- const errorsMessages =
313
- errors !== null && errors.length > 0 ? (
314
- <Errors errors={errors} className={styles.errors} />
315
- ) : null;
316
-
317
- const content =
318
- isLoading && item === null ? (
319
- <div className={classNames(['py-4', styles.loading])}>
320
- <Loading loading />
321
- </div>
322
- ) : (
323
- form
324
- );
325
-
326
- return (
327
- <div
328
- className={classNames([
329
- styles.container,
330
- {
331
- [styles.isFullscreen]: fullscreen,
332
- },
333
- ])}
334
- >
335
- {fullscreen ? (
336
- <div className={classNames([styles.inner])}>
337
- {header}
338
- <div className={classNames([styles.content])}>
339
- {errorsMessages}
340
- {content}
341
- </div>
342
- </div>
343
- ) : (
344
- <div className="container">
345
- <div className="row justify-content-md-center">
346
- <div className="col-lg-9">
347
- {header}
348
- {errorsMessages}
349
- {content}
350
- </div>
351
- </div>
352
- </div>
353
- )}
354
- </div>
355
- );
356
- };
357
-
358
- ResourceForm.propTypes = propTypes;
359
- ResourceForm.defaultProps = defaultProps;
360
-
361
- export default ResourceForm;
@@ -1,256 +0,0 @@
1
- /* eslint-disable jsx-a11y/anchor-is-valid, react/no-array-index-key, react/jsx-props-no-spreading */
2
- import React, { useCallback, useEffect, useState } from 'react';
3
- import PropTypes from 'prop-types';
4
- import get from 'lodash/get';
5
- import classNames from 'classnames';
6
- import { defineMessages, injectIntl } from 'react-intl';
7
- import { PropTypes as PanneauPropTypes, useResourceApi } from '@panneau/core';
8
- import { isMessage } from '@panneau/core/utils';
9
- import { useComponent } from '@panneau/core/contexts';
10
- import { Loading, Errors } from '@panneau/core/components';
11
-
12
- import ResourceIndexHeader from '../partials/ResourceIndexHeader';
13
-
14
- import styles from '../../styles/pages/resource-index.scss';
15
-
16
- const messages = defineMessages({
17
- add: {
18
- id: 'core.buttons.resources.add',
19
- description: 'The label of the "add" index button',
20
- defaultMessage: 'Add {name}',
21
- },
22
- title: {
23
- id: 'core.titles.resources.index',
24
- description: 'The title of the resource index page',
25
- defaultMessage: '{name}',
26
- },
27
- confirmDelete: {
28
- id: 'core.resources.index.confirm_delete',
29
- description: 'The confirm message when deleting on the resource index page',
30
- defaultMessage: 'Are you sure you want to delete this item from {name}?',
31
- },
32
- });
33
-
34
- const propTypes = {
35
- intl: PanneauPropTypes.intl.isRequired,
36
- resource: PanneauPropTypes.resource.isRequired,
37
- query: PropTypes.shape({
38
- page: PropTypes.string,
39
- }),
40
- title: PanneauPropTypes.message,
41
- showAddButton: PropTypes.bool,
42
- addButtonLabel: PanneauPropTypes.message,
43
- confirmDeleteMessage: PanneauPropTypes.message,
44
- getResourceActionUrl: PropTypes.func.isRequired,
45
- gotoResourceAction: PropTypes.func.isRequired,
46
- };
47
-
48
- const defaultProps = {
49
- query: null,
50
- title: messages.title,
51
- showAddButton: true,
52
- addButtonLabel: messages.add,
53
- confirmDeleteMessage: messages.confirmDelete,
54
- };
55
-
56
- const ResourceIndex = ({
57
- intl,
58
- resource,
59
- query,
60
- title,
61
- addButtonLabel,
62
- confirmDeleteMessage,
63
- getResourceActionUrl,
64
- gotoResourceAction,
65
- showAddButton,
66
- }) => {
67
- const [request, setRequest] = useState({
68
- isLoading: false,
69
- query: null,
70
- items: null,
71
- errors: null,
72
- pagination: null,
73
- });
74
- const resourceApi = useResourceApi(resource);
75
- const { isLoading, items, pagination, errors } = request;
76
- const { type: listType, pagination: hasPagination = false, ...listProps } = resource.list(
77
- 'index',
78
- );
79
-
80
- useEffect(() => {
81
- if (isLoading) {
82
- return null;
83
- }
84
- let canceled = false;
85
- const onItemsLoaded = data => {
86
- if (canceled) {
87
- return;
88
- }
89
- if (hasPagination) {
90
- const { data: newItems, ...newPagination } = data;
91
- setRequest({
92
- ...request,
93
- items: newItems,
94
- pagination: newPagination,
95
- isLoading: false,
96
- });
97
- return;
98
- }
99
- setRequest({
100
- ...request,
101
- items: data,
102
- isLoading: false,
103
- });
104
- };
105
-
106
- const onItemsLoadError = newErrors => {
107
- if (canceled) {
108
- return;
109
- }
110
- setRequest({
111
- ...request,
112
- errors: newErrors,
113
- isLoading: false,
114
- });
115
- };
116
-
117
- const { page = null } = query || {};
118
- const params = {};
119
- if (hasPagination && page !== null) {
120
- params.page = page;
121
- }
122
- resourceApi
123
- .index(params)
124
- .then(onItemsLoaded)
125
- .catch(onItemsLoadError);
126
-
127
- setRequest({
128
- ...request,
129
- isLoading: true,
130
- });
131
-
132
- return () => {
133
- canceled = true;
134
- };
135
- }, [query, hasPagination]);
136
-
137
- const deleteItem = useCallback(
138
- id => {
139
- const onItemDeleted = ({ id: itemId }) => {
140
- setRequest({
141
- ...request,
142
- items: items.filter(it => it.id !== itemId),
143
- });
144
- };
145
-
146
- const { name } = resource;
147
- const confirmMessage = resource.message('confirm_delete', confirmDeleteMessage);
148
- const message = isMessage(confirmMessage)
149
- ? intl.formatMessage(confirmMessage, {
150
- name,
151
- id,
152
- })
153
- : confirmMessage;
154
- // eslint-disable-next-line no-alert
155
- if (window.confirm(message)) {
156
- resourceApi.destroy(id).then(onItemDeleted);
157
- }
158
- },
159
- [resource, request],
160
- );
161
-
162
- const onClickAction = useCallback(
163
- (e, action, it) => {
164
- const useRouter = get(action, 'useRouter', true);
165
-
166
- switch (action.id) {
167
- case 'edit':
168
- case 'show':
169
- if (useRouter) {
170
- gotoResourceAction(action.id, it.id);
171
- } else {
172
- window.location.href = getResourceActionUrl(action.id);
173
- }
174
- break;
175
- case 'delete': {
176
- const hasPage = get(action, 'hasPage', false);
177
- if (!hasPage) {
178
- deleteItem(it.id);
179
- } else if (useRouter) {
180
- gotoResourceAction(action.id, it.id);
181
- } else {
182
- window.location.href = getResourceActionUrl(action.id);
183
- }
184
- break;
185
- }
186
- default:
187
- break;
188
- }
189
- },
190
- [deleteItem, gotoResourceAction, getResourceActionUrl],
191
- );
192
-
193
- // Components
194
- const ListComponent = useComponent(listType || 'table', 'lists');
195
- const Pagination = useComponent('pagination', 'lists');
196
-
197
- return (
198
- <div className={classNames([styles.container])}>
199
- <div className="container">
200
- <div className="row justify-content-md-center">
201
- <div className="col-lg-8">
202
- <ResourceIndexHeader
203
- resource={resource}
204
- title={title}
205
- addButtonLabel={addButtonLabel}
206
- showAddButton={showAddButton}
207
- getResourceActionUrl={getResourceActionUrl}
208
- />
209
- <div className={styles.listContainer}>
210
- <Errors errors={errors} />
211
- <div className={styles.list}>
212
- {items !== null && ListComponent !== null ? (
213
- <div className={styles.list}>
214
- <ListComponent
215
- {...listProps}
216
- items={items || []}
217
- onClickAction={onClickAction}
218
- />
219
- </div>
220
- ) : null}
221
- </div>
222
- {isLoading ? (
223
- <div
224
- className={classNames({
225
- [styles.loading]: true,
226
- [styles.alone]: items === null,
227
- })}
228
- >
229
- <div className={styles.inner}>
230
- <div className={styles.middle}>
231
- <Loading loading />
232
- </div>
233
- </div>
234
- </div>
235
- ) : null}
236
- </div>
237
- {pagination !== null && pagination.last_page > 1 ? (
238
- <Pagination
239
- total={pagination.total}
240
- perPage={pagination.per_page}
241
- currentPage={pagination.current_page}
242
- lastPage={pagination.last_page}
243
- url={getResourceActionUrl('index')}
244
- />
245
- ) : null}
246
- </div>
247
- </div>
248
- </div>
249
- </div>
250
- );
251
- };
252
-
253
- ResourceIndex.propTypes = propTypes;
254
- ResourceIndex.defaultProps = defaultProps;
255
-
256
- export default injectIntl(ResourceIndex);
@@ -1,23 +0,0 @@
1
- import React from 'react';
2
-
3
- import ResourceForm from './ResourceForm';
4
-
5
- const propTypes = {
6
-
7
- };
8
-
9
- const defaultProps = {
10
-
11
- };
12
-
13
- const ResourceShow = props => (
14
- <ResourceForm
15
- readOnly
16
- {...props}
17
- />
18
- );
19
-
20
- ResourceShow.propTypes = propTypes;
21
- ResourceShow.defaultProps = defaultProps;
22
-
23
- export default ResourceShow;