quasar-ui-danx 0.0.11 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
}
|