quasar-ui-danx 0.0.11 → 0.0.12
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/package.json +6 -1
- package/src/components/ActionTable/ActionTable.vue +49 -41
- package/src/components/ActionTable/BatchActionMenu.vue +20 -20
- package/src/components/ActionTable/EmptyTableState.vue +5 -5
- package/src/components/ActionTable/Filters/CollapsableFiltersSidebar.vue +11 -11
- package/src/components/ActionTable/Filters/FilterGroupItem.vue +7 -7
- package/src/components/ActionTable/Filters/FilterGroupList.vue +29 -29
- package/src/components/ActionTable/Filters/FilterListToggle.vue +15 -15
- package/src/components/ActionTable/Filters/FilterableField.vue +82 -80
- package/src/components/ActionTable/Filters/index.ts +5 -0
- package/src/components/ActionTable/Form/Fields/BooleanField.vue +13 -13
- package/src/components/ActionTable/Form/Fields/ConfirmPasswordField.vue +11 -11
- package/src/components/ActionTable/Form/Fields/DateField.vue +13 -13
- package/src/components/ActionTable/Form/Fields/DateRangeField.vue +25 -25
- package/src/components/ActionTable/Form/Fields/DateTimeField.vue +21 -21
- package/src/components/ActionTable/Form/Fields/DateTimePicker.vue +23 -23
- package/src/components/ActionTable/Form/Fields/FileUploadButton.vue +31 -31
- package/src/components/ActionTable/Form/Fields/InlineDateTimeField.vue +19 -19
- package/src/components/ActionTable/Form/Fields/IntegerField.vue +7 -7
- package/src/components/ActionTable/Form/Fields/LabelValueBlock.vue +22 -0
- package/src/components/ActionTable/Form/Fields/LabeledInput.vue +19 -19
- package/src/components/ActionTable/Form/Fields/MultiFileField.vue +40 -40
- package/src/components/ActionTable/Form/Fields/MultiKeywordField.vue +23 -23
- package/src/components/ActionTable/Form/Fields/NewPasswordField.vue +10 -10
- package/src/components/ActionTable/Form/Fields/NumberField.vue +29 -29
- package/src/components/ActionTable/Form/Fields/NumberRangeField.vue +33 -33
- package/src/components/ActionTable/Form/Fields/SelectDrawer.vue +36 -36
- package/src/components/ActionTable/Form/Fields/SelectField.vue +66 -66
- package/src/components/ActionTable/Form/Fields/SelectWithChildrenField.vue +23 -23
- package/src/components/ActionTable/Form/Fields/SingleFileField.vue +32 -32
- package/src/components/ActionTable/Form/Fields/TextField.vue +36 -36
- package/src/components/ActionTable/Form/Fields/WysiwygField.vue +16 -16
- package/src/components/ActionTable/Form/Fields/index.ts +23 -23
- package/src/components/ActionTable/Form/RenderedForm.vue +27 -25
- package/src/components/ActionTable/Form/index.ts +2 -0
- package/src/components/ActionTable/TableSummaryRow.vue +33 -33
- package/src/components/ActionTable/index.ts +8 -13
- package/src/components/ActionTable/listActions.ts +340 -339
- package/src/components/ActionTable/listHelpers.ts +74 -0
- package/src/components/ActionTable/tableColumns.ts +56 -56
- package/src/components/DragAndDrop/HandleDraggable.vue +29 -29
- package/src/components/DragAndDrop/ListItemDraggable.vue +10 -10
- package/src/components/DragAndDrop/index.ts +0 -1
- package/src/components/DragAndDrop/listDragAndDrop.ts +1 -1
- package/src/components/Utility/CollapsableSidebar.vue +35 -35
- package/src/components/Utility/ContentDrawer.vue +20 -20
- package/src/components/Utility/Dialogs/ConfirmDialog.vue +55 -55
- package/src/components/Utility/Dialogs/FullScreenDialog.vue +18 -18
- package/src/components/Utility/Dialogs/FullscreenCarouselDialog.vue +105 -0
- package/src/components/Utility/Dialogs/InfoDialog.vue +10 -10
- package/src/components/Utility/Dialogs/InputDialog.vue +13 -13
- package/src/components/Utility/ImagePreview.vue +192 -0
- package/src/components/Utility/Popover/PopoverMenu.vue +64 -0
- package/src/components/Utility/Transitions/StaggeredListTransition.vue +15 -15
- package/src/components/Utility/index.ts +11 -9
- package/src/components/index.ts +1 -1
- package/src/helpers/FileUpload.ts +274 -273
- package/src/helpers/compatibility.ts +45 -45
- package/src/helpers/date.ts +2 -2
- package/src/helpers/download.ts +166 -158
- package/src/helpers/downloadPdf.ts +48 -48
- package/src/helpers/files.ts +42 -42
- package/src/helpers/index.ts +2 -0
- package/src/helpers/multiFileUpload.ts +56 -56
- package/src/helpers/singleFileUpload.ts +49 -49
- package/src/index.esm.js +3 -4
- package/src/svg/FilterIcon.svg +7 -0
- package/src/svg/ImageIcon.svg +30 -0
- package/src/svg/PdfIcon.svg +21 -0
- package/src/svg/PercentIcon.svg +13 -0
- package/src/svg/TrashIcon.svg +15 -0
- package/src/svg/XIcon.svg +18 -0
- package/src/svg/index.ts +8 -0
- package/src/vendor/tinymce-config.ts +1 -0
- package/src/vue-plugin.js +7 -4
- package/tsconfig.json +14 -13
- package/src/components/ActionTable/tableHelpers.ts +0 -83
- package/src/components/DragAndDrop/Icons/index.ts +0 -2
- /package/src/{components/DragAndDrop/Icons → svg}/DragHandleDotsIcon.svg +0 -0
- /package/src/{components/DragAndDrop/Icons → svg}/DragHandleIcon.svg +0 -0
@@ -1,361 +1,362 @@
|
|
1
|
-
import {
|
2
|
-
import { getItem, setItem } from "danx/src/helpers";
|
1
|
+
import { getItem, setItem } from "@ui/helpers";
|
3
2
|
import { computed, ref, watch } from "vue";
|
3
|
+
import { getFilterFromUrl, mapSortBy, waitForRef } from "./listHelpers";
|
4
4
|
|
5
5
|
export function useListActions(name, {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
listRoute,
|
7
|
+
filterFieldOptionsRoute,
|
8
|
+
summaryRoute = null,
|
9
|
+
moreRoute = null,
|
10
|
+
applyActionRoute = null,
|
11
|
+
applyBatchActionRoute = null,
|
12
|
+
itemDetailsRoute = null,
|
13
|
+
columns = null,
|
14
|
+
filterGroups = null,
|
15
|
+
refreshFilters = false,
|
16
|
+
urlPattern = null,
|
17
|
+
filterDefaults = {}
|
18
18
|
}) {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
}
|
60
|
-
|
61
|
-
async function loadList() {
|
62
|
-
isLoadingList.value = true;
|
63
|
-
setPagedItems(await listRoute(pager.value));
|
64
|
-
isLoadingList.value = false;
|
65
|
-
}
|
66
|
-
|
67
|
-
async function loadSummary() {
|
68
|
-
if (summaryRoute) {
|
69
|
-
isLoadingSummary.value = true;
|
70
|
-
const summaryFilter = { id: null, ...filter.value };
|
71
|
-
if (selectedRows.value.length) {
|
72
|
-
summaryFilter.id = selectedRows.value.map((row) => row.id);
|
73
|
-
}
|
74
|
-
summary.value = await summaryRoute(summaryFilter);
|
75
|
-
isLoadingSummary.value = false;
|
19
|
+
const PAGE_SETTINGS_KEY = `${name}-pagination-settings`;
|
20
|
+
const pagedItems = ref(null);
|
21
|
+
const filter = ref({});
|
22
|
+
const showFilters = ref(getItem(`${name}-show-filters`, true));
|
23
|
+
const selectedRows = ref([]);
|
24
|
+
const isLoadingList = ref(false);
|
25
|
+
const isLoadingSummary = ref(false);
|
26
|
+
const summary = ref(null);
|
27
|
+
|
28
|
+
const filterActiveCount = computed(() => Object.keys(filter.value).filter(key => filter.value[key] !== undefined).length);
|
29
|
+
|
30
|
+
const PAGING_DEFAULT = {
|
31
|
+
sortBy: null,
|
32
|
+
descending: false,
|
33
|
+
page: 1,
|
34
|
+
rowsNumber: 0,
|
35
|
+
rowsPerPage: 50
|
36
|
+
};
|
37
|
+
const quasarPagination = ref(PAGING_DEFAULT);
|
38
|
+
|
39
|
+
const pager = computed(() => ({
|
40
|
+
perPage: quasarPagination.value.rowsPerPage,
|
41
|
+
page: quasarPagination.value.page,
|
42
|
+
filter: filter.value,
|
43
|
+
sort: columns ? mapSortBy(quasarPagination.value, columns) : undefined
|
44
|
+
}));
|
45
|
+
|
46
|
+
// When any part of the filter changes, get the new list of creatives
|
47
|
+
watch(pager, () => {
|
48
|
+
saveSettings();
|
49
|
+
loadList();
|
50
|
+
});
|
51
|
+
watch(filter, () => {
|
52
|
+
saveSettings();
|
53
|
+
loadSummary();
|
54
|
+
});
|
55
|
+
watch(selectedRows, loadSummary);
|
56
|
+
|
57
|
+
if (refreshFilters) {
|
58
|
+
watch(filter, loadFilterFieldOptions);
|
76
59
|
}
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
const isLoadingFilters = ref(false);
|
83
|
-
|
84
|
-
watch(() => showFilters.value, (show) => {
|
85
|
-
setItem(`${name}-show-filters`, show);
|
86
|
-
});
|
87
|
-
|
88
|
-
async function loadFilterFieldOptions() {
|
89
|
-
isLoadingFilters.value = true;
|
90
|
-
filterFieldOptions.value = await filterFieldOptionsRoute(filter.value);
|
91
|
-
isLoadingFilters.value = false;
|
92
|
-
}
|
93
|
-
|
94
|
-
// A flat list of valid filterable field names
|
95
|
-
const validFilterKeys = computed(() => filterGroups?.value?.map(group => group.fields.map(field => field.name)).flat());
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Watches for a filter URL parameter and applies the filter if it is set.
|
99
|
-
*/
|
100
|
-
function applyFilterFromUrl(url) {
|
101
|
-
if (url.match(urlPattern)) {
|
102
|
-
const urlFilter = getFilterFromUrl(url, validFilterKeys.value);
|
103
|
-
|
104
|
-
if (Object.keys(urlFilter).length > 0) {
|
105
|
-
filter.value = urlFilter;
|
106
|
-
}
|
60
|
+
|
61
|
+
async function loadList() {
|
62
|
+
isLoadingList.value = true;
|
63
|
+
setPagedItems(await listRoute(pager.value));
|
64
|
+
isLoadingList.value = false;
|
107
65
|
}
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
}
|
66
|
+
|
67
|
+
async function loadSummary() {
|
68
|
+
if (summaryRoute) {
|
69
|
+
isLoadingSummary.value = true;
|
70
|
+
const summaryFilter = { id: null, ...filter.value };
|
71
|
+
if (selectedRows.value.length) {
|
72
|
+
summaryFilter.id = selectedRows.value.map((row) => row.id);
|
73
|
+
}
|
74
|
+
summary.value = await summaryRoute(summaryFilter);
|
75
|
+
isLoadingSummary.value = false;
|
76
|
+
}
|
120
77
|
}
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
/**
|
131
|
-
* Updates a row in the paged items list with the new item data. Uses the item's id to find the row.
|
132
|
-
*
|
133
|
-
* @param updatedItem
|
134
|
-
*/
|
135
|
-
function setItemInPagedList(updatedItem) {
|
136
|
-
const data = pagedItems.value?.data?.map(item => (item.id === updatedItem.id && (item.updated_at === null || item.updated_at <= updatedItem.updated_at)) ? updatedItem : item);
|
137
|
-
pagedItems.value = { ...pagedItems.value, data };
|
138
|
-
}
|
139
|
-
|
140
|
-
/**
|
141
|
-
* Loads more items into the list.
|
142
|
-
* @param index
|
143
|
-
* @param perPage
|
144
|
-
*/
|
145
|
-
async function loadMore(index, perPage = undefined) {
|
146
|
-
const newItems = await moreRoute({
|
147
|
-
page: index + 1,
|
148
|
-
perPage,
|
149
|
-
filter: filter.value
|
78
|
+
|
79
|
+
// Filter fields are the field values available for the currently applied filter on Creative Groups
|
80
|
+
// (ie: all states available under the current filter)
|
81
|
+
const filterFieldOptions = ref({});
|
82
|
+
const isLoadingFilters = ref(false);
|
83
|
+
|
84
|
+
watch(() => showFilters.value, (show) => {
|
85
|
+
setItem(`${name}-show-filters`, show);
|
150
86
|
});
|
151
87
|
|
152
|
-
|
153
|
-
|
154
|
-
|
88
|
+
async function loadFilterFieldOptions() {
|
89
|
+
isLoadingFilters.value = true;
|
90
|
+
filterFieldOptions.value = await filterFieldOptionsRoute(filter.value);
|
91
|
+
isLoadingFilters.value = false;
|
155
92
|
}
|
156
93
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
function loadSettings() {
|
172
|
-
const settings = getItem(PAGE_SETTINGS_KEY);
|
173
|
-
|
174
|
-
// Load the filter settings from local storage
|
175
|
-
if (settings) {
|
176
|
-
filter.value = { ...settings.filter, ...filter.value };
|
177
|
-
quasarPagination.value = settings.quasarPagination;
|
178
|
-
} else {
|
179
|
-
// If no local storage settings, apply the default filters
|
180
|
-
filter.value = { ...filterDefaults, ...filter.value };
|
94
|
+
// A flat list of valid filterable field names
|
95
|
+
const validFilterKeys = computed(() => filterGroups?.value?.map(group => group.fields.map(field => field.name)).flat());
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Watches for a filter URL parameter and applies the filter if it is set.
|
99
|
+
*/
|
100
|
+
function applyFilterFromUrl(url) {
|
101
|
+
if (url.match(urlPattern)) {
|
102
|
+
const urlFilter = getFilterFromUrl(url, validFilterKeys.value);
|
103
|
+
|
104
|
+
if (Object.keys(urlFilter).length > 0) {
|
105
|
+
filter.value = urlFilter;
|
106
|
+
}
|
107
|
+
}
|
181
108
|
}
|
182
109
|
|
183
|
-
//
|
184
|
-
|
110
|
+
// Set the reactive pager to map from the Laravel pagination to Quasar pagination
|
111
|
+
// and automatically update the list of ads
|
112
|
+
function setPagedItems(items) {
|
113
|
+
if (Array.isArray(items)) {
|
114
|
+
pagedItems.value = { data: items, meta: { total: items.length } };
|
115
|
+
} else {
|
116
|
+
pagedItems.value = items;
|
117
|
+
if (items?.meta && items.meta.total !== quasarPagination.value.rowsNumber) {
|
118
|
+
quasarPagination.value.rowsNumber = items.meta.total;
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
185
122
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
123
|
+
/**
|
124
|
+
* Resets the filter and pagination settings to their defaults.
|
125
|
+
*/
|
126
|
+
function resetPaging() {
|
127
|
+
quasarPagination.value = PAGING_DEFAULT;
|
128
|
+
}
|
190
129
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
}
|
200
|
-
|
201
|
-
/**
|
202
|
-
* Saves the current filter and pagination settings to local storage.
|
203
|
-
*/
|
204
|
-
async function saveSettings() {
|
205
|
-
const settings = {
|
206
|
-
filter: filter.value,
|
207
|
-
quasarPagination: { ...quasarPagination.value, page: 1 }
|
208
|
-
};
|
209
|
-
// save in local storage
|
210
|
-
setItem(PAGE_SETTINGS_KEY, settings);
|
211
|
-
}
|
212
|
-
|
213
|
-
/**
|
214
|
-
* Applies an action to an item.
|
215
|
-
*/
|
216
|
-
const isApplyingActionToItem = ref(null);
|
217
|
-
let actionResultCount = 0;
|
218
|
-
|
219
|
-
async function applyAction(item, input, itemData = {}) {
|
220
|
-
isApplyingActionToItem.value = item;
|
221
|
-
const resultNumber = ++actionResultCount;
|
222
|
-
setItemInPagedList({ ...item, ...input, ...itemData });
|
223
|
-
const result = await applyActionRoute(item, input);
|
224
|
-
if (result.success) {
|
225
|
-
// Only render the most recent campaign changes
|
226
|
-
if (resultNumber !== actionResultCount) return;
|
227
|
-
|
228
|
-
// Update the updated item in the previously loaded list if it exists
|
229
|
-
setItemInPagedList(result.item);
|
230
|
-
|
231
|
-
// Update the active item if it is the same as the updated item
|
232
|
-
if (activeItem.value?.id === result.item.id) {
|
233
|
-
activeItem.value = { ...activeItem.value, ...result.item };
|
234
|
-
}
|
130
|
+
/**
|
131
|
+
* Updates a row in the paged items list with the new item data. Uses the item's id to find the row.
|
132
|
+
*
|
133
|
+
* @param updatedItem
|
134
|
+
*/
|
135
|
+
function setItemInPagedList(updatedItem) {
|
136
|
+
const data = pagedItems.value?.data?.map(item => (item.id === updatedItem.id && (item.updated_at === null || item.updated_at <= updatedItem.updated_at)) ? updatedItem : item);
|
137
|
+
pagedItems.value = { ...pagedItems.value, data };
|
235
138
|
}
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
return result;
|
253
|
-
}
|
254
|
-
|
255
|
-
// The active ad for viewing / editing in the Ad Form
|
256
|
-
const activeItem = ref(null);
|
257
|
-
// Controls the tab on the Ad Form
|
258
|
-
const formTab = ref("general");
|
259
|
-
|
260
|
-
// Whenever the active item changes, fill the additional item details
|
261
|
-
// (ie: tasks, verifications, creatives, etc.)
|
262
|
-
if (itemDetailsRoute) {
|
263
|
-
watch(() => activeItem.value, async (newItem, oldItem) => {
|
264
|
-
if (newItem && oldItem?.id !== newItem.id) {
|
265
|
-
const result = await itemDetailsRoute(newItem);
|
266
|
-
|
267
|
-
// Only set the ad details if we are the response for the currently loaded item
|
268
|
-
// NOTE: race conditions might allow the finished loading item to be different to the currently requested item
|
269
|
-
if (result?.id === activeItem.value?.id) {
|
270
|
-
activeItem.value = result;
|
139
|
+
|
140
|
+
/**
|
141
|
+
* Loads more items into the list.
|
142
|
+
* @param index
|
143
|
+
* @param perPage
|
144
|
+
*/
|
145
|
+
async function loadMore(index, perPage = undefined) {
|
146
|
+
const newItems = await moreRoute({
|
147
|
+
page: index + 1,
|
148
|
+
perPage,
|
149
|
+
filter: filter.value
|
150
|
+
});
|
151
|
+
|
152
|
+
if (newItems && newItems.length > 0) {
|
153
|
+
pagedItems.value.data = [...pagedItems.value.data, ...newItems];
|
154
|
+
return true;
|
271
155
|
}
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
function openItemForm(item, tab) {
|
283
|
-
activeItem.value = item;
|
284
|
-
formTab.value = tab;
|
285
|
-
}
|
286
|
-
|
287
|
-
/**
|
288
|
-
* Gets the next item in the list at the given offset (ie: 1 or -1) from the current position in the list of the
|
289
|
-
* selected item. If the next item is on a previous or next page, it will load the page first then select the item
|
290
|
-
* @param offset
|
291
|
-
* @returns {Promise<void>}
|
292
|
-
*/
|
293
|
-
async function getNextItem(offset) {
|
294
|
-
const index = pagedItems.value?.data.findIndex(i => i.id === activeItem.value.id);
|
295
|
-
if (index === undefined) return;
|
296
|
-
let nextIndex = index + offset;
|
297
|
-
|
298
|
-
// Load the previous page if the offset is before index 0
|
299
|
-
if (nextIndex < 0) {
|
300
|
-
if (quasarPagination.value.page > 1) {
|
301
|
-
quasarPagination.value = { ...quasarPagination.value, page: quasarPagination.value.page - 1 };
|
302
|
-
await waitForRef(isLoadingList, false);
|
303
|
-
nextIndex = pagedItems.value.data.length - 1;
|
304
|
-
} else {
|
305
|
-
// There are no more previous pages
|
306
|
-
return;
|
307
|
-
}
|
156
|
+
|
157
|
+
return false;
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Refreshes the list, summary, and filter field options.
|
162
|
+
* @returns {Promise<Awaited<void>[]>}
|
163
|
+
*/
|
164
|
+
async function refreshAll() {
|
165
|
+
return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions()]);
|
308
166
|
}
|
309
167
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
168
|
+
/**
|
169
|
+
* Loads the filter and pagination settings from local storage.
|
170
|
+
*/
|
171
|
+
function loadSettings() {
|
172
|
+
const settings = getItem(PAGE_SETTINGS_KEY);
|
173
|
+
|
174
|
+
// Load the filter settings from local storage
|
175
|
+
if (settings) {
|
176
|
+
filter.value = { ...settings.filter, ...filter.value };
|
177
|
+
quasarPagination.value = settings.quasarPagination;
|
178
|
+
} else {
|
179
|
+
// If no local storage settings, apply the default filters
|
180
|
+
filter.value = { ...filterDefaults, ...filter.value };
|
181
|
+
}
|
182
|
+
|
183
|
+
// Load the URL filters if they are set
|
184
|
+
applyFilterFromUrl(window.location.href);
|
185
|
+
|
186
|
+
setTimeout(() => {
|
187
|
+
if (!isLoadingList.value) {
|
188
|
+
loadList();
|
189
|
+
}
|
190
|
+
|
191
|
+
if (!isLoadingSummary.value) {
|
192
|
+
loadSummary();
|
193
|
+
}
|
194
|
+
|
195
|
+
if (!isLoadingFilters.value) {
|
196
|
+
loadFilterFieldOptions();
|
197
|
+
}
|
198
|
+
}, 1);
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Saves the current filter and pagination settings to local storage.
|
203
|
+
*/
|
204
|
+
async function saveSettings() {
|
205
|
+
const settings = {
|
206
|
+
filter: filter.value,
|
207
|
+
quasarPagination: { ...quasarPagination.value, page: 1 }
|
208
|
+
};
|
209
|
+
// save in local storage
|
210
|
+
setItem(PAGE_SETTINGS_KEY, settings);
|
320
211
|
}
|
321
212
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
213
|
+
/**
|
214
|
+
* Applies an action to an item.
|
215
|
+
*/
|
216
|
+
const isApplyingActionToItem = ref(null);
|
217
|
+
let actionResultCount = 0;
|
218
|
+
|
219
|
+
async function applyAction(item, input, itemData = {}) {
|
220
|
+
isApplyingActionToItem.value = item;
|
221
|
+
const resultNumber = ++actionResultCount;
|
222
|
+
setItemInPagedList({ ...item, ...input, ...itemData });
|
223
|
+
const result = await applyActionRoute(item, input);
|
224
|
+
if (result.success) {
|
225
|
+
// Only render the most recent campaign changes
|
226
|
+
if (resultNumber !== actionResultCount) return;
|
227
|
+
|
228
|
+
// Update the updated item in the previously loaded list if it exists
|
229
|
+
setItemInPagedList(result.item);
|
230
|
+
|
231
|
+
// Update the active item if it is the same as the updated item
|
232
|
+
if (activeItem.value?.id === result.item.id) {
|
233
|
+
activeItem.value = { ...activeItem.value, ...result.item };
|
234
|
+
}
|
235
|
+
}
|
236
|
+
isApplyingActionToItem.value = null;
|
237
|
+
return result;
|
238
|
+
}
|
239
|
+
|
240
|
+
/**
|
241
|
+
* Applies an action to all selected items.
|
242
|
+
*/
|
243
|
+
const isApplyingBatchAction = ref(false);
|
244
|
+
|
245
|
+
async function applyBatchAction(input) {
|
246
|
+
isApplyingBatchAction.value = true;
|
247
|
+
const batchFilter = { id: selectedRows.value.map(r => r.id) };
|
248
|
+
const result = await applyBatchActionRoute(batchFilter, input);
|
249
|
+
isApplyingBatchAction.value = false;
|
250
|
+
await refreshAll();
|
251
|
+
|
252
|
+
return result;
|
253
|
+
}
|
254
|
+
|
255
|
+
// The active ad for viewing / editing in the Ad Form
|
256
|
+
const activeItem = ref(null);
|
257
|
+
// Controls the tab on the Ad Form
|
258
|
+
const formTab = ref("general");
|
259
|
+
|
260
|
+
// Whenever the active item changes, fill the additional item details
|
261
|
+
// (ie: tasks, verifications, creatives, etc.)
|
262
|
+
if (itemDetailsRoute) {
|
263
|
+
watch(() => activeItem.value, async (newItem, oldItem) => {
|
264
|
+
if (newItem && oldItem?.id !== newItem.id) {
|
265
|
+
const result = await itemDetailsRoute(newItem);
|
266
|
+
|
267
|
+
// Only set the ad details if we are the response for the currently loaded item
|
268
|
+
// NOTE: race conditions might allow the finished loading item to be different to the currently
|
269
|
+
// requested item
|
270
|
+
if (result?.id === activeItem.value?.id) {
|
271
|
+
activeItem.value = result;
|
272
|
+
}
|
273
|
+
}
|
274
|
+
});
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Opens the item's form with the given item and tab
|
279
|
+
*
|
280
|
+
* @param item
|
281
|
+
* @param tab
|
282
|
+
*/
|
283
|
+
function openItemForm(item, tab) {
|
284
|
+
activeItem.value = item;
|
285
|
+
formTab.value = tab;
|
286
|
+
}
|
287
|
+
|
288
|
+
/**
|
289
|
+
* Gets the next item in the list at the given offset (ie: 1 or -1) from the current position in the list of the
|
290
|
+
* selected item. If the next item is on a previous or next page, it will load the page first then select the item
|
291
|
+
* @param offset
|
292
|
+
* @returns {Promise<void>}
|
293
|
+
*/
|
294
|
+
async function getNextItem(offset) {
|
295
|
+
const index = pagedItems.value?.data.findIndex(i => i.id === activeItem.value.id);
|
296
|
+
if (index === undefined) return;
|
297
|
+
let nextIndex = index + offset;
|
298
|
+
|
299
|
+
// Load the previous page if the offset is before index 0
|
300
|
+
if (nextIndex < 0) {
|
301
|
+
if (quasarPagination.value.page > 1) {
|
302
|
+
quasarPagination.value = { ...quasarPagination.value, page: quasarPagination.value.page - 1 };
|
303
|
+
await waitForRef(isLoadingList, false);
|
304
|
+
nextIndex = pagedItems.value.data.length - 1;
|
305
|
+
} else {
|
306
|
+
// There are no more previous pages
|
307
|
+
return;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
// Load the next page if the offset is past the last index
|
312
|
+
if (nextIndex >= pagedItems.value.data.length) {
|
313
|
+
if (quasarPagination.value.page < pagedItems.value.meta.last_page) {
|
314
|
+
quasarPagination.value = { ...quasarPagination.value, page: quasarPagination.value.page + 1 };
|
315
|
+
await waitForRef(isLoadingList, false);
|
316
|
+
nextIndex = 0;
|
317
|
+
} else {
|
318
|
+
// There are no more next pages
|
319
|
+
return;
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
activeItem.value = pagedItems.value.data[nextIndex];
|
324
|
+
}
|
325
|
+
|
326
|
+
// Async load the settings for this Action List
|
327
|
+
setTimeout(loadSettings, 1);
|
328
|
+
|
329
|
+
return {
|
330
|
+
// State
|
331
|
+
pagedItems,
|
332
|
+
filter,
|
333
|
+
filterActiveCount,
|
334
|
+
showFilters,
|
335
|
+
summary,
|
336
|
+
filterFieldOptions,
|
337
|
+
selectedRows,
|
338
|
+
isLoadingList,
|
339
|
+
isLoadingFilters,
|
340
|
+
isLoadingSummary,
|
341
|
+
pager,
|
342
|
+
quasarPagination,
|
343
|
+
isApplyingActionToItem,
|
344
|
+
isApplyingBatchAction,
|
345
|
+
activeItem,
|
346
|
+
formTab,
|
347
|
+
columns,
|
348
|
+
filterGroups,
|
349
|
+
|
350
|
+
// Actions
|
351
|
+
loadSummary,
|
352
|
+
resetPaging,
|
353
|
+
loadList,
|
354
|
+
loadMore,
|
355
|
+
refreshAll,
|
356
|
+
applyAction,
|
357
|
+
applyBatchAction,
|
358
|
+
getNextItem,
|
359
|
+
openItemForm,
|
360
|
+
applyFilterFromUrl
|
361
|
+
};
|
361
362
|
}
|