@eeacms/volto-clms-theme 1.1.203 → 1.1.205
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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/components/Blocks/CclRelatedListingBlock/CclRelatedListingView.jsx +22 -4
- package/src/components/CLMSDatasetDetailView/DataSetInfoContent.jsx +2 -2
- package/src/components/CLMSDownloadsView/FileCard.jsx +1 -1
- package/src/components/Widgets/OrderDocumentsWidget.jsx +174 -0
- package/src/components/Widgets/OrderDocumentsWidget.test.jsx +209 -0
- package/src/index.js +2 -0
- package/theme/site/extras/custom.overrides +2 -1
- package/theme/site/extras/order-documents-widget.less +67 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
### [1.1.205](https://github.com/eea/volto-clms-theme/compare/1.1.204...1.1.205) - 18 December 2024
|
|
8
|
+
|
|
9
|
+
#### :rocket: New Features
|
|
10
|
+
|
|
11
|
+
- feat: add a drag-and-drop field in the dataset to arrange the technical documents in the accordion - refs #271669 [ana-oprea - [`8aebc43`](https://github.com/eea/volto-clms-theme/commit/8aebc43986f30b0092a0ac77f3e653cfc94a179d)]
|
|
12
|
+
|
|
13
|
+
### [1.1.204](https://github.com/eea/volto-clms-theme/compare/1.1.203...1.1.204) - 9 December 2024
|
|
14
|
+
|
|
15
|
+
#### :bug: Bug Fixes
|
|
16
|
+
|
|
17
|
+
- fix: In downloads page, sometimes the dataset name is not loaded - refs #279962 [ana-oprea - [`48a85bf`](https://github.com/eea/volto-clms-theme/commit/48a85bfb55262082e7d8e26716ac47ef3159ae4e)]
|
|
18
|
+
|
|
7
19
|
### [1.1.203](https://github.com/eea/volto-clms-theme/compare/1.1.202...1.1.203) - 3 December 2024
|
|
8
20
|
|
|
9
21
|
### [1.1.202](https://github.com/eea/volto-clms-theme/compare/1.1.201...1.1.202) - 20 November 2024
|
package/package.json
CHANGED
|
@@ -116,12 +116,30 @@ const CclRelatedListingView = (props) => {
|
|
|
116
116
|
}, [data, id, uid, dispatch]);
|
|
117
117
|
|
|
118
118
|
React.useEffect(() => {
|
|
119
|
-
if (sLoaded) {
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
if (sLoaded && properties?.technical_documents_order) {
|
|
120
|
+
const orderMap = new Map(
|
|
121
|
+
properties?.technical_documents_order.items?.map((item, index) => [
|
|
122
|
+
item.id,
|
|
123
|
+
index,
|
|
124
|
+
]) || [],
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Sort libraries based on the technical_documents_order
|
|
128
|
+
const sorted = [...libraries].sort(
|
|
129
|
+
(a, b) =>
|
|
130
|
+
(orderMap.get(a['@id']) ?? Infinity) -
|
|
131
|
+
(orderMap.get(b['@id']) ?? Infinity),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
p_functions.setOriginalDataList([...sorted]);
|
|
135
|
+
p_functions.setDataList([...sorted]);
|
|
136
|
+
}
|
|
137
|
+
if (sLoaded && !properties?.technical_documents_order) {
|
|
138
|
+
p_functions.setOriginalDataList([...libraries]);
|
|
139
|
+
p_functions.setDataList([...libraries]);
|
|
122
140
|
}
|
|
123
141
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
124
|
-
}, [sLoaded]);
|
|
142
|
+
}, [sLoaded, properties.technical_documents_order, libraries]);
|
|
125
143
|
|
|
126
144
|
return (
|
|
127
145
|
<>
|
|
@@ -35,10 +35,10 @@ const DataSetInfoContent = (props) => {
|
|
|
35
35
|
if (UID) {
|
|
36
36
|
dispatch(
|
|
37
37
|
searchContent(
|
|
38
|
-
'',
|
|
38
|
+
'en/technical-library',
|
|
39
39
|
{
|
|
40
40
|
portal_type: 'TechnicalLibrary',
|
|
41
|
-
path: '/',
|
|
41
|
+
path: 'en/technical-library',
|
|
42
42
|
associated_datasets: UID,
|
|
43
43
|
b_size: 99999,
|
|
44
44
|
},
|
|
@@ -41,7 +41,7 @@ const DatasetNaming = (props) => {
|
|
|
41
41
|
const { dataset } = props;
|
|
42
42
|
return (
|
|
43
43
|
<>
|
|
44
|
-
{`${dataset['name']
|
|
44
|
+
{`${dataset['name'] || dataset['DatasetTitle'] || 'loading...'} -
|
|
45
45
|
${
|
|
46
46
|
dataset?.OutputFormat
|
|
47
47
|
? dataset?.OutputFormat
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React, { useEffect, useCallback, useMemo } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useLocation } from 'react-router-dom';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
|
+
import { searchContent } from '@plone/volto/actions';
|
|
6
|
+
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
|
7
|
+
import { FormFieldWrapper, Icon } from '@plone/volto/components';
|
|
8
|
+
import { Button } from 'semantic-ui-react';
|
|
9
|
+
import dragSVG from '@plone/volto/icons/drag.svg';
|
|
10
|
+
|
|
11
|
+
const reorder = (list, startIndex, endIndex) => {
|
|
12
|
+
const result = Array.from(list);
|
|
13
|
+
const [removed] = result.splice(startIndex, 1);
|
|
14
|
+
result.splice(endIndex, 0, removed);
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DocumentItem = React.memo(({ item, provided }) => (
|
|
19
|
+
<div
|
|
20
|
+
ref={provided.innerRef}
|
|
21
|
+
{...provided.draggableProps}
|
|
22
|
+
{...provided.dragHandleProps}
|
|
23
|
+
className="document-item"
|
|
24
|
+
role="listitem"
|
|
25
|
+
aria-label={`Draggable item: ${item.title}`}
|
|
26
|
+
>
|
|
27
|
+
<div style={{ display: 'inline-block' }}>
|
|
28
|
+
<Button icon basic aria-label="Drag handle">
|
|
29
|
+
<Icon
|
|
30
|
+
name={dragSVG}
|
|
31
|
+
size="20px"
|
|
32
|
+
color="#878f93"
|
|
33
|
+
className="content drag handle"
|
|
34
|
+
/>
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
<div>{item.title}</div>
|
|
38
|
+
</div>
|
|
39
|
+
));
|
|
40
|
+
|
|
41
|
+
const OrderDocumentsWidget = (props) => {
|
|
42
|
+
const { id, formData, onChange, value } = props;
|
|
43
|
+
const UID = formData?.UID;
|
|
44
|
+
const location = useLocation();
|
|
45
|
+
const dispatch = useDispatch();
|
|
46
|
+
const searchSubrequests = useSelector((state) => state.search.subrequests);
|
|
47
|
+
const [documentsList, setDocumentsList] = React.useState([]);
|
|
48
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
49
|
+
|
|
50
|
+
const fetchDocuments = useCallback(() => {
|
|
51
|
+
if (UID) {
|
|
52
|
+
const sort_on = ['documentation_sorting', 'sortable_title'];
|
|
53
|
+
const sort_order = ['ascending', 'ascending'];
|
|
54
|
+
|
|
55
|
+
setIsLoading(true);
|
|
56
|
+
dispatch(
|
|
57
|
+
searchContent(
|
|
58
|
+
'en/technical-library',
|
|
59
|
+
{
|
|
60
|
+
portal_type: 'TechnicalLibrary',
|
|
61
|
+
path: 'en/technical-library',
|
|
62
|
+
associated_datasets: UID,
|
|
63
|
+
sort_on: sort_on,
|
|
64
|
+
sort_order: sort_order,
|
|
65
|
+
b_size: 99999,
|
|
66
|
+
},
|
|
67
|
+
id,
|
|
68
|
+
),
|
|
69
|
+
).finally(() => setIsLoading(false));
|
|
70
|
+
}
|
|
71
|
+
}, [id, UID, dispatch]);
|
|
72
|
+
|
|
73
|
+
const handleDragEnd = useCallback(
|
|
74
|
+
(result) => {
|
|
75
|
+
if (!result.destination) return;
|
|
76
|
+
|
|
77
|
+
const reorderedList = reorder(
|
|
78
|
+
documentsList,
|
|
79
|
+
result.source.index,
|
|
80
|
+
result.destination.index,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
onChange(id, { items: reorderedList });
|
|
84
|
+
setDocumentsList(reorderedList);
|
|
85
|
+
},
|
|
86
|
+
[documentsList, onChange, id],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const memoizedList = useMemo(
|
|
90
|
+
() => (
|
|
91
|
+
<DragDropContext onDragEnd={handleDragEnd}>
|
|
92
|
+
<Droppable droppableId="documents">
|
|
93
|
+
{(provided) => (
|
|
94
|
+
<div
|
|
95
|
+
ref={provided.innerRef}
|
|
96
|
+
{...provided.droppableProps}
|
|
97
|
+
className="documents-list"
|
|
98
|
+
role="list"
|
|
99
|
+
aria-label="Draggable documents list"
|
|
100
|
+
>
|
|
101
|
+
{documentsList.map((item, index) => (
|
|
102
|
+
<Draggable key={item.id} draggableId={item.id} index={index}>
|
|
103
|
+
{(provided) => (
|
|
104
|
+
<DocumentItem item={item} provided={provided} />
|
|
105
|
+
)}
|
|
106
|
+
</Draggable>
|
|
107
|
+
))}
|
|
108
|
+
{provided.placeholder}
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
</Droppable>
|
|
112
|
+
</DragDropContext>
|
|
113
|
+
),
|
|
114
|
+
[documentsList, handleDragEnd],
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
fetchDocuments();
|
|
119
|
+
}, [fetchDocuments, location]);
|
|
120
|
+
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (searchSubrequests?.[id]?.items) {
|
|
123
|
+
const libraries = searchSubrequests[id].items;
|
|
124
|
+
const receivedDocumentsList = libraries.map((item) => ({
|
|
125
|
+
id: item['@id'],
|
|
126
|
+
title: item.title,
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
if (value?.items?.length < receivedDocumentsList.length) {
|
|
130
|
+
const newItems = receivedDocumentsList.filter(
|
|
131
|
+
(item) => !value.items.some((item2) => item2.id === item.id),
|
|
132
|
+
);
|
|
133
|
+
setDocumentsList([...value.items, ...newItems]);
|
|
134
|
+
} else {
|
|
135
|
+
setDocumentsList(value.items);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}, [searchSubrequests, id, value]);
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<FormFieldWrapper
|
|
142
|
+
{...props}
|
|
143
|
+
draggable={true}
|
|
144
|
+
className="drag-drop-list-widget"
|
|
145
|
+
>
|
|
146
|
+
<div className="order-documents-area">
|
|
147
|
+
<div className="documents-list-header">
|
|
148
|
+
<div>Technical Document Title</div>
|
|
149
|
+
</div>
|
|
150
|
+
{isLoading ? (
|
|
151
|
+
<div className="loading-documents">Loading documents...</div>
|
|
152
|
+
) : (
|
|
153
|
+
memoizedList
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
</FormFieldWrapper>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
OrderDocumentsWidget.propTypes = {
|
|
161
|
+
id: PropTypes.string.isRequired,
|
|
162
|
+
formData: PropTypes.object,
|
|
163
|
+
onChange: PropTypes.func.isRequired,
|
|
164
|
+
value: PropTypes.shape({
|
|
165
|
+
items: PropTypes.arrayOf(
|
|
166
|
+
PropTypes.shape({
|
|
167
|
+
id: PropTypes.string,
|
|
168
|
+
title: PropTypes.string,
|
|
169
|
+
}),
|
|
170
|
+
),
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export default React.memo(OrderDocumentsWidget);
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
3
|
+
import { Provider } from 'react-intl-redux';
|
|
4
|
+
import configureStore from 'redux-mock-store';
|
|
5
|
+
import OrderDocumentsWidget from './OrderDocumentsWidget';
|
|
6
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
7
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
8
|
+
|
|
9
|
+
const mockStore = configureStore([]);
|
|
10
|
+
|
|
11
|
+
jest.mock('@plone/volto/actions', () => ({
|
|
12
|
+
searchContent: jest.fn(() => ({
|
|
13
|
+
type: 'SEARCH_CONTENT',
|
|
14
|
+
path: 'en/technical-library',
|
|
15
|
+
subrequest: 'test-widget',
|
|
16
|
+
})),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe('OrderDocumentsWidget', () => {
|
|
20
|
+
const SPACE = { keyCode: 32 };
|
|
21
|
+
const ARROW_UP = { keyCode: 38 };
|
|
22
|
+
const ARROW_DOWN = { keyCode: 40 };
|
|
23
|
+
|
|
24
|
+
let store;
|
|
25
|
+
let mockOnChange;
|
|
26
|
+
let initialState;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
initialState = {
|
|
30
|
+
intl: {
|
|
31
|
+
locale: 'en',
|
|
32
|
+
messages: {},
|
|
33
|
+
},
|
|
34
|
+
search: {
|
|
35
|
+
subrequests: {
|
|
36
|
+
testId: {
|
|
37
|
+
items: [
|
|
38
|
+
{ '@id': 'doc1', title: 'Document 1' },
|
|
39
|
+
{ '@id': 'doc2', title: 'Document 2' },
|
|
40
|
+
{ '@id': 'doc3', title: 'Document 3' },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
store = mockStore(initialState);
|
|
47
|
+
mockOnChange = jest.fn();
|
|
48
|
+
store.dispatch = jest.fn(() => Promise.resolve());
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders the widget with documents', async () => {
|
|
52
|
+
act(() => {
|
|
53
|
+
render(
|
|
54
|
+
<Provider store={store}>
|
|
55
|
+
<MemoryRouter>
|
|
56
|
+
<OrderDocumentsWidget
|
|
57
|
+
id="testId"
|
|
58
|
+
formData={{ UID: 'someUID' }}
|
|
59
|
+
onChange={mockOnChange}
|
|
60
|
+
value={{ items: [] }}
|
|
61
|
+
/>
|
|
62
|
+
</MemoryRouter>
|
|
63
|
+
</Provider>,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
await Promise.resolve();
|
|
67
|
+
|
|
68
|
+
expect(screen.getByText('Technical Document Title')).toBeInTheDocument();
|
|
69
|
+
expect(screen.getByText('Document 1')).toBeInTheDocument();
|
|
70
|
+
expect(screen.getByText('Document 2')).toBeInTheDocument();
|
|
71
|
+
expect(screen.getByText('Document 3')).toBeInTheDocument();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('shows loading state when fetching documents', async () => {
|
|
75
|
+
act(() => {
|
|
76
|
+
render(
|
|
77
|
+
<Provider store={store}>
|
|
78
|
+
<MemoryRouter>
|
|
79
|
+
<OrderDocumentsWidget
|
|
80
|
+
id="testId"
|
|
81
|
+
formData={{ UID: 'someUID' }}
|
|
82
|
+
onChange={mockOnChange}
|
|
83
|
+
value={{ items: [] }}
|
|
84
|
+
/>
|
|
85
|
+
</MemoryRouter>
|
|
86
|
+
</Provider>,
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(screen.getByText('Loading documents...')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('calls onChange with reordered documents after drag-and-drop', async () => {
|
|
94
|
+
const { container } = render(
|
|
95
|
+
<Provider store={store}>
|
|
96
|
+
<MemoryRouter>
|
|
97
|
+
<OrderDocumentsWidget
|
|
98
|
+
id="testId"
|
|
99
|
+
formData={{ UID: 'someUID' }}
|
|
100
|
+
onChange={mockOnChange}
|
|
101
|
+
value={{
|
|
102
|
+
items: [
|
|
103
|
+
{ id: 'doc1', title: 'Document 1' },
|
|
104
|
+
{ id: 'doc2', title: 'Document 2' },
|
|
105
|
+
{ id: 'doc3', title: 'Document 3' },
|
|
106
|
+
],
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
</MemoryRouter>
|
|
110
|
+
</Provider>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
await Promise.resolve();
|
|
114
|
+
|
|
115
|
+
const dragItem = container.querySelector(
|
|
116
|
+
'div[aria-label="Draggable item: Document 1"]',
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
fireEvent.keyDown(dragItem, SPACE);
|
|
120
|
+
fireEvent.keyDown(dragItem, ARROW_DOWN);
|
|
121
|
+
fireEvent.keyDown(dragItem, SPACE);
|
|
122
|
+
|
|
123
|
+
expect(mockOnChange).toHaveBeenCalledWith('testId', {
|
|
124
|
+
items: [
|
|
125
|
+
{ id: 'doc2', title: 'Document 2' },
|
|
126
|
+
{ id: 'doc1', title: 'Document 1' },
|
|
127
|
+
{ id: 'doc3', title: 'Document 3' },
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const dragItem2 = container.querySelector(
|
|
132
|
+
'div[aria-label="Draggable item: Document 3"]',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
fireEvent.keyDown(dragItem2, SPACE);
|
|
136
|
+
fireEvent.keyDown(dragItem2, ARROW_UP);
|
|
137
|
+
fireEvent.keyDown(dragItem2, SPACE);
|
|
138
|
+
|
|
139
|
+
expect(mockOnChange).toHaveBeenCalledWith('testId', {
|
|
140
|
+
items: [
|
|
141
|
+
{ id: 'doc2', title: 'Document 2' },
|
|
142
|
+
{ id: 'doc3', title: 'Document 3' },
|
|
143
|
+
{ id: 'doc1', title: 'Document 1' },
|
|
144
|
+
],
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('updates state when new documents are fetched from Redux', async () => {
|
|
149
|
+
const { rerender } = render(
|
|
150
|
+
<Provider store={store}>
|
|
151
|
+
<MemoryRouter>
|
|
152
|
+
<OrderDocumentsWidget
|
|
153
|
+
id="testId"
|
|
154
|
+
formData={{ UID: 'someUID' }}
|
|
155
|
+
onChange={mockOnChange}
|
|
156
|
+
value={{ items: [] }}
|
|
157
|
+
/>
|
|
158
|
+
</MemoryRouter>
|
|
159
|
+
</Provider>,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
await Promise.resolve();
|
|
163
|
+
|
|
164
|
+
const updatedState = {
|
|
165
|
+
intl: {
|
|
166
|
+
locale: 'en',
|
|
167
|
+
messages: {},
|
|
168
|
+
},
|
|
169
|
+
search: {
|
|
170
|
+
subrequests: {
|
|
171
|
+
testId: {
|
|
172
|
+
items: [
|
|
173
|
+
{ '@id': 'doc1', title: 'Document 1' },
|
|
174
|
+
{ '@id': 'doc2', title: 'Document 2' },
|
|
175
|
+
{ '@id': 'doc3', title: 'Document 3' },
|
|
176
|
+
{ '@id': 'doc4', title: 'Document 4' },
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const updatedStore = mockStore(updatedState);
|
|
184
|
+
mockOnChange = jest.fn();
|
|
185
|
+
updatedStore.dispatch = jest.fn(() => Promise.resolve());
|
|
186
|
+
|
|
187
|
+
act(() => {
|
|
188
|
+
rerender(
|
|
189
|
+
<Provider store={updatedStore}>
|
|
190
|
+
<MemoryRouter>
|
|
191
|
+
<OrderDocumentsWidget
|
|
192
|
+
id="testId"
|
|
193
|
+
formData={{ UID: 'someUID' }}
|
|
194
|
+
onChange={mockOnChange}
|
|
195
|
+
value={{ items: [] }}
|
|
196
|
+
/>
|
|
197
|
+
</MemoryRouter>
|
|
198
|
+
</Provider>,
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
await Promise.resolve();
|
|
203
|
+
|
|
204
|
+
expect(screen.getByText('Document 1')).toBeInTheDocument();
|
|
205
|
+
expect(screen.getByText('Document 2')).toBeInTheDocument();
|
|
206
|
+
expect(screen.getByText('Document 3')).toBeInTheDocument();
|
|
207
|
+
expect(screen.getByText('Document 4')).toBeInTheDocument();
|
|
208
|
+
});
|
|
209
|
+
});
|
package/src/index.js
CHANGED
|
@@ -30,6 +30,7 @@ import CLMSTechnicalLibraryView from './components/CLMSTechnicalLibraryView/CLMS
|
|
|
30
30
|
import CLMSProductionUpdatesView from './components/CLMSProductionUpdatesView/CLMSProductionUpdatesView';
|
|
31
31
|
|
|
32
32
|
// WIDGET
|
|
33
|
+
import OrderDocumentsWidget from './components/Widgets/OrderDocumentsWidget';
|
|
33
34
|
import BoundingWidget from './components/Widgets/BoundingWidget';
|
|
34
35
|
import ContactWidget from './components/Widgets/ContactWidget';
|
|
35
36
|
import DatasetDownloadInformationWidget from './components/Widgets/DatasetDownloadInformationWidget';
|
|
@@ -98,6 +99,7 @@ const applyConfig = (config) => {
|
|
|
98
99
|
config.widgets.type.tabs = TabsWidget;
|
|
99
100
|
config.widgets.widget = {
|
|
100
101
|
...config.widgets.widget,
|
|
102
|
+
order_docs_widget: OrderDocumentsWidget,
|
|
101
103
|
bounding_widget: BoundingWidget,
|
|
102
104
|
layer_widget: MapLayersWidget,
|
|
103
105
|
downloadable_files_widget: DownloadableFilesTableWidget,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
.order-documents-area {
|
|
2
|
+
width: 100%;
|
|
3
|
+
max-width: 800px;
|
|
4
|
+
margin: 16px auto;
|
|
5
|
+
border: 1px solid #e0e0e0;
|
|
6
|
+
border-radius: 8px;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
background-color: #ffffff;
|
|
9
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
10
|
+
|
|
11
|
+
[data-rbd-draggable-context-id] {
|
|
12
|
+
margin-bottom: 0.5rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Table header */
|
|
16
|
+
.documents-list-header {
|
|
17
|
+
display: flex;
|
|
18
|
+
background-color: #f5f5f5;
|
|
19
|
+
padding: 12px 16px;
|
|
20
|
+
font-weight: bold;
|
|
21
|
+
border-bottom: 1px solid #e0e0e0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.documents-list-header div {
|
|
25
|
+
text-align: left;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* List container */
|
|
29
|
+
.documents-list {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
color: @clmsGreen;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Individual document items (rows) */
|
|
36
|
+
.document-item {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
padding: 12px 16px;
|
|
40
|
+
border-bottom: 1px solid #e0e0e0;
|
|
41
|
+
background-color: #ffffff;
|
|
42
|
+
transition: background-color 0.2s ease;
|
|
43
|
+
cursor: grab;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.document-item:last-child {
|
|
47
|
+
border-bottom: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.document-item:hover {
|
|
51
|
+
background-color: #f9f9f9;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Columns within a row */
|
|
55
|
+
.document-item div {
|
|
56
|
+
text-align: left;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
text-overflow: ellipsis;
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Dragging styles */
|
|
63
|
+
.document-item[aria-grabbed='true'] {
|
|
64
|
+
background-color: #f0f8ff;
|
|
65
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
66
|
+
}
|
|
67
|
+
}
|