@truedat/qx 7.13.6 → 7.13.8

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 (74) hide show
  1. package/package.json +3 -3
  2. package/src/components/QxRoutes.js +8 -6
  3. package/src/components/common/ResourceSelector.js +25 -6
  4. package/src/components/common/TypeSelector.js +14 -9
  5. package/src/components/common/__tests__/__snapshots__/ResourceSelector.spec.js.snap +269 -241
  6. package/src/components/common/__tests__/__snapshots__/TypeSelector.spec.js.snap +198 -190
  7. package/src/components/common/expressions/Clauses.js +19 -11
  8. package/src/components/common/expressions/Condition.js +32 -31
  9. package/src/components/common/expressions/FieldSelector.js +30 -16
  10. package/src/components/common/expressions/FunctionSelector.js +38 -23
  11. package/src/components/common/expressions/ShapeSelector.js +6 -5
  12. package/src/components/common/expressions/__tests__/ShapeSelector.spec.js +1 -1
  13. package/src/components/common/expressions/__tests__/__snapshots__/Clauses.spec.js.snap +36 -12
  14. package/src/components/common/expressions/__tests__/__snapshots__/Condition.spec.js.snap +87 -75
  15. package/src/components/common/expressions/__tests__/__snapshots__/ConstantSelector.spec.js.snap +99 -97
  16. package/src/components/common/expressions/__tests__/__snapshots__/Expression.spec.js.snap +236 -216
  17. package/src/components/common/expressions/__tests__/__snapshots__/FunctionArgs.spec.js.snap +97 -89
  18. package/src/components/common/expressions/__tests__/__snapshots__/FunctionSelector.spec.js.snap +373 -345
  19. package/src/components/common/expressions/constantInputs/AnySelector.js +2 -1
  20. package/src/components/common/expressions/constantInputs/BooleanSelector.js +20 -15
  21. package/src/components/common/expressions/constantInputs/DefaultSelector.js +0 -1
  22. package/src/components/common/expressions/constantInputs/__tests__/__snapshots__/AnySelector.spec.js.snap +189 -182
  23. package/src/components/common/expressions/constantInputs/__tests__/__snapshots__/BooleanSelector.spec.js.snap +74 -66
  24. package/src/components/common/expressions/constantInputs/__tests__/__snapshots__/DefaultSelector.spec.js.snap +2 -4
  25. package/src/components/common/resourceSelectors/DataStructureSelector.js +5 -4
  26. package/src/components/common/resourceSelectors/DataViewSelector.js +4 -3
  27. package/src/components/common/resourceSelectors/ReferenceDatasetSelector.js +4 -3
  28. package/src/components/common/resourceSelectors/__tests__/__snapshots__/DataStructureSelector.spec.js.snap +65 -61
  29. package/src/components/common/resourceSelectors/__tests__/__snapshots__/DataViewSelector.spec.js.snap +38 -34
  30. package/src/components/common/resourceSelectors/__tests__/__snapshots__/ReferenceDatasetSelector.spec.js.snap +38 -34
  31. package/src/components/dataViews/BreadCrumb.js +20 -0
  32. package/src/components/dataViews/DataViewEditor.js +169 -178
  33. package/src/components/dataViews/DataViews.js +113 -135
  34. package/src/components/dataViews/__tests__/AdvancedDataViewEditor.spec.js +260 -0
  35. package/src/components/dataViews/__tests__/DataViewEditor.spec.js +173 -239
  36. package/src/components/dataViews/__tests__/DataViewSelect.spec.js +1 -1
  37. package/src/components/dataViews/__tests__/DataViews.spec.js +124 -51
  38. package/src/components/dataViews/__tests__/Queryable.spec.js +1 -1
  39. package/src/components/dataViews/__tests__/Queryables.spec.js +1 -1
  40. package/src/components/dataViews/__tests__/SimpleDataViewEditor.spec.js +164 -0
  41. package/src/components/dataViews/__tests__/__snapshots__/{DataViewEditor.spec.js.snap → AdvancedDataViewEditor.spec.js.snap} +230 -200
  42. package/src/components/dataViews/__tests__/__snapshots__/DataViews.spec.js.snap +141 -29
  43. package/src/components/dataViews/__tests__/__snapshots__/Queryable.spec.js.snap +184 -141
  44. package/src/components/dataViews/__tests__/__snapshots__/Queryables.spec.js.snap +126 -91
  45. package/src/components/dataViews/actions/CancelButton.js +33 -0
  46. package/src/components/dataViews/actions/DeleteButton.js +33 -0
  47. package/src/components/dataViews/advancedForm/AdvancedDataViewEditor.js +159 -0
  48. package/src/components/dataViews/{DataViewSelect.js → advancedForm/DataViewSelect.js} +2 -2
  49. package/src/components/dataViews/{Queryable.js → advancedForm/Queryable.js} +2 -2
  50. package/src/components/dataViews/queryableFunctions.js +7 -0
  51. package/src/components/dataViews/queryableProperties/GroupBy.js +23 -27
  52. package/src/components/dataViews/queryableProperties/Join.js +0 -3
  53. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/From.spec.js.snap +30 -26
  54. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/GroupBy.spec.js.snap +130 -102
  55. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Join.spec.js.snap +42 -31
  56. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Select.spec.js.snap +81 -69
  57. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/SelectField.spec.js.snap +62 -54
  58. package/src/components/dataViews/queryableProperties/__tests__/__snapshots__/Where.spec.js.snap +12 -4
  59. package/src/components/dataViews/simpleForm/AggregationForm.js +179 -0
  60. package/src/components/dataViews/simpleForm/DatasetForm.js +199 -0
  61. package/src/components/dataViews/simpleForm/FormQueryable.js +114 -0
  62. package/src/components/dataViews/simpleForm/InformationForm.js +107 -0
  63. package/src/components/dataViews/simpleForm/SelectionForm.js +50 -0
  64. package/src/components/dataViews/simpleForm/SimpleDataViewEditor.js +265 -0
  65. package/src/components/functions/__tests__/__snapshots__/FunctionEditor.spec.js.snap +663 -631
  66. package/src/components/functions/__tests__/__snapshots__/FunctionParams.spec.js.snap +113 -109
  67. package/src/components/qualityControls/__tests__/__snapshots__/ControlProperties.spec.js.snap +92 -76
  68. package/src/components/qualityControls/__tests__/__snapshots__/EditQualityControl.spec.js.snap +108 -80
  69. package/src/components/qualityControls/__tests__/__snapshots__/NewDraftQualityControl.spec.js.snap +108 -80
  70. package/src/components/qualityControls/__tests__/__snapshots__/QualityControlEditor.spec.js.snap +108 -80
  71. package/src/components/qualityControls/controlProperties/__tests__/__snapshots__/Count.spec.js.snap +40 -36
  72. package/src/components/qualityControls/controlProperties/__tests__/__snapshots__/Ratio.spec.js.snap +92 -76
  73. package/src/hooks/useDataViews.js +7 -0
  74. /package/src/components/dataViews/{Queryables.js → advancedForm/Queryables.js} +0 -0
@@ -1,194 +1,185 @@
1
1
  import _ from "lodash/fp";
2
- import { lazy, Fragment, useEffect } from "react";
3
- import PropTypes from "prop-types";
2
+ import { Fragment, useState, useEffect } from "react";
3
+ import { useQuery } from "@apollo/client";
4
4
  import { useIntl } from "react-intl";
5
+ import PropTypes from "prop-types";
6
+ import { Loading } from "@truedat/core/components";
7
+ import { Button, Divider, Segment } from "semantic-ui-react";
8
+ import { useParams, useNavigate } from "react-router";
5
9
  import {
6
- Button,
7
- Container,
8
- Divider,
9
- Grid,
10
- GridColumn,
11
- Header,
12
- Form,
13
- } from "semantic-ui-react";
14
- import { FormProvider, useForm, Controller } from "react-hook-form";
10
+ useDataViewFetch,
11
+ useDataViews,
12
+ useDataViewCreate,
13
+ useDataViewUpdate,
14
+ } from "@truedat/qx/hooks/useDataViews";
15
+ import { REFERENCE_DATASETS_HEADERS_QUERY } from "@truedat/dd/api/queries";
16
+ import { DATA_VIEWS } from "@truedat/core/routes";
17
+ import { useFunctions } from "@truedat/qx/hooks/useFunctions";
18
+ import { lowerDeburrTrim } from "@truedat/core/services/sort";
19
+ import AdvancedDataViewEditor from "./advancedForm/AdvancedDataViewEditor";
20
+ import SimpleDataViewEditor from "./simpleForm/SimpleDataViewEditor";
21
+ import BreadCrumb from "./BreadCrumb";
15
22
  import { ConfirmModal } from "@truedat/core/components";
16
- import { DescriptionInput } from "@truedat/qx/components/common";
17
- import QxContext from "@truedat/qx/components/QxContext";
18
- import DataViewSelect from "./DataViewSelect";
19
- import Queryables from "./Queryables";
20
-
21
- const SourceSelector = lazy(
22
- () => import("@truedat/cx/sources/components/SourceSelector")
23
- );
24
-
25
- export default function DataViewEditor({
26
- selectedDataView,
27
- context,
28
- onSubmit,
29
- onCancel,
30
- onDelete,
31
- isSubmitting,
32
- setDirty,
33
- }) {
23
+
24
+ const NEW_DATA_VIEW = {
25
+ name: "",
26
+ description: "",
27
+ mode: "guided",
28
+ queryables: [
29
+ {
30
+ id: 0,
31
+ alias: "",
32
+ type: "from",
33
+ properties: {
34
+ resource: null,
35
+ },
36
+ },
37
+ ],
38
+ select: { type: "select" },
39
+ };
40
+
41
+ export function DataViewDetailEditor({ dataView }) {
42
+ const selectedDataView = _.defaultTo(NEW_DATA_VIEW)(dataView);
43
+ const viewMode = selectedDataView?.mode || "advanced";
44
+ const [mode, setMode] = useState("guided");
34
45
  const { formatMessage } = useIntl();
35
- const form = useForm({
36
- mode: "onTouched",
37
- defaultValues: selectedDataView,
38
- });
39
- const { control, handleSubmit, watch, formState } = form;
40
- const { isDirty, isValid } = formState;
46
+ const navigate = useNavigate();
47
+ const { data: functionsData, loading: functionsLoading } = useFunctions();
48
+ const { data, loading: dataViewsLoading } = useDataViews();
49
+ const { data: referenceDatasetsData, loading: referenceDatasetsLoading } =
50
+ useQuery(REFERENCE_DATASETS_HEADERS_QUERY);
51
+ const loading =
52
+ dataViewsLoading || functionsLoading || referenceDatasetsLoading;
53
+ const dataViews = _.orderBy(["id"], ["asc"])(data?.data);
54
+ const functions = functionsData?.data;
55
+ const referenceDatasets = _.flow(
56
+ _.getOr([], "referenceDatasets"),
57
+ _.sortBy([({ name }) => lowerDeburrTrim(name), "name"]),
58
+ _.map((row) => ({ ...row, id: Number(row?.id) }))
59
+ )(referenceDatasetsData);
41
60
 
42
61
  useEffect(() => {
43
- setDirty(isDirty);
44
- }, [setDirty, isDirty]);
62
+ setMode(viewMode);
63
+ }, [viewMode, setMode]);
64
+
65
+ const context = {
66
+ dataViews,
67
+ referenceDatasets,
68
+ functions,
69
+ currentDataViewId: selectedDataView?.id,
70
+ };
45
71
 
46
- const isEditForm = !_.isNil(selectedDataView?.id);
47
- const sourceId = watch("source_id");
48
- if (!selectedDataView) return null;
72
+ const { trigger: createDataView, isMutating: isCreating } =
73
+ useDataViewCreate();
74
+
75
+ const { trigger: updateDataView, isMutating: isUpdating } =
76
+ useDataViewUpdate(selectedDataView);
77
+
78
+ const isSubmitting = isCreating || isUpdating;
79
+ const onDataViewCreate = (data_view) => {
80
+ const mutateDataView = selectedDataView?.id
81
+ ? updateDataView
82
+ : createDataView;
83
+
84
+ mutateDataView({ data_view }).then(() => {
85
+ navigate(DATA_VIEWS);
86
+ });
87
+ };
88
+
89
+ const clearForm = () => {
90
+ navigate(DATA_VIEWS);
91
+ };
92
+
93
+ const editorProps = {
94
+ selectedDataView: selectedDataView,
95
+ context: context,
96
+ onCancel: clearForm,
97
+ onSubmit: onDataViewCreate,
98
+ isSubmitting: isSubmitting,
99
+ mode: mode,
100
+ };
49
101
 
50
102
  return (
51
- <Fragment>
52
- <FormProvider {...form}>
53
- <Form>
54
- <Header as="h3" dividing>
55
- {watch("name") || formatMessage({ id: "dataViews.action.new" })}
56
- </Header>
57
- <Grid>
58
- <GridColumn>
59
- <Grid.Row>
60
- <Controller
61
- control={control}
62
- name="name"
63
- rules={{
64
- required: formatMessage(
65
- { id: "form.validation.required" },
66
- { prop: formatMessage({ id: "dataViews.form.name" }) }
67
- ),
68
- }}
69
- render={({
70
- field: { onBlur, onChange, value },
71
- fieldState: { error },
72
- }) => (
73
- <Form.Field required>
74
- <label>{formatMessage({ id: "group.props.name" })}</label>
75
- <Form.Input
76
- autoComplete="off"
77
- placeholder={formatMessage({
78
- id: "dataViews.form.name",
79
- })}
80
- error={error?.message}
81
- onBlur={onBlur}
82
- onChange={(_e, { value }) => onChange(value)}
83
- value={value}
84
- />
85
- </Form.Field>
86
- )}
87
- />
88
- </Grid.Row>
89
- <Grid.Row>
90
- <Controller
91
- name="description"
92
- control={control}
93
- render={({ field: { onBlur, onChange, value } }) => (
94
- <DescriptionInput
95
- onBlur={onBlur}
96
- value={value}
97
- onChange={(_e, { value }) => onChange(value)}
98
- />
99
- )}
100
- />
101
- </Grid.Row>
102
- </GridColumn>
103
- </Grid>
104
- <Controller
105
- control={control}
106
- name="source_id"
107
- rules={{ required: true }}
108
- render={({ field: { onChange, value } }) => (
109
- <Form.Field required>
110
- <label>{formatMessage({ id: "dataViews.form.source" })}</label>
111
- <SourceSelector
112
- disabled={isEditForm}
113
- value={value + ""}
114
- onChange={(_e, { value }) => onChange(value)}
115
- />
116
- </Form.Field>
117
- )}
118
- />
119
- {!_.isNil(sourceId) ? (
120
- <QxContext value={{ ...context, sourceId }}>
121
- <Queryables />
122
- <DataViewSelect />
123
- </QxContext>
124
- ) : null}
125
- <Divider hidden />
126
- <Container textAlign="right">
103
+ <Segment loading={loading}>
104
+ <Button.Group floated={"right"}>
105
+ <ConfirmModal
106
+ trigger={
127
107
  <Button
128
- onClick={handleSubmit((data) => onSubmit(data))}
129
- primary
130
- loading={isSubmitting}
131
- disabled={!isValid || !isDirty}
132
- content={formatMessage({ id: "actions.save" })}
108
+ positive={mode === "guided"}
109
+ disabled={viewMode === "advanced" || mode == "guided"}
110
+ icon="compass"
111
+ content={formatMessage({ id: "actions.mode.guided" })}
112
+
133
113
  />
134
- {isDirty ? (
135
- <ConfirmModal
136
- trigger={
137
- <Button
138
- content={formatMessage({ id: "actions.cancel" })}
139
- disabled={isSubmitting}
140
- />
141
- }
142
- header={formatMessage({
143
- id: "actions.discard.confirmation.header",
144
- })}
145
- content={formatMessage({
146
- id: "actions.discard.confirmation.content",
147
- })}
148
- onConfirm={onCancel}
149
- onOpen={(e) => e.stopPropagation()}
150
- onClose={(e) => e.stopPropagation()}
151
- />
152
- ) : (
153
- <Button
154
- content={formatMessage({ id: "actions.cancel" })}
155
- disabled={isSubmitting}
156
- onClick={onCancel}
157
- />
158
- )}
159
- {onDelete ? (
160
- <ConfirmModal
161
- trigger={
162
- <Button
163
- color="red"
164
- content={formatMessage({ id: "actions.delete" })}
165
- disabled={isSubmitting}
166
- />
167
- }
168
- header={formatMessage({
169
- id: "dataViews.action.delete.header",
170
- })}
171
- content={formatMessage({
172
- id: "dataViews.action.delete.content",
173
- })}
174
- onConfirm={onDelete}
175
- onOpen={(e) => e.stopPropagation()}
176
- onClose={(e) => e.stopPropagation()}
177
- />
178
- ) : null}
179
- </Container>
180
- </Form>
181
- </FormProvider>
182
- </Fragment>
114
+ }
115
+ header={formatMessage({ id: "actions.mode.confirmation.header" })}
116
+ size="small"
117
+ content={formatMessage({ id: "actions.mode.confirmation.content" })}
118
+ onConfirm={() => setMode("guided")}
119
+ onOpen={(e) => e.stopPropagation()}
120
+ onClose={(e) => e.stopPropagation()}
121
+ />
122
+ <Button.Or text={formatMessage({ id: "actions.or" })} />
123
+ <ConfirmModal
124
+ trigger={
125
+ <Button
126
+ positive={mode === "advanced"}
127
+ disabled={mode === "advanced"}
128
+ icon="code"
129
+ content={formatMessage({ id: "actions.mode.advanced" })}
130
+
131
+ />
132
+ }
133
+ header={formatMessage({ id: "actions.mode.confirmation.header" })}
134
+ size="small"
135
+ content={formatMessage({ id: "actions.mode.confirmation.content" })}
136
+ onConfirm={() => setMode("advanced")}
137
+ onOpen={(e) => e.stopPropagation()}
138
+ onClose={(e) => e.stopPropagation()}
139
+ />
140
+ </Button.Group>
141
+
142
+ <Divider hidden />
143
+ <Fragment>
144
+ {mode === "guided" ? (
145
+ <SimpleDataViewEditor {...editorProps} />
146
+ ) : (
147
+ <AdvancedDataViewEditor {...editorProps} />
148
+ )}
149
+ <Divider hidden />
150
+ </Fragment>
151
+ </Segment>
183
152
  );
184
153
  }
185
154
 
186
- DataViewEditor.propTypes = {
187
- selectedDataView: PropTypes.object,
188
- context: PropTypes.object,
189
- onSubmit: PropTypes.func,
190
- onCancel: PropTypes.func,
191
- onDelete: PropTypes.func,
192
- isSubmitting: PropTypes.bool,
193
- setDirty: PropTypes.func,
155
+ DataViewDetailEditor.propTypes = {
156
+ dataView: PropTypes.object,
194
157
  };
158
+
159
+ export default function DataViewEditor() {
160
+ const { id } = useParams();
161
+ const { formatMessage } = useIntl();
162
+
163
+ if (_.isNil(id)) {
164
+ return (
165
+ <>
166
+ <BreadCrumb text={formatMessage({ id: "dataViews.new" })} />
167
+ <Divider hidden />
168
+ <DataViewDetailEditor />
169
+ </>
170
+ );
171
+ }
172
+
173
+ const { dataView, loading, error } = useDataViewFetch(id);
174
+
175
+ if (error) return null;
176
+ if (loading) return <Loading />;
177
+
178
+ return (
179
+ <>
180
+ <BreadCrumb text={dataView?.name} />
181
+ <Divider hidden />
182
+ <DataViewDetailEditor dataView={dataView} />
183
+ </>
184
+ );
185
+ }
@@ -1,168 +1,146 @@
1
1
  import _ from "lodash/fp";
2
- import { useState } from "react";
3
- import { useIntl, FormattedMessage } from "react-intl";
4
- import { useQuery } from "@apollo/client";
2
+ import { useMemo, useState } from "react";
3
+ import { compile } from "path-to-regexp";
5
4
  import {
5
+ Card,
6
6
  Button,
7
- Grid,
8
- GridColumn,
7
+ Divider,
9
8
  Header,
10
9
  Icon,
11
- List,
10
+ Input,
12
11
  Segment,
13
12
  } from "semantic-ui-react";
14
- import { REFERENCE_DATASETS_HEADERS_QUERY } from "@truedat/dd/api/queries";
15
- import { lowerDeburrTrim } from "@truedat/core/services/sort";
13
+ import { Link } from "react-router";
14
+ import { useIntl } from "react-intl";
15
+ import PropTypes from "prop-types";
16
+ import { DATA_VIEW, DATA_VIEWS_NEW } from "@truedat/core/routes";
16
17
  import {
17
18
  useDataViews,
18
- useDataViewCreate,
19
19
  useDataViewDelete,
20
- useDataViewUpdate,
21
20
  } from "@truedat/qx/hooks/useDataViews";
22
- import { useFunctions } from "@truedat/qx/hooks/useFunctions";
23
- import DataViewEditor from "./DataViewEditor";
21
+ import DeleteButton from "./actions/DeleteButton";
24
22
 
25
- const NEW_DATA_VIEW = {
26
- name: "",
27
- description: "",
28
- queryables: [
29
- {
30
- id: 0,
31
- alias: "",
32
- type: "from",
33
- properties: {
34
- resource: null,
35
- },
36
- },
37
- ],
38
- select: {
39
- type: "select",
40
- },
41
- };
23
+ export function filterDataView(data, searchTerm, keys) {
24
+ if (!searchTerm.trim()) return data;
42
25
 
43
- export default function DataViews() {
44
- const { formatMessage } = useIntl();
45
- const { data, loading: dataViewsLoading, mutate } = useDataViews();
46
- const { data: functionsData, loading: functionsLoading } = useFunctions();
47
- const { data: referenceDatasetsData, loading: referenceDatasetsLoading } =
48
- useQuery(REFERENCE_DATASETS_HEADERS_QUERY);
26
+ const lower = searchTerm.toLowerCase();
49
27
 
50
- const loading =
51
- dataViewsLoading || functionsLoading || referenceDatasetsLoading;
52
- const [selectedDataView, setSelectedDataView] = useState();
53
- const [isDirty, setDirty] = useState(false);
28
+ return data.filter((item) =>
29
+ (keys ?? Object.keys(item)).some((key) => {
30
+ const value = item[key];
31
+ return value && String(value).toLowerCase().includes(lower);
32
+ })
33
+ );
34
+ }
54
35
 
55
- const dataViews = _.orderBy(["id"], ["asc"])(data?.data);
56
- const functions = functionsData?.data;
57
- const referenceDatasets = _.flow(
58
- _.getOr([], "referenceDatasets"),
59
- _.sortBy([({ name }) => lowerDeburrTrim(name), "name"]),
60
- _.map((row) => ({ ...row, id: Number(row?.id) }))
61
- )(referenceDatasetsData);
36
+ function DataViewCard({ dataView, onDeleteSuccess }) {
37
+ const { formatMessage } = useIntl();
38
+ const { trigger: deleteDataView, isMutating: isDeleting } =
39
+ useDataViewDelete(dataView);
62
40
 
63
- const context = {
64
- dataViews,
65
- referenceDatasets,
66
- functions,
67
- currentDataViewId: selectedDataView?.id,
41
+ const onDelete = () => {
42
+ deleteDataView({ data_view: dataView }).then(() => {
43
+ onDeleteSuccess?.();
44
+ });
68
45
  };
69
46
 
70
- const changeSelectedDataView = (selection) => {
71
- setSelectedDataView(null);
72
- setSelectedDataView(selection);
73
- };
74
- const setStateNewDataView = () => changeSelectedDataView(NEW_DATA_VIEW);
75
- const clearForm = () => {
76
- setSelectedDataView(null);
77
- setDirty(false);
78
- };
47
+ return (
48
+ <Card>
49
+ <Card.Content>
50
+ <Card.Header
51
+ as={Link}
52
+ to={compile(DATA_VIEW)({ id: `${dataView.id}` })}
53
+ >
54
+ <Icon
55
+ name={formatMessage({
56
+ id: `dataViews.mode.icon.${dataView.mode || "advanced"}`,
57
+ })}
58
+ size="small"
59
+ />
60
+ {dataView.name || formatMessage({ id: "dataViews.untitled" })}
61
+ </Card.Header>
62
+ {dataView.description && (
63
+ <Card.Description>{dataView.description}</Card.Description>
64
+ )}
65
+ </Card.Content>
79
66
 
80
- const { trigger: createDataView, isMutating: isCreating } =
81
- useDataViewCreate();
67
+ <Card.Content extra>
68
+ <div className="ui actions">
69
+ <DeleteButton
70
+ floated="right"
71
+ onConfirm={onDelete}
72
+ disabled={isDeleting}
73
+ />
74
+ </div>
75
+ </Card.Content>
76
+ </Card>
77
+ );
78
+ }
82
79
 
83
- const { trigger: updateDataView, isMutating: isUpdating } =
84
- useDataViewUpdate(selectedDataView);
80
+ DataViewCard.propTypes = {
81
+ dataView: PropTypes.object.isRequired,
82
+ onDeleteSuccess: PropTypes.func,
83
+ };
85
84
 
86
- const { trigger: deleteDataView, isMutating: isDeleting } =
87
- useDataViewDelete(selectedDataView);
85
+ export default function DataViews() {
86
+ const { formatMessage } = useIntl();
87
+ const { data, loading: dataViewsLoading, mutate } = useDataViews();
88
+ const dataViews = _.orderBy(["id"], ["asc"])(data?.data || []);
89
+ const [searchInput, setSearchInput] = useState("");
90
+
91
+ const filteredData = useMemo(
92
+ () => filterDataView(dataViews, searchInput, ["name", "description"]),
93
+ [dataViews, searchInput]
94
+ );
88
95
 
89
- const isSubmitting = isCreating || isUpdating || isDeleting;
90
- const onDataViewCreate = (data_view) => {
91
- const mutateDataView = selectedDataView?.id
92
- ? updateDataView
93
- : createDataView;
94
- mutateDataView({ data_view }).then(() => {
95
- clearForm();
96
- mutate();
97
- });
98
- };
99
- const onDataViewDelete = (data_view) => {
100
- deleteDataView({ data_view }).then(() => {
101
- clearForm();
102
- mutate();
103
- });
104
- };
105
96
  return (
106
- <Segment loading={loading}>
97
+ <Segment loading={dataViewsLoading}>
107
98
  <Header as="h2">
108
99
  <Icon circular name="archive" />
109
100
  <Header.Content>
110
- <FormattedMessage id="dataViews.header" />
101
+ {formatMessage({ id: "dataViews.header" })}
111
102
  <Header.Subheader>
112
- <FormattedMessage id="dataViews.subheader" />
103
+ {formatMessage({ id: "dataViews.subheader" })}
113
104
  </Header.Subheader>
114
105
  </Header.Content>
115
106
  </Header>
116
- <Grid>
117
- <GridColumn width={4}>
118
- <Button fluid onClick={setStateNewDataView} disabled={isDirty}>
119
- {formatMessage({ id: "dataViews.action.new" })}
120
- </Button>
121
- <List divided selection={!isDirty}>
122
- {!_.isEmpty(dataViews) ? (
123
- dataViews.map((dataView, key) => (
124
- <List.Item
125
- key={key}
126
- onClick={() => !isDirty && changeSelectedDataView(dataView)}
127
- >
128
- <List.Content>
129
- <List.Header>{dataView.name || "DataView"}</List.Header>
130
- </List.Content>
131
- </List.Item>
132
- ))
133
- ) : (
134
- <List.Item>
135
- <List.Content>
136
- <List.Header>
137
- {formatMessage({ id: "dataViews.empty_list" })}
138
- </List.Header>
139
- </List.Content>
140
- </List.Item>
141
- )}
142
- </List>
143
- </GridColumn>
144
- <GridColumn width={11}>
145
- {selectedDataView ? (
146
- <DataViewEditor
147
- key={selectedDataView?.id || "new"}
148
- selectedDataView={selectedDataView}
149
- context={context}
150
- onCancel={clearForm}
151
- onSubmit={onDataViewCreate}
152
- onDelete={selectedDataView?.id ? onDataViewDelete : null}
153
- isSubmitting={isSubmitting}
154
- setDirty={setDirty}
155
- />
156
- ) : (
157
- <Header as="h2" icon textAlign="center">
158
- <Icon name="hand pointer outline" />
159
- <Header.Subheader>
160
- {formatMessage({ id: "dataViews.no_selection" })}
161
- </Header.Subheader>
162
- </Header>
163
- )}
164
- </GridColumn>
165
- </Grid>
107
+ <Divider hidden />
108
+ <Button
109
+ floated="right"
110
+ content={formatMessage({ id: "dataViews.action.new" })}
111
+ primary
112
+ as={Link}
113
+ to={DATA_VIEWS_NEW}
114
+ />
115
+ {dataViews.length === 0 ? (
116
+ <Header as="h4">
117
+ <Icon name="search" />
118
+ <Header.Content>
119
+ {formatMessage({ id: "dataViews.empty_list" })}
120
+ </Header.Content>
121
+ </Header>
122
+ ) : (
123
+ <>
124
+ <Input
125
+ icon
126
+ placeholder={formatMessage({ id: "search.placeholder" })}
127
+ onChange={(e, { value }) => setSearchInput(value)}
128
+ >
129
+ <input />
130
+ <Icon name="search" />
131
+ </Input>
132
+ <Divider hidden />
133
+ <Card.Group stackable itemsPerRow={5}>
134
+ {filteredData.map((dv) => (
135
+ <DataViewCard
136
+ key={dv.id}
137
+ dataView={dv}
138
+ onDeleteSuccess={mutate}
139
+ />
140
+ ))}
141
+ </Card.Group>
142
+ </>
143
+ )}
166
144
  </Segment>
167
145
  );
168
146
  }