quasar-ui-danx 0.0.28 → 0.0.30
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 +1 -1
- package/src/components/ActionTable/ActionMenu.vue +6 -60
- package/src/components/ActionTable/Layouts/ActionTableLayout.vue +7 -2
- package/src/components/ActionTable/listActions.ts +22 -11
- package/src/components/ActionTable/listHelpers.ts +2 -19
- package/src/components/Utility/Popovers/PopoverMenu.vue +3 -3
- package/src/components/Utility/Tools/ActionPerformerTool.vue +43 -8
- package/src/helpers/performAction.ts +51 -2
- package/src/helpers/utils.ts +34 -15
package/package.json
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
/>
|
8
|
-
</div>
|
2
|
+
<PopoverMenu
|
3
|
+
class="px-4 h-full flex"
|
4
|
+
:items="items"
|
5
|
+
@action-item="onAction"
|
6
|
+
/>
|
9
7
|
</template>
|
10
8
|
<script setup>
|
11
|
-
import {
|
12
|
-
import { FlashMessages, performAction } from '../../helpers';
|
9
|
+
import { performAction } from '../../helpers';
|
13
10
|
import { PopoverMenu } from '../Utility';
|
14
11
|
|
15
12
|
const emit = defineEmits(['action']);
|
@@ -25,59 +22,8 @@ const props = defineProps({
|
|
25
22
|
});
|
26
23
|
|
27
24
|
|
28
|
-
const activeAction = shallowRef(null);
|
29
|
-
const confirmDialog = shallowRef(null);
|
30
|
-
const isSaving = ref(false);
|
31
|
-
|
32
25
|
function onAction(item) {
|
33
26
|
emit('action', item);
|
34
|
-
|
35
27
|
performAction(item, props.rows);
|
36
28
|
}
|
37
|
-
|
38
|
-
function onCancel() {
|
39
|
-
activeAction.value = null;
|
40
|
-
confirmDialog.value = null;
|
41
|
-
}
|
42
|
-
|
43
|
-
async function onConfirmAction(input) {
|
44
|
-
if (!activeAction.value.onAction) {
|
45
|
-
throw new Error('No onAction handler found for the selected action:' + activeAction.value.action);
|
46
|
-
}
|
47
|
-
|
48
|
-
isSaving.value = true;
|
49
|
-
const result = await activeAction.value.onAction(input, props.rows);
|
50
|
-
isSaving.value = false;
|
51
|
-
|
52
|
-
if (!result.success) {
|
53
|
-
const errors = [];
|
54
|
-
if (result.errors) {
|
55
|
-
errors.push(...result.errors);
|
56
|
-
} else if (result.error) {
|
57
|
-
errors.push(result.error.message);
|
58
|
-
} else {
|
59
|
-
errors.push('An unknown error occurred. Please try again later.');
|
60
|
-
}
|
61
|
-
|
62
|
-
FlashMessages.combine('error', errors);
|
63
|
-
|
64
|
-
if (activeAction.value.onError) {
|
65
|
-
await activeAction.value.onError(result, input);
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
FlashMessages.success(`The update was successful`);
|
70
|
-
|
71
|
-
if (activeAction.value.onSuccess) {
|
72
|
-
await activeAction.value.onSuccess(result, input);
|
73
|
-
}
|
74
|
-
|
75
|
-
if (activeAction.value.onFinish) {
|
76
|
-
await activeAction.value.onFinish();
|
77
|
-
}
|
78
|
-
|
79
|
-
confirmDialog.value = null;
|
80
|
-
activeAction.value = null;
|
81
|
-
}
|
82
|
-
|
83
29
|
</script>
|
@@ -6,10 +6,15 @@
|
|
6
6
|
<slot name="filters" />
|
7
7
|
<slot />
|
8
8
|
</div>
|
9
|
-
<ActionPerformerTool
|
9
|
+
<ActionPerformerTool
|
10
|
+
v-if="activeAction"
|
11
|
+
:targets="actionTargets"
|
12
|
+
:action="activeAction"
|
13
|
+
@done="clearAction"
|
14
|
+
/>
|
10
15
|
</div>
|
11
16
|
</template>
|
12
17
|
<script setup>
|
13
|
-
import { actionTargets, activeAction } from '../../../helpers';
|
18
|
+
import { actionTargets, activeAction, clearAction } from '../../../helpers';
|
14
19
|
import { ActionPerformerTool } from '../../Utility';
|
15
20
|
</script>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { computed, ref, watch } from "vue";
|
2
|
-
import { getItem, setItem } from "../../helpers";
|
3
|
-
import { getFilterFromUrl
|
2
|
+
import { getItem, setItem, waitForRef } from "../../helpers";
|
3
|
+
import { getFilterFromUrl } from "./listHelpers";
|
4
4
|
|
5
5
|
export function useListActions(name, {
|
6
6
|
listRoute,
|
@@ -165,7 +165,7 @@ export function useListActions(name, {
|
|
165
165
|
* @returns {Promise<Awaited<void>[]>}
|
166
166
|
*/
|
167
167
|
async function refreshAll() {
|
168
|
-
return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions()]);
|
168
|
+
return Promise.all([loadList(), loadSummary(), loadFilterFieldOptions(), getActiveItemDetails()]);
|
169
169
|
}
|
170
170
|
|
171
171
|
/**
|
@@ -260,19 +260,30 @@ export function useListActions(name, {
|
|
260
260
|
// Controls the tab on the Ad Form
|
261
261
|
const formTab = ref("general");
|
262
262
|
|
263
|
+
/**
|
264
|
+
* Gets the additional details for the currently active item.
|
265
|
+
* (ie: data that is not normally loaded in the list because it is not needed for the list view)
|
266
|
+
* @returns {Promise<void>}
|
267
|
+
*/
|
268
|
+
async function getActiveItemDetails() {
|
269
|
+
if (!activeItem.value) return;
|
270
|
+
|
271
|
+
const result = await itemDetailsRoute(activeItem.value);
|
272
|
+
|
273
|
+
// Only set the ad details if we are the response for the currently loaded item
|
274
|
+
// NOTE: race conditions might allow the finished loading item to be different to the currently
|
275
|
+
// requested item
|
276
|
+
if (result?.id === activeItem.value?.id) {
|
277
|
+
activeItem.value = result;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
263
281
|
// Whenever the active item changes, fill the additional item details
|
264
282
|
// (ie: tasks, verifications, creatives, etc.)
|
265
283
|
if (itemDetailsRoute) {
|
266
284
|
watch(() => activeItem.value, async (newItem, oldItem) => {
|
267
285
|
if (newItem && oldItem?.id !== newItem.id) {
|
268
|
-
|
269
|
-
|
270
|
-
// Only set the ad details if we are the response for the currently loaded item
|
271
|
-
// NOTE: race conditions might allow the finished loading item to be different to the currently
|
272
|
-
// requested item
|
273
|
-
if (result?.id === activeItem.value?.id) {
|
274
|
-
activeItem.value = result;
|
275
|
-
}
|
286
|
+
await getActiveItemDetails();
|
276
287
|
}
|
277
288
|
});
|
278
289
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { onMounted
|
1
|
+
import { onMounted } from "vue";
|
2
2
|
import { getUrlParam } from "../../helpers";
|
3
3
|
|
4
4
|
export function registerStickyScrolling(tableRef) {
|
@@ -37,29 +37,12 @@ export function mapSortBy(pagination, columns) {
|
|
37
37
|
];
|
38
38
|
}
|
39
39
|
|
40
|
-
/**
|
41
|
-
* Wait for a ref to have a value and then resolve the promise
|
42
|
-
*
|
43
|
-
* @param ref
|
44
|
-
* @param value
|
45
|
-
* @returns {Promise<void>}
|
46
|
-
*/
|
47
|
-
export function waitForRef(ref, value) {
|
48
|
-
return new Promise<void>((resolve) => {
|
49
|
-
watch(ref, (newValue) => {
|
50
|
-
if (newValue === value) {
|
51
|
-
resolve();
|
52
|
-
}
|
53
|
-
});
|
54
|
-
});
|
55
|
-
}
|
56
|
-
|
57
40
|
/**
|
58
41
|
* Returns the filter from the URL if it is set
|
59
42
|
* @param url
|
60
43
|
* @param allowedKeys
|
61
44
|
*/
|
62
|
-
export function getFilterFromUrl(url, allowedKeys = null) {
|
45
|
+
export function getFilterFromUrl(url: string, allowedKeys = null) {
|
63
46
|
const filter = {};
|
64
47
|
const urlFilter = getUrlParam("filter", url);
|
65
48
|
if (urlFilter) {
|
@@ -34,7 +34,7 @@
|
|
34
34
|
</a>
|
35
35
|
<q-item
|
36
36
|
v-else
|
37
|
-
:key="item.action"
|
37
|
+
:key="item.name || item.action"
|
38
38
|
clickable
|
39
39
|
:class="item.class"
|
40
40
|
@click="onAction(item)"
|
@@ -55,7 +55,7 @@ defineProps({
|
|
55
55
|
type: Array,
|
56
56
|
required: true,
|
57
57
|
validator(items) {
|
58
|
-
return items.every((item) => item.label && (item.url || item.action));
|
58
|
+
return items.every((item) => item.label && (item.url || item.action || item.name));
|
59
59
|
}
|
60
60
|
},
|
61
61
|
disabled: Boolean,
|
@@ -63,7 +63,7 @@ defineProps({
|
|
63
63
|
});
|
64
64
|
|
65
65
|
function onAction(item) {
|
66
|
-
emit('action', item.action);
|
66
|
+
emit('action', item.name || item.action);
|
67
67
|
emit('action-item', item);
|
68
68
|
}
|
69
69
|
</script>
|
@@ -1,11 +1,20 @@
|
|
1
1
|
<template>
|
2
2
|
<div>
|
3
|
-
<Component
|
3
|
+
<Component
|
4
|
+
v-if="confirmDialog"
|
5
|
+
:is="confirmDialog.is"
|
6
|
+
v-bind="confirmDialog.props"
|
7
|
+
:is-saving="isSaving"
|
8
|
+
@confirm="onConfirmAction"
|
9
|
+
@close="$emit('done')"
|
10
|
+
/>
|
4
11
|
</div>
|
5
12
|
</template>
|
6
13
|
<script setup>
|
7
14
|
import { onMounted, ref, shallowRef } from 'vue';
|
15
|
+
import { FlashMessages } from '../../../helpers';
|
8
16
|
|
17
|
+
const emit = defineEmits(['done']);
|
9
18
|
const props = defineProps({
|
10
19
|
action: {
|
11
20
|
type: Object,
|
@@ -21,22 +30,48 @@ const confirmDialog = shallowRef(props.action.confirmDialog ? props.action.confi
|
|
21
30
|
const isSaving = ref(null);
|
22
31
|
|
23
32
|
onMounted(async () => {
|
24
|
-
console.log('mounting action', props.action, props.targets);
|
25
33
|
// If there is no dialog, we auto-confirm the action
|
26
34
|
if (!confirmDialog.value) {
|
27
|
-
onConfirmAction();
|
35
|
+
await onConfirmAction();
|
28
36
|
}
|
29
37
|
});
|
30
38
|
|
31
|
-
function onConfirmAction(input) {
|
32
|
-
console.log('action confirmed', input);
|
39
|
+
async function onConfirmAction(input) {
|
33
40
|
if (!props.action.onAction) {
|
34
41
|
throw new Error('No onAction handler found for the selected action:' + props.action.name);
|
35
42
|
}
|
36
43
|
|
37
44
|
isSaving.value = true;
|
38
|
-
props.action.onAction(
|
39
|
-
|
40
|
-
|
45
|
+
const result = await props.action.onAction(props.targets, input);
|
46
|
+
isSaving.value = false;
|
47
|
+
|
48
|
+
if (!result.success) {
|
49
|
+
const errors = [];
|
50
|
+
if (result.errors) {
|
51
|
+
errors.push(...result.errors);
|
52
|
+
} else if (result.error) {
|
53
|
+
errors.push(result.error.message);
|
54
|
+
} else {
|
55
|
+
errors.push('An unknown error occurred. Please try again later.');
|
56
|
+
}
|
57
|
+
|
58
|
+
FlashMessages.combine('error', errors);
|
59
|
+
|
60
|
+
if (props.action.onError) {
|
61
|
+
await props.action.onError(result, props.targets, input);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
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
|
+
if (props.action.onFinish) {
|
72
|
+
await props.action.onFinish(result, props.targets, input);
|
73
|
+
}
|
74
|
+
|
75
|
+
emit('done');
|
41
76
|
}
|
42
77
|
</script>
|
@@ -1,10 +1,59 @@
|
|
1
1
|
import { ref } from "vue";
|
2
|
+
import { waitForRef } from "./index";
|
2
3
|
|
3
4
|
export const activeAction = ref(null);
|
4
5
|
export const actionTargets = ref([]);
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
/**
|
8
|
+
* Hook to perform an action on a set of targets
|
9
|
+
* This helper allows you to perform actions by name on a set of targets using a provided list of actions
|
10
|
+
*
|
11
|
+
* @param actions
|
12
|
+
* @returns {{performAction(name, targets): Promise<void>}}
|
13
|
+
*/
|
14
|
+
export function usePerformAction(actions: any[]) {
|
15
|
+
return {
|
16
|
+
/**
|
17
|
+
* Perform an action on a set of targets
|
18
|
+
*
|
19
|
+
* @param name - can either be a string or an action object
|
20
|
+
* @param targets - an array of targets (or a single target object)
|
21
|
+
* @param options
|
22
|
+
* @returns {Promise<void>}
|
23
|
+
*/
|
24
|
+
async performAction(name, targets, options = {}) {
|
25
|
+
const action = typeof name === "string" ? actions.find(a => a.name === name) : name;
|
26
|
+
if (!action) {
|
27
|
+
throw new Error(`Unknown action: ${name}`);
|
28
|
+
}
|
29
|
+
targets = Array.isArray(targets) ? targets : [targets];
|
30
|
+
|
31
|
+
await performAction({ ...action, ...options }, targets);
|
32
|
+
}
|
33
|
+
};
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Perform an action on a set of targets
|
38
|
+
*
|
39
|
+
* NOTE: This function and variables should be used w/ the ActionPerformerTool - make sure to use a Layout that has
|
40
|
+
* rendered this component so the actions will be performed
|
41
|
+
*
|
42
|
+
* @param action
|
43
|
+
* @param targets
|
44
|
+
* @returns {Promise<void>}
|
45
|
+
*/
|
46
|
+
export async function performAction(action: any, targets: any[]): Promise<void> {
|
8
47
|
activeAction.value = action;
|
9
48
|
actionTargets.value = targets;
|
49
|
+
await waitForRef(activeAction, null);
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Clear the active action and targets - (note: this will tear down any dialogs / rendered components) triggered by the
|
54
|
+
* ActionPerformerTool
|
55
|
+
*/
|
56
|
+
export function clearAction() {
|
57
|
+
activeAction.value = null;
|
58
|
+
actionTargets.value = [];
|
10
59
|
}
|
package/src/helpers/utils.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
import { watch } from "vue";
|
2
|
+
|
1
3
|
/**
|
2
4
|
* Sleep function to be used in conjuction with async await:
|
3
5
|
*
|
@@ -7,7 +9,24 @@
|
|
7
9
|
* @returns {Promise<any>}
|
8
10
|
*/
|
9
11
|
export function sleep(delay) {
|
10
|
-
|
12
|
+
return new Promise((resolve) => setTimeout(resolve, delay));
|
13
|
+
}
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Wait for a ref to have a value and then resolve the promise
|
17
|
+
*
|
18
|
+
* @param ref
|
19
|
+
* @param value
|
20
|
+
* @returns {Promise<void>}
|
21
|
+
*/
|
22
|
+
export function waitForRef(ref, value) {
|
23
|
+
return new Promise<void>((resolve) => {
|
24
|
+
watch(ref, (newValue) => {
|
25
|
+
if (newValue === value) {
|
26
|
+
resolve();
|
27
|
+
}
|
28
|
+
});
|
29
|
+
});
|
11
30
|
}
|
12
31
|
|
13
32
|
/**
|
@@ -18,7 +37,7 @@ export function sleep(delay) {
|
|
18
37
|
* @returns {number}
|
19
38
|
*/
|
20
39
|
export function minmax(min, max, value) {
|
21
|
-
|
40
|
+
return Math.max(min, Math.min(max, value));
|
22
41
|
}
|
23
42
|
|
24
43
|
/**
|
@@ -27,7 +46,7 @@ export function minmax(min, max, value) {
|
|
27
46
|
* @returns {number}
|
28
47
|
*/
|
29
48
|
export function metersToMiles(meters) {
|
30
|
-
|
49
|
+
return meters * 0.000621371;
|
31
50
|
}
|
32
51
|
|
33
52
|
/**
|
@@ -36,7 +55,7 @@ export function metersToMiles(meters) {
|
|
36
55
|
* @returns {number}
|
37
56
|
*/
|
38
57
|
export function milesToMeters(miles) {
|
39
|
-
|
58
|
+
return miles / 0.000621371;
|
40
59
|
}
|
41
60
|
|
42
61
|
/**
|
@@ -45,19 +64,19 @@ export function milesToMeters(miles) {
|
|
45
64
|
* @returns {null|{lng: number, lat: number}}
|
46
65
|
*/
|
47
66
|
export function parseCoords(location) {
|
48
|
-
|
67
|
+
const latLong = location.split(",");
|
49
68
|
|
50
|
-
|
51
|
-
|
52
|
-
|
69
|
+
if (latLong.length === 2) {
|
70
|
+
const lat = parseFloat(latLong[0].trim());
|
71
|
+
const lng = parseFloat(latLong[1].trim());
|
53
72
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
73
|
+
if (!isNaN(lat) && !isNaN(lng)) {
|
74
|
+
return {
|
75
|
+
lat,
|
76
|
+
lng,
|
77
|
+
};
|
78
|
+
}
|
59
79
|
}
|
60
|
-
}
|
61
80
|
|
62
|
-
|
81
|
+
return null;
|
63
82
|
}
|