quasar-ui-danx 0.0.31 → 0.0.33
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +1 -1
- package/src/components/ActionTable/ActionMenu.vue +10 -3
- package/src/components/ActionTable/ActionTable.vue +7 -6
- package/src/components/ActionTable/index.ts +0 -1
- package/src/components/ActionTable/listActions.ts +34 -38
- package/src/components/Utility/Popovers/PopoverMenu.vue +1 -0
- package/src/components/Utility/Tools/ActionPerformerTool.vue +9 -9
- package/src/helpers/http.ts +34 -34
- package/src/helpers/performAction.ts +13 -1
- package/src/styles/actions.scss +10 -0
- package/src/components/ActionTable/BatchActionMenu.vue +0 -60
package/package.json
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
<template>
|
2
2
|
<PopoverMenu
|
3
|
-
class="px-
|
3
|
+
class="px-2 flex action-button"
|
4
4
|
:items="activeItems"
|
5
|
+
:disabled="targets.length === 0"
|
5
6
|
@action-item="onAction"
|
6
|
-
|
7
|
+
>
|
8
|
+
<q-tooltip v-if="targets.length === 0">{{ tooltip }}</q-tooltip>
|
9
|
+
</PopoverMenu>
|
7
10
|
</template>
|
8
11
|
<script setup>
|
9
12
|
import { computed } from 'vue';
|
@@ -19,12 +22,16 @@ const props = defineProps({
|
|
19
22
|
targets: {
|
20
23
|
type: Array,
|
21
24
|
required: true
|
25
|
+
},
|
26
|
+
tooltip: {
|
27
|
+
type: String,
|
28
|
+
default: 'First select records to perform a batch action'
|
22
29
|
}
|
23
30
|
});
|
24
31
|
|
25
32
|
const activeItems = computed(() => props.items.filter(item => {
|
26
33
|
if (item.enabled === undefined) return true;
|
27
|
-
return typeof item.enabled === 'function' ? !!item.enabled(props.targets) : !!item.enabled;
|
34
|
+
return typeof item.enabled === 'function' ? !!item.enabled(props.targets?.[0] ?? null, props.targets) : !!item.enabled;
|
28
35
|
}));
|
29
36
|
|
30
37
|
function onAction(item) {
|
@@ -70,12 +70,13 @@
|
|
70
70
|
{{ rowProps.value }}
|
71
71
|
</slot>
|
72
72
|
</div>
|
73
|
-
<
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
<div v-if="rowProps.col.actions" class="flex-grow flex justify-end pl-2">
|
74
|
+
<ActionMenu
|
75
|
+
:items="rowProps.col.actions"
|
76
|
+
:targets="[rowProps.row]"
|
77
|
+
@action="(action) => $emit('action', {action: action, row: rowProps.row})"
|
78
|
+
/>
|
79
|
+
</div>
|
79
80
|
</component>
|
80
81
|
</q-td>
|
81
82
|
</template>
|
@@ -6,7 +6,6 @@ export * from "./listHelpers";
|
|
6
6
|
export * from "./tableColumns";
|
7
7
|
export { default as ActionMenu } from "./ActionMenu.vue";
|
8
8
|
export { default as ActionTable } from "./ActionTable.vue";
|
9
|
-
export { default as BatchActionMenu } from "./BatchActionMenu.vue";
|
10
9
|
export { default as EmptyTableState } from "./EmptyTableState.vue";
|
11
10
|
export { default as RenderComponent } from "./RenderComponent.vue";
|
12
11
|
export { default as TableSummaryRow } from "./TableSummaryRow.vue";
|
@@ -2,18 +2,30 @@ import { computed, ref, watch } from "vue";
|
|
2
2
|
import { getItem, setItem, waitForRef } from "../../helpers";
|
3
3
|
import { getFilterFromUrl } from "./listHelpers";
|
4
4
|
|
5
|
-
|
5
|
+
interface ListActionsOptions {
|
6
|
+
listRoute: Function;
|
7
|
+
summaryRoute?: Function;
|
8
|
+
filterFieldOptionsRoute?: Function;
|
9
|
+
moreRoute?: Function;
|
10
|
+
applyActionRoute?: Function;
|
11
|
+
itemDetailsRoute?: Function;
|
12
|
+
urlPattern?: RegExp;
|
13
|
+
filterDefaults?: Record<string, any>;
|
14
|
+
refreshFilters?: boolean;
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
export function useListActions(name: string, {
|
6
19
|
listRoute,
|
7
|
-
filterFieldOptionsRoute,
|
8
20
|
summaryRoute = null,
|
21
|
+
filterFieldOptionsRoute = null,
|
9
22
|
moreRoute = null,
|
10
23
|
applyActionRoute = null,
|
11
|
-
applyBatchActionRoute = null,
|
12
24
|
itemDetailsRoute = null,
|
13
25
|
refreshFilters = false,
|
14
26
|
urlPattern = null,
|
15
27
|
filterDefaults = {}
|
16
|
-
}) {
|
28
|
+
}: ListActionsOptions) {
|
17
29
|
let isInitialized = false;
|
18
30
|
const PAGE_SETTINGS_KEY = `${name}-pagination-settings`;
|
19
31
|
const pagedItems = ref(null);
|
@@ -60,23 +72,22 @@ export function useListActions(name, {
|
|
60
72
|
}
|
61
73
|
|
62
74
|
async function loadList() {
|
63
|
-
if (isInitialized)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
}
|
75
|
+
if (!isInitialized) return;
|
76
|
+
isLoadingList.value = true;
|
77
|
+
setPagedItems(await listRoute(pager.value));
|
78
|
+
isLoadingList.value = false;
|
68
79
|
}
|
69
80
|
|
70
81
|
async function loadSummary() {
|
71
|
-
if (summaryRoute
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
summary.value = await summaryRoute(summaryFilter);
|
78
|
-
isLoadingSummary.value = false;
|
82
|
+
if (!summaryRoute || !isInitialized) return;
|
83
|
+
|
84
|
+
isLoadingSummary.value = true;
|
85
|
+
const summaryFilter = { id: null, ...filter.value, ...globalFilter.value };
|
86
|
+
if (selectedRows.value.length) {
|
87
|
+
summaryFilter.id = selectedRows.value.map((row) => row.id);
|
79
88
|
}
|
89
|
+
summary.value = await summaryRoute(summaryFilter);
|
90
|
+
isLoadingSummary.value = false;
|
80
91
|
}
|
81
92
|
|
82
93
|
// Filter fields are the field values available for the currently applied filter on Creative Groups
|
@@ -89,6 +100,7 @@ export function useListActions(name, {
|
|
89
100
|
});
|
90
101
|
|
91
102
|
async function loadFilterFieldOptions() {
|
103
|
+
if (!filterFieldOptionsRoute || !isInitialized) return;
|
92
104
|
isLoadingFilters.value = true;
|
93
105
|
filterFieldOptions.value = await filterFieldOptionsRoute(filter.value);
|
94
106
|
isLoadingFilters.value = false;
|
@@ -240,21 +252,6 @@ export function useListActions(name, {
|
|
240
252
|
return result;
|
241
253
|
}
|
242
254
|
|
243
|
-
/**
|
244
|
-
* Applies an action to all selected items.
|
245
|
-
*/
|
246
|
-
const isApplyingBatchAction = ref(false);
|
247
|
-
|
248
|
-
async function applyBatchAction(input) {
|
249
|
-
isApplyingBatchAction.value = true;
|
250
|
-
const batchFilter = { id: selectedRows.value.map(r => r.id) };
|
251
|
-
const result = await applyBatchActionRoute(batchFilter, input);
|
252
|
-
isApplyingBatchAction.value = false;
|
253
|
-
await refreshAll();
|
254
|
-
|
255
|
-
return result;
|
256
|
-
}
|
257
|
-
|
258
255
|
// The active ad for viewing / editing in the Ad Form
|
259
256
|
const activeItem = ref(null);
|
260
257
|
// Controls the tab on the Ad Form
|
@@ -267,7 +264,7 @@ export function useListActions(name, {
|
|
267
264
|
*/
|
268
265
|
async function getActiveItemDetails() {
|
269
266
|
if (!activeItem.value) return;
|
270
|
-
|
267
|
+
|
271
268
|
const result = await itemDetailsRoute(activeItem.value);
|
272
269
|
|
273
270
|
// Only set the ad details if we are the response for the currently loaded item
|
@@ -337,11 +334,11 @@ export function useListActions(name, {
|
|
337
334
|
activeItem.value = pagedItems.value.data[nextIndex];
|
338
335
|
}
|
339
336
|
|
340
|
-
//
|
341
|
-
|
337
|
+
// Initialize the list actions and load settings, lists, summaries, filter fields, etc.
|
338
|
+
function initialize() {
|
342
339
|
isInitialized = true;
|
343
340
|
loadSettings();
|
344
|
-
}
|
341
|
+
}
|
345
342
|
|
346
343
|
return {
|
347
344
|
// State
|
@@ -359,18 +356,17 @@ export function useListActions(name, {
|
|
359
356
|
pager,
|
360
357
|
quasarPagination,
|
361
358
|
isApplyingActionToItem,
|
362
|
-
isApplyingBatchAction,
|
363
359
|
activeItem,
|
364
360
|
formTab,
|
365
361
|
|
366
362
|
// Actions
|
363
|
+
initialize,
|
367
364
|
loadSummary,
|
368
365
|
resetPaging,
|
369
366
|
loadList,
|
370
367
|
loadMore,
|
371
368
|
refreshAll,
|
372
369
|
applyAction,
|
373
|
-
applyBatchAction,
|
374
370
|
getNextItem,
|
375
371
|
openItemForm,
|
376
372
|
applyFilterFromUrl
|
@@ -45,7 +45,15 @@ async function onConfirmAction(input) {
|
|
45
45
|
const result = await props.action.onAction(props.targets, input);
|
46
46
|
isSaving.value = false;
|
47
47
|
|
48
|
-
if (
|
48
|
+
if (result.success) {
|
49
|
+
FlashMessages.success(`The update was successful`);
|
50
|
+
|
51
|
+
if (props.action.onSuccess) {
|
52
|
+
await props.action.onSuccess(result, props.targets, input);
|
53
|
+
}
|
54
|
+
|
55
|
+
emit('done');
|
56
|
+
} else {
|
49
57
|
const errors = [];
|
50
58
|
if (result.errors) {
|
51
59
|
errors.push(...result.errors);
|
@@ -62,16 +70,8 @@ async function onConfirmAction(input) {
|
|
62
70
|
}
|
63
71
|
}
|
64
72
|
|
65
|
-
FlashMessages.success(`The update was successful`);
|
66
|
-
|
67
|
-
if (props.action.onSuccess) {
|
68
|
-
await props.action.onSuccess(result, props.targets, input);
|
69
|
-
}
|
70
|
-
|
71
73
|
if (props.action.onFinish) {
|
72
74
|
await props.action.onFinish(result, props.targets, input);
|
73
75
|
}
|
74
|
-
|
75
|
-
emit('done');
|
76
76
|
}
|
77
77
|
</script>
|
package/src/helpers/http.ts
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
export const request = {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
async get(url, options = {}) {
|
3
|
+
return fetch(url, {
|
4
|
+
method: "get",
|
5
|
+
headers: {
|
6
|
+
Accept: "application/json",
|
7
|
+
"Content-Type": "application/json"
|
8
|
+
},
|
9
|
+
...options
|
10
|
+
}).then((r) => r.json());
|
11
|
+
},
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
async post(url, data = {}, options = {}) {
|
14
|
+
return fetch(url, {
|
15
|
+
method: "post",
|
16
|
+
body: JSON.stringify(data),
|
17
|
+
headers: {
|
18
|
+
Accept: "application/json",
|
19
|
+
"Content-Type": "application/json"
|
20
|
+
},
|
21
|
+
...options
|
22
|
+
}).then((r) => r.json());
|
23
|
+
}
|
24
24
|
};
|
25
25
|
|
26
26
|
/**
|
@@ -36,19 +36,19 @@ export const request = {
|
|
36
36
|
* @returns {Promise<void>}
|
37
37
|
*/
|
38
38
|
export async function fetchResourceListWithSelected(fetchFn, list, id, filter) {
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
// First make sure we have the selected record, so we can always add it to the list
|
40
|
+
let selectedResource;
|
41
|
+
if (id) {
|
42
|
+
selectedResource = list.value.find((c) => c.id === id) || (await fetchFn({ id }))[0];
|
43
|
+
}
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
// Get the filtered campaign list
|
46
|
+
list.value = await fetchFn(filter);
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
// If our selected campaign is not in the filtered list, add it
|
49
|
+
if (selectedResource && !list.value.find((c) => c.id === id)) {
|
50
|
+
list.value.push(selectedResource);
|
51
|
+
}
|
52
52
|
}
|
53
53
|
|
54
54
|
/**
|
@@ -57,6 +57,6 @@ export async function fetchResourceListWithSelected(fetchFn, list, id, filter) {
|
|
57
57
|
* @param url
|
58
58
|
*/
|
59
59
|
export function getUrlParam(key, url = undefined) {
|
60
|
-
|
61
|
-
|
60
|
+
const params = new URLSearchParams(url?.replace(/.*\?/, "") || window.location.search);
|
61
|
+
return params.get(key);
|
62
62
|
}
|
@@ -12,6 +12,17 @@ export const actionTargets = ref([]);
|
|
12
12
|
* @returns {{performAction(name, targets): Promise<void>}}
|
13
13
|
*/
|
14
14
|
export function usePerformAction(actions: any[]) {
|
15
|
+
|
16
|
+
function filterActions(filters) {
|
17
|
+
let filteredActions = [...actions];
|
18
|
+
|
19
|
+
for (const filter of Object.keys(filters)) {
|
20
|
+
const filterValue = filters[filter];
|
21
|
+
filteredActions = filteredActions.filter(a => a[filter] === filterValue || (Array.isArray(filterValue) && filterValue.includes(a[filter])));
|
22
|
+
}
|
23
|
+
return filteredActions;
|
24
|
+
}
|
25
|
+
|
15
26
|
return {
|
16
27
|
/**
|
17
28
|
* Perform an action on a set of targets
|
@@ -31,7 +42,8 @@ export function usePerformAction(actions: any[]) {
|
|
31
42
|
await performAction({ ...action, ...options }, targets);
|
32
43
|
},
|
33
44
|
|
34
|
-
|
45
|
+
actions,
|
46
|
+
filterActions,
|
35
47
|
};
|
36
48
|
}
|
37
49
|
|
@@ -1,60 +0,0 @@
|
|
1
|
-
<template>
|
2
|
-
<div>
|
3
|
-
<PopoverMenu
|
4
|
-
class="bg-neutral-plus-6 px-4 h-full flex"
|
5
|
-
:items="items"
|
6
|
-
:disabled="selectedRows.length === 0"
|
7
|
-
@action="onAction"
|
8
|
-
/>
|
9
|
-
<q-tooltip v-if="selectedRows.length === 0">
|
10
|
-
Batch actions require a selection
|
11
|
-
</q-tooltip>
|
12
|
-
<slot>
|
13
|
-
<Component
|
14
|
-
:is="activeComponent.is"
|
15
|
-
v-if="activeComponent"
|
16
|
-
v-bind="activeComponent.props"
|
17
|
-
:is-saving="isSaving"
|
18
|
-
@close="activeAction = false"
|
19
|
-
@confirm="onConfirmAction"
|
20
|
-
/>
|
21
|
-
</slot>
|
22
|
-
</div>
|
23
|
-
</template>
|
24
|
-
<script setup>
|
25
|
-
import { computed, ref } from 'vue';
|
26
|
-
import { PopoverMenu } from '../Utility';
|
27
|
-
|
28
|
-
const emit = defineEmits(['action']);
|
29
|
-
const props = defineProps({
|
30
|
-
items: {
|
31
|
-
type: Array,
|
32
|
-
required: true
|
33
|
-
},
|
34
|
-
selectedRows: {
|
35
|
-
type: Array,
|
36
|
-
required: true
|
37
|
-
},
|
38
|
-
applyBatchAction: {
|
39
|
-
type: Function,
|
40
|
-
required: true
|
41
|
-
},
|
42
|
-
isSaving: Boolean
|
43
|
-
});
|
44
|
-
|
45
|
-
|
46
|
-
const activeAction = ref(null);
|
47
|
-
const activeComponent = computed(() => (props.items.find(i => i.action === activeAction.value)?.component || (() => null))(props.selectedRows));
|
48
|
-
|
49
|
-
function onAction(action) {
|
50
|
-
activeAction.value = action;
|
51
|
-
emit('action', action);
|
52
|
-
}
|
53
|
-
async function onConfirmAction(input) {
|
54
|
-
const result = await props.applyBatchAction(input || activeComponent.value.input());
|
55
|
-
|
56
|
-
if (result?.success) {
|
57
|
-
activeAction.value = null;
|
58
|
-
}
|
59
|
-
}
|
60
|
-
</script>
|