quasar-ui-danx 0.4.12 → 0.4.14

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.
Files changed (32) hide show
  1. package/dist/danx.es.js +10975 -6424
  2. package/dist/danx.es.js.map +1 -1
  3. package/dist/danx.umd.js +137 -7
  4. package/dist/danx.umd.js.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +3 -2
  7. package/src/components/ActionTable/ActionTable.vue +65 -63
  8. package/src/components/ActionTable/Form/ActionForm.vue +18 -12
  9. package/src/components/ActionTable/Form/RenderedForm.vue +86 -60
  10. package/src/components/ActionTable/Layouts/ActionTableLayout.vue +80 -70
  11. package/src/components/ActionTable/Toolbars/ActionToolbar.vue +29 -29
  12. package/src/components/ActionTable/listControls.ts +32 -31
  13. package/src/components/Navigation/NavigationMenu.vue +83 -40
  14. package/src/components/PanelsDrawer/PanelsDrawer.vue +20 -8
  15. package/src/components/PanelsDrawer/PanelsDrawerPanels.vue +3 -1
  16. package/src/components/PanelsDrawer/PanelsDrawerTabs.vue +17 -4
  17. package/src/components/Utility/Tools/RenderVnode.vue +5 -1
  18. package/src/components/Utility/Transitions/AutoHeightTransition.vue +21 -0
  19. package/src/components/Utility/Transitions/index.ts +1 -0
  20. package/src/config/index.ts +1 -0
  21. package/src/helpers/actions.ts +31 -20
  22. package/src/helpers/date.ts +2 -2
  23. package/src/helpers/formats.ts +55 -5
  24. package/src/helpers/objectStore.ts +7 -0
  25. package/src/helpers/request.ts +1 -1
  26. package/src/helpers/routes.ts +5 -0
  27. package/src/helpers/utils.ts +3 -3
  28. package/src/types/actions.d.ts +4 -7
  29. package/src/types/config.d.ts +3 -1
  30. package/src/types/controls.d.ts +5 -7
  31. package/src/types/forms.d.ts +20 -1
  32. package/src/types/shared.d.ts +1 -0
@@ -1,5 +1,6 @@
1
1
  import { computed, Ref, ref, shallowRef, watch } from "vue";
2
- import { RouteLocationNormalizedLoaded, RouteParams, Router } from "vue-router";
2
+ import { RouteParams, Router } from "vue-router";
3
+ import { danxOptions } from "../../config";
3
4
  import { getItem, setItem, storeObject, waitForRef } from "../../helpers";
4
5
  import {
5
6
  ActionController,
@@ -7,7 +8,6 @@ import {
7
8
  AnyObject,
8
9
  FilterGroup,
9
10
  ListControlsFilter,
10
- ListControlsInitializeOptions,
11
11
  ListControlsOptions,
12
12
  ListControlsPagination,
13
13
  PagedItems
@@ -16,8 +16,6 @@ import { getFilterFromUrl } from "./listHelpers";
16
16
 
17
17
  export function useListControls(name: string, options: ListControlsOptions): ActionController {
18
18
  let isInitialized = false;
19
- let vueRoute: RouteLocationNormalizedLoaded | null | undefined;
20
- let vueRouter: Router | null | undefined;
21
19
  const PAGE_SETTINGS_KEY = `dx-${name}-pager`;
22
20
  const pagedItems = shallowRef<PagedItems | null>(null);
23
21
  const activeFilter = ref<ListControlsFilter>({});
@@ -112,11 +110,9 @@ export function useListControls(name: string, options: ListControlsOptions): Act
112
110
 
113
111
  /**
114
112
  * Loads the filter field options for the current filter.
115
- *
116
- * @returns {Promise<void>}
117
113
  */
118
114
  async function loadFieldOptions() {
119
- if (!options.routes.fieldOptions || !isInitialized) return;
115
+ if (!options.routes.fieldOptions) return;
120
116
  isLoadingFilters.value = true;
121
117
  try {
122
118
  fieldOptions.value = await options.routes.fieldOptions(activeFilter.value) || {};
@@ -184,9 +180,9 @@ export function useListControls(name: string, options: ListControlsOptions): Act
184
180
  /**
185
181
  * Sets the pagination settings to the given values.
186
182
  */
187
- function setPagination(updated: ListControlsPagination) {
183
+ function setPagination(updated: Partial<ListControlsPagination>) {
188
184
  // @ts-expect-error Seems like a bug in the typescript linting?
189
- pagination.value = updated;
185
+ pagination.value = { ...pagination.value, ...updated };
190
186
  }
191
187
 
192
188
  /**
@@ -250,9 +246,6 @@ export function useListControls(name: string, options: ListControlsOptions): Act
250
246
  * Loads the filter and pagination settings from local storage.
251
247
  */
252
248
  function loadSettings() {
253
- // Only load settings when the class is fully initialized
254
- if (!isInitialized) return;
255
-
256
249
  const settings = getItem(PAGE_SETTINGS_KEY);
257
250
 
258
251
  // Load the filter settings from local storage
@@ -402,26 +395,26 @@ export function useListControls(name: string, options: ListControlsOptions): Act
402
395
  }
403
396
 
404
397
  // Initialize the list actions and load settings, lists, summaries, filter fields, etc.
405
- function initialize(initOptions?: ListControlsInitializeOptions) {
406
- vueRouter = initOptions?.vueRouter;
407
- vueRoute = initOptions?.vueRouter?.currentRoute.value;
398
+ function initialize() {
399
+ const vueRouter = getVueRouter();
408
400
  isInitialized = true;
409
401
  loadSettings();
410
402
 
411
403
  /**
412
404
  * Watch the id params in the route and set the active item to the item with the given id.
413
405
  */
414
- if (options.routes.details && vueRoute && vueRouter) {
415
- const { params, meta } = vueRoute;
416
-
417
- const controlRouteName = vueRoute.name;
418
- vueRouter.afterEach((to) => {
419
- if (to.name === controlRouteName) {
420
- setPanelFromRoute(to.params, to.meta);
421
- }
422
- });
406
+ if (options.routes.details) {
407
+ const { params, meta, name: controlRouteName } = vueRouter.currentRoute.value;
408
+
409
+ if (controlRouteName === name) {
410
+ vueRouter.afterEach((to) => {
411
+ if (to.name === controlRouteName) {
412
+ setPanelFromRoute(to.params, to.meta);
413
+ }
414
+ });
423
415
 
424
- setPanelFromRoute(params, meta);
416
+ setPanelFromRoute(params, meta);
417
+ }
425
418
  }
426
419
  }
427
420
 
@@ -429,12 +422,12 @@ export function useListControls(name: string, options: ListControlsOptions): Act
429
422
  * Updates the URL bar and route to the given params.
430
423
  */
431
424
  function updateRouteParams(params: AnyObject) {
432
- if (vueRouter && vueRoute) {
433
- vueRouter.push({
434
- name: (Array.isArray(vueRoute.name) ? vueRoute.name[0] : vueRoute.name) || "home",
435
- params
436
- });
437
- }
425
+ const vueRouter = getVueRouter();
426
+ const { name: routeName } = vueRouter.currentRoute.value;
427
+ vueRouter.push({
428
+ name: (Array.isArray(routeName) ? routeName[0] : routeName) || "home",
429
+ params
430
+ });
438
431
  }
439
432
 
440
433
  function setPanelFromRoute(params: RouteParams, meta: AnyObject) {
@@ -445,6 +438,13 @@ export function useListControls(name: string, options: ListControlsOptions): Act
445
438
  }
446
439
  }
447
440
 
441
+ function getVueRouter(): Router {
442
+ if (!danxOptions.value.router) {
443
+ throw new Error("Vue Router must be configured in danxOptions");
444
+ }
445
+ return danxOptions.value.router;
446
+ }
447
+
448
448
  return {
449
449
  // State
450
450
  name,
@@ -474,6 +474,7 @@ export function useListControls(name: string, options: ListControlsOptions): Act
474
474
  loadSummary,
475
475
  loadListAndSummary,
476
476
  loadMore,
477
+ loadFieldOptions,
477
478
  getActiveItemDetails,
478
479
  refreshAll,
479
480
  exportList,
@@ -8,10 +8,12 @@
8
8
  :key="'nav-item-' + item.label"
9
9
  class="nav-menu-item-box"
10
10
  >
11
- <div
11
+ <a
12
12
  class="nav-menu-item flex flex-nowrap"
13
+ :href="resolveUrl(item)"
13
14
  :class="item.class || itemClass"
14
- @click="item.onClick"
15
+ :target="item.target || '_self'"
16
+ @click="onClick($event, item)"
15
17
  >
16
18
  <div
17
19
  v-if="item.icon"
@@ -36,7 +38,7 @@
36
38
  >
37
39
  {{ item.tooltip?.text || item.label }}
38
40
  </QTooltip>
39
- </div>
41
+ </a>
40
42
  <QSeparator
41
43
  v-if="item.separator"
42
44
  :key="'separator-' + item.label"
@@ -47,55 +49,96 @@
47
49
  </template>
48
50
  <script setup>
49
51
  import { computed } from "vue";
52
+ import { danxOptions } from "../../config";
50
53
 
51
54
  const props = defineProps({
52
- collapsed: Boolean,
53
- itemClass: {
54
- type: String,
55
- default: "hover:bg-gray-200"
56
- },
57
- activeClass: {
58
- type: String,
59
- default: "bg-blue-200"
60
- },
61
- items: {
62
- type: Array,
63
- required: true
64
- }
55
+ collapsed: Boolean,
56
+ itemClass: {
57
+ type: String,
58
+ default: "hover:bg-gray-200"
59
+ },
60
+ activeClass: {
61
+ type: String,
62
+ default: "bg-blue-200"
63
+ },
64
+ items: {
65
+ type: Array,
66
+ required: true
67
+ }
65
68
  });
66
69
 
70
+ const router = danxOptions.value.router;
67
71
  const allowedItems = computed(() => props.items.filter((item) => !item.hidden));
72
+
73
+ function getItemRoute(item) {
74
+ if (!router) {
75
+ console.error("Router is not available. Configure in danx options.");
76
+ return;
77
+ }
78
+
79
+ return typeof item.route === "function" ? item.route() : item.route;
80
+ }
81
+
82
+ function onClick(e, item) {
83
+ if (!item.url && !e.ctrlKey) {
84
+ e.preventDefault();
85
+ }
86
+
87
+ if (item.disabled || e.ctrlKey) {
88
+ return;
89
+ }
90
+
91
+ if (item.onClick) {
92
+ item.onClick();
93
+ }
94
+
95
+ if (item.route) {
96
+ router.push(getItemRoute(item));
97
+ }
98
+ }
99
+
100
+ function resolveUrl(item) {
101
+ if (item.url) {
102
+ return item.url;
103
+ }
104
+
105
+ if (item.route) {
106
+ return router.resolve(getItemRoute(item))?.href || "#";
107
+ }
108
+
109
+ return "#";
110
+ }
68
111
  </script>
69
112
 
70
113
  <style lang="scss">
71
114
  .nav-menu-item {
72
- padding: 1em;
73
- border-radius: 0.5em;
74
- font-weight: bold;
75
- font-size: 14px;
76
- transition: all 150ms, color 0ms;
77
- cursor: pointer;
78
-
79
- &.is-disabled {
80
- @apply bg-inherit;
81
- }
82
-
83
- .label {
84
- @apply transition-all;
85
- }
86
-
87
- .nav-icon {
88
- @apply w-5 h-5 flex-shrink-0;
89
- }
115
+ padding: 1em;
116
+ border-radius: 0.5em;
117
+ font-weight: bold;
118
+ font-size: 14px;
119
+ transition: all 150ms, color 0ms;
120
+ cursor: pointer;
121
+
122
+ &.is-disabled {
123
+ @apply bg-inherit;
124
+ }
125
+
126
+ .label {
127
+ @apply transition-all;
128
+ }
129
+
130
+ .nav-icon {
131
+ @apply w-5 h-5 flex-shrink-0;
132
+ }
90
133
  }
91
134
 
92
135
  .is-collapsed {
93
- .nav-link {
94
- width: 3.8em;
95
- }
136
+ .nav-link {
137
+ width: 3.8em;
138
+ }
96
139
 
97
- .label {
98
- @apply opacity-0;
99
- }
140
+ .label {
141
+ @apply opacity-0;
142
+ }
100
143
  }
101
144
  </style>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <ContentDrawer
3
- position="right"
4
- :show="true"
3
+ :position="position"
4
+ show
5
5
  overlay
6
6
  content-class="h-full"
7
7
  class="dx-panels-drawer"
@@ -13,7 +13,13 @@
13
13
  <div class="dx-panels-drawer-header flex items-center px-6 py-4">
14
14
  <div class="flex-grow">
15
15
  <slot name="header">
16
- <h2>{{ title }}</h2>
16
+ <h2 v-if="title">
17
+ {{ title }}
18
+ </h2>
19
+ <div v-if="!activeItem">
20
+ Loading
21
+ <QSpinnerHourglass />
22
+ </div>
17
23
  </slot>
18
24
  </div>
19
25
  <div
@@ -32,10 +38,14 @@
32
38
  </div>
33
39
  </div>
34
40
  <div class="dx-panels-drawer-body flex-grow overflow-hidden h-full">
35
- <div class="flex items-stretch flex-nowrap h-full">
41
+ <div
42
+ v-if="activeItem.__timestamp > 0"
43
+ class="flex items-stretch flex-nowrap h-full"
44
+ >
36
45
  <PanelsDrawerTabs
37
46
  :key="'pd-tabs:' + activeItem.id"
38
47
  v-model="activePanel"
48
+ :active-item="activeItem"
39
49
  :class="tabsClass"
40
50
  :panels="panels"
41
51
  @update:model-value="$emit('update:model-value', $event)"
@@ -44,6 +54,7 @@
44
54
  :key="'pd-panels:' + activeItem.id"
45
55
  :panels="panels"
46
56
  :active-panel="activePanel"
57
+ :active-item="activeItem"
47
58
  :class="activePanelOptions?.class || panelsClass"
48
59
  />
49
60
  <div
@@ -68,9 +79,10 @@ import PanelsDrawerTabs from "./PanelsDrawerTabs";
68
79
  export interface Props {
69
80
  title?: string,
70
81
  modelValue?: string | number,
71
- activeItem?: ActionTargetItem;
82
+ activeItem: ActionTargetItem;
72
83
  tabsClass?: string | object,
73
84
  panelsClass?: string | object,
85
+ position?: "standard" | "right" | "left";
74
86
  panels: ActionPanel[]
75
87
  }
76
88
 
@@ -78,9 +90,9 @@ defineEmits(["update:model-value", "close"]);
78
90
  const props = withDefaults(defineProps<Props>(), {
79
91
  title: "",
80
92
  modelValue: null,
81
- activeItem: null,
82
- tabsClass: "w-[13.5rem]",
83
- panelsClass: "w-[35.5rem]"
93
+ tabsClass: "w-[13.5rem] flex-shrink-0",
94
+ panelsClass: "w-[80rem]",
95
+ position: "right"
84
96
  });
85
97
 
86
98
  const activePanel = ref(props.modelValue);
@@ -11,17 +11,19 @@
11
11
  <RenderVnode
12
12
  v-if="panel.vnode"
13
13
  :vnode="panel.vnode"
14
+ :props="activeItem"
14
15
  />
15
16
  </QTabPanel>
16
17
  </QTabPanels>
17
18
  </template>
18
19
 
19
20
  <script setup lang="ts">
20
- import { ActionPanel } from "../../types";
21
+ import { ActionPanel, ActionTargetItem } from "../../types";
21
22
  import { RenderVnode } from "../Utility";
22
23
 
23
24
  defineProps<{
24
25
  activePanel?: string | number,
26
+ activeItem: ActionTargetItem,
25
27
  panels: ActionPanel[]
26
28
  }>();
27
29
  </script>
@@ -9,11 +9,11 @@
9
9
  @update:model-value="$emit('update:model-value', $event)"
10
10
  >
11
11
  <template v-for="panel in panels">
12
- <template v-if="panel.enabled === undefined || !!panel.enabled">
12
+ <template v-if="isEnabled(panel)">
13
13
  <RenderVnode
14
14
  v-if="panel.tabVnode"
15
15
  :key="panel.name"
16
- :vnode="panel.tabVnode(modelValue)"
16
+ :vnode="panel.tabVnode(activeItem, modelValue)"
17
17
  :is-active="modelValue === panel.name"
18
18
  :name="panel.name"
19
19
  :label="panel.label"
@@ -30,19 +30,32 @@
30
30
  </template>
31
31
  <script setup lang="ts">
32
32
  import { QTab } from "quasar";
33
- import { ActionPanel } from "../../types";
33
+ import { ActionPanel, ActionTargetItem } from "../../types";
34
34
  import { RenderVnode } from "../Utility";
35
35
 
36
36
  defineEmits(["update:model-value"]);
37
37
 
38
38
  interface Props {
39
39
  modelValue?: string | number;
40
+ activeItem: ActionTargetItem;
40
41
  panels: ActionPanel[];
41
42
  }
42
43
 
43
- withDefaults(defineProps<Props>(), {
44
+ const props = withDefaults(defineProps<Props>(), {
44
45
  modelValue: "general"
45
46
  });
47
+
48
+ function isEnabled(panel) {
49
+ if (panel.enabled === undefined) return true;
50
+
51
+ if (!panel.enabled) return false;
52
+
53
+ if (typeof panel.enabled === "function") {
54
+ return panel.enabled(props.activeItem);
55
+ }
56
+
57
+ return true;
58
+ }
46
59
  </script>
47
60
 
48
61
  <style lang="scss" module="cls">
@@ -11,7 +11,7 @@ const RenderVnode = (props) => {
11
11
  }
12
12
 
13
13
  if (typeof props.vnode === "function") {
14
- return props.vnode(props.props);
14
+ return props.vnode(props.props, props.params);
15
15
  }
16
16
 
17
17
  return null;
@@ -24,6 +24,10 @@ RenderVnode.props = {
24
24
  props: {
25
25
  type: Object,
26
26
  default: () => ({})
27
+ },
28
+ params: {
29
+ type: Object,
30
+ default: null
27
31
  }
28
32
  };
29
33
  export default RenderVnode;
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <Transition name="autoHeight">
3
+ <slot />
4
+ </Transition>
5
+ </template>
6
+
7
+ <style lang="scss">
8
+ .autoHeight-enter-active,
9
+ .autoHeight-leave-active {
10
+ transition: height .3s linear;
11
+ height: calc-size(auto);
12
+ }
13
+
14
+ .autoHeight-enter-to, .autoHeight-leave-from {
15
+ height: calc-size(auto);
16
+ }
17
+
18
+ .autoHeight-enter-from, .autoHeight-leave-to {
19
+ height: 0;
20
+ }
21
+ </style>
@@ -1,3 +1,4 @@
1
+ export { default as AutoHeightTransition } from "./AutoHeightTransition.vue";
1
2
  export { default as ListTransition } from "./ListTransition.vue";
2
3
  export { default as MaxHeightTransition } from "./MaxHeightTransition.vue";
3
4
  export { default as SlideTransition } from "./SlideTransition.vue";
@@ -3,6 +3,7 @@ import { DanxOptions } from "../types";
3
3
 
4
4
  export const danxOptions = shallowRef<DanxOptions>({
5
5
  tinyMceApiKey: "set-api-key-in-danx-options",
6
+ router: undefined,
6
7
  request: {
7
8
  baseUrl: "",
8
9
  headers: {},
@@ -1,7 +1,7 @@
1
1
  import { useDebounceFn } from "@vueuse/core";
2
2
  import { uid } from "quasar";
3
3
  import { isReactive, Ref, shallowRef } from "vue";
4
- import type { ActionOptions, ActionOptionsPartial, ActionTarget, AnyObject, ResourceAction } from "../types";
4
+ import type { ActionOptions, ActionTarget, AnyObject, ResourceAction } from "../types";
5
5
  import { FlashMessages } from "./FlashMessages";
6
6
  import { storeObject } from "./objectStore";
7
7
 
@@ -11,36 +11,34 @@ export const activeActionVnode: Ref = shallowRef(null);
11
11
  * Hook to perform an action on a set of targets
12
12
  * This helper allows you to perform actions by name on a set of targets using a provided list of actions
13
13
  */
14
- export function useActions(actions: ActionOptions[], globalOptions: ActionOptionsPartial | null = null) {
14
+ export function useActions(actions: ActionOptions[], globalOptions: Partial<ActionOptions> | null = null) {
15
15
  const namespace = uid();
16
16
 
17
17
  /**
18
18
  * Resolve the action object based on the provided name (or return the object if the name is already an object)
19
19
  */
20
- function getAction(actionName: string | ActionOptions | ResourceAction): ResourceAction {
21
- let actionOptions: ActionOptions | ResourceAction;
22
-
20
+ function getAction(actionName: string, actionOptions?: Partial<ActionOptions>): ResourceAction {
23
21
  /// Resolve the action options or resource action based on the provided input
24
- if (typeof actionName === "string") {
25
- actionOptions = actions.find(a => a.name === actionName) || { name: actionName };
26
- } else {
27
- actionOptions = actionName;
22
+ const baseOptions = actions.find(a => a.name === actionName) || { name: actionName };
23
+
24
+ if (actionOptions) {
25
+ Object.assign(baseOptions, actionOptions);
28
26
  }
29
27
 
30
28
  // If the action is already reactive, return it
31
- if (isReactive(actionOptions) && "__type" in actionOptions) return actionOptions as ResourceAction;
29
+ if (isReactive(baseOptions) && "__type" in baseOptions) return baseOptions as ResourceAction;
32
30
 
33
31
  const resourceAction: ResourceAction = storeObject({
34
32
  ...globalOptions,
35
- ...actionOptions,
33
+ ...baseOptions,
36
34
  trigger: (target, input) => performAction(resourceAction, target, input),
37
35
  isApplying: false,
38
36
  __type: "__Action:" + namespace
39
37
  });
40
38
 
41
39
  // Assign Trigger function if it doesn't exist
42
- if (actionOptions.debounce) {
43
- resourceAction.trigger = useDebounceFn((target, input) => performAction(resourceAction, target, input), actionOptions.debounce);
40
+ if (baseOptions.debounce) {
41
+ resourceAction.trigger = useDebounceFn((target, input) => performAction(resourceAction, target, input), baseOptions.debounce);
44
42
  }
45
43
 
46
44
  return resourceAction;
@@ -63,7 +61,7 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
63
61
  }
64
62
  }
65
63
 
66
- return filteredActions.map((a: ActionOptions) => getAction(a));
64
+ return filteredActions.map((a: ActionOptions) => getAction(a.name));
67
65
  }
68
66
 
69
67
  /**
@@ -121,6 +119,11 @@ export function useActions(actions: ActionOptions[], globalOptions: ActionOption
121
119
  result = await onConfirmAction(action, target, input);
122
120
  }
123
121
 
122
+ // If the request was aborted (likely due to a newer request), just return immediately without changing state
123
+ if (result?.abort) {
124
+ return result;
125
+ }
126
+
124
127
  action.isApplying = false;
125
128
  setTargetSavingState(target, false);
126
129
 
@@ -187,8 +190,16 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
187
190
  result = await action.onAction(action.alias || action.name, target, input);
188
191
  }
189
192
  } catch (e) {
190
- console.error(e);
191
- result = { error: `An error occurred while performing the action ${action.label}. Please try again later.` };
193
+ if (("" + e).match(/Request was aborted/)) {
194
+ result = { abort: true };
195
+ } else {
196
+ console.error(e);
197
+ result = { error: `An error occurred while performing the action ${action.label}. Please try again later.` };
198
+ }
199
+ }
200
+
201
+ if (result?.abort) {
202
+ return result;
192
203
  }
193
204
 
194
205
  // If there is no return value or the result marks it as successful, we show a success message
@@ -203,11 +214,11 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
203
214
  }
204
215
 
205
216
  if (action.onSuccess) {
206
- action.onSuccess(result, target, input);
217
+ await action.onSuccess(result, target, input);
207
218
  }
208
219
 
209
220
  if (isBatch && action.onBatchSuccess) {
210
- action.onBatchSuccess(result, target, input);
221
+ await action.onBatchSuccess(result, target, input);
211
222
  }
212
223
  } else {
213
224
  const errors = [];
@@ -230,12 +241,12 @@ async function onConfirmAction(action: ActionOptions, target: ActionTarget, inpu
230
241
  FlashMessages.combine("error", errors);
231
242
 
232
243
  if (action.onError) {
233
- action.onError(result, target, input);
244
+ await action.onError(result, target, input);
234
245
  }
235
246
  }
236
247
 
237
248
  if (action.onFinish) {
238
- action.onFinish(result, target, input);
249
+ await action.onFinish(result, target, input);
239
250
  }
240
251
 
241
252
  return result;
@@ -1,5 +1,5 @@
1
1
  import { parseDateTime } from "./formats";
2
2
 
3
- export function diffInDays(date1, date2) {
4
- return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
3
+ export function diffInDays(date1: string, date2: string) {
4
+ return parseDateTime(date2).diff(parseDateTime(date1), ["days"]).days;
5
5
  }