@omnitend/dashboard-for-laravel 0.38.0 → 0.40.0
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/dist/dashboard-for-laravel.js +3841 -3804
- package/dist/dashboard-for-laravel.js.map +1 -1
- package/dist/dashboard-for-laravel.umd.cjs +5 -5
- package/dist/dashboard-for-laravel.umd.cjs.map +1 -1
- package/dist/style.css +2 -2
- package/docs/public/api-reference.json +25 -6
- package/docs/public/docs-map.md +1 -1
- package/package.json +1 -1
- package/resources/css/theme.scss +5 -1
- package/resources/js/components/extended/DXCurrencyInput.vue +23 -1
- package/resources/js/components/extended/DXSwitch.vue +6 -4
- package/resources/js/components/extended/DXTable.vue +44 -4
- package/resources/js/composables/useResourceEditor.ts +102 -21
- package/dist/charts.d.ts +0 -19
- package/dist/components/base/DAccordion.vue.d.ts +0 -12
- package/dist/components/base/DAccordionItem.vue.d.ts +0 -12
- package/dist/components/base/DAlert.vue.d.ts +0 -17
- package/dist/components/base/DAspect.vue.d.ts +0 -12
- package/dist/components/base/DAutocomplete.vue.d.ts +0 -32
- package/dist/components/base/DAvatar.vue.d.ts +0 -12
- package/dist/components/base/DBadge.vue.d.ts +0 -12
- package/dist/components/base/DBreadcrumb.vue.d.ts +0 -12
- package/dist/components/base/DButton.vue.d.ts +0 -88
- package/dist/components/base/DButtonGroup.vue.d.ts +0 -12
- package/dist/components/base/DButtonToolbar.vue.d.ts +0 -12
- package/dist/components/base/DCard.vue.d.ts +0 -12
- package/dist/components/base/DCol.vue.d.ts +0 -12
- package/dist/components/base/DCollapse.vue.d.ts +0 -12
- package/dist/components/base/DContainer.vue.d.ts +0 -12
- package/dist/components/base/DDropdown.vue.d.ts +0 -12
- package/dist/components/base/DDropdownDivider.vue.d.ts +0 -2
- package/dist/components/base/DDropdownItem.vue.d.ts +0 -12
- package/dist/components/base/DForm.vue.d.ts +0 -12
- package/dist/components/base/DFormCheckbox.vue.d.ts +0 -12
- package/dist/components/base/DFormCheckboxGroup.vue.d.ts +0 -12
- package/dist/components/base/DFormGroup.vue.d.ts +0 -12
- package/dist/components/base/DFormInput.vue.d.ts +0 -2
- package/dist/components/base/DFormInvalidFeedback.vue.d.ts +0 -12
- package/dist/components/base/DFormOtp.vue.d.ts +0 -12
- package/dist/components/base/DFormRadio.vue.d.ts +0 -12
- package/dist/components/base/DFormRadioGroup.vue.d.ts +0 -12
- package/dist/components/base/DFormSelect.vue.d.ts +0 -27
- package/dist/components/base/DFormSpinbutton.vue.d.ts +0 -12
- package/dist/components/base/DFormTags.vue.d.ts +0 -12
- package/dist/components/base/DFormText.vue.d.ts +0 -12
- package/dist/components/base/DFormTextarea.vue.d.ts +0 -2
- package/dist/components/base/DImage.vue.d.ts +0 -12
- package/dist/components/base/DInputGroup.vue.d.ts +0 -12
- package/dist/components/base/DLink.vue.d.ts +0 -12
- package/dist/components/base/DListGroup.vue.d.ts +0 -12
- package/dist/components/base/DListGroupItem.vue.d.ts +0 -12
- package/dist/components/base/DModal.vue.d.ts +0 -12
- package/dist/components/base/DNav.vue.d.ts +0 -12
- package/dist/components/base/DNavItem.vue.d.ts +0 -12
- package/dist/components/base/DNavbar.vue.d.ts +0 -12
- package/dist/components/base/DNavbarBrand.vue.d.ts +0 -12
- package/dist/components/base/DNavbarNav.vue.d.ts +0 -12
- package/dist/components/base/DNavbarToggle.vue.d.ts +0 -12
- package/dist/components/base/DOffcanvas.vue.d.ts +0 -12
- package/dist/components/base/DOverlay.vue.d.ts +0 -12
- package/dist/components/base/DPagination.vue.d.ts +0 -2
- package/dist/components/base/DPlaceholder.vue.d.ts +0 -12
- package/dist/components/base/DPopover.vue.d.ts +0 -12
- package/dist/components/base/DProgress.vue.d.ts +0 -12
- package/dist/components/base/DRow.vue.d.ts +0 -12
- package/dist/components/base/DSpinner.vue.d.ts +0 -2
- package/dist/components/base/DTable.vue.d.ts +0 -34
- package/dist/components/base/DTabs.vue.d.ts +0 -12
- package/dist/components/base/DToast.vue.d.ts +0 -12
- package/dist/components/base/DToaster.vue.d.ts +0 -12
- package/dist/components/base/DTooltip.vue.d.ts +0 -12
- package/dist/components/charts/DXBarChart.vue.d.ts +0 -7
- package/dist/components/charts/DXDoughnutChart.vue.d.ts +0 -24
- package/dist/components/charts/DXLineChart.vue.d.ts +0 -7
- package/dist/components/charts/chartTheme.d.ts +0 -59
- package/dist/components/charts/useThemedChart.d.ts +0 -40
- package/dist/components/extended/DXBasicForm.vue.d.ts +0 -10
- package/dist/components/extended/DXChoiceField.vue.d.ts +0 -26
- package/dist/components/extended/DXCurrencyInput.vue.d.ts +0 -27
- package/dist/components/extended/DXDashboard.vue.d.ts +0 -105
- package/dist/components/extended/DXDashboardNavbar.vue.d.ts +0 -86
- package/dist/components/extended/DXDashboardSidebar.vue.d.ts +0 -61
- package/dist/components/extended/DXField.vue.d.ts +0 -86
- package/dist/components/extended/DXFieldLabel.vue.d.ts +0 -18
- package/dist/components/extended/DXFieldShell.vue.d.ts +0 -73
- package/dist/components/extended/DXForm.vue.d.ts +0 -112
- package/dist/components/extended/DXFormField.vue.d.ts +0 -41
- package/dist/components/extended/DXNumericField.vue.d.ts +0 -23
- package/dist/components/extended/DXRepeater.vue.d.ts +0 -46
- package/dist/components/extended/DXRepeaterCards.vue.d.ts +0 -45
- package/dist/components/extended/DXStatCard.vue.d.ts +0 -56
- package/dist/components/extended/DXSwitch.vue.d.ts +0 -46
- package/dist/components/extended/DXTable.vue.d.ts +0 -466
- package/dist/components/extended/DXTableEditorModal.vue.d.ts +0 -100
- package/dist/components/extended/DXTablePagination.vue.d.ts +0 -50
- package/dist/components/extended/DXTableShell.vue.d.ts +0 -24
- package/dist/components/extended/DXUserAvatar.vue.d.ts +0 -40
- package/dist/components/extended/dxFieldContext.d.ts +0 -11
- package/dist/composables/defineForm.d.ts +0 -41
- package/dist/composables/useAsyncOptions.d.ts +0 -20
- package/dist/composables/useContainerWidth.d.ts +0 -96
- package/dist/composables/useForm.d.ts +0 -46
- package/dist/composables/useResourceEditor.d.ts +0 -75
- package/dist/composables/useToast.d.ts +0 -2
- package/dist/index.d.ts +0 -93
- package/dist/types/index.d.ts +0 -477
- package/dist/types/navigation.d.ts +0 -43
- package/dist/utils/api.d.ts +0 -30
- package/dist/utils/formSchema.d.ts +0 -59
- package/dist/utils/objectPath.d.ts +0 -18
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-
|
|
2
|
+
"generated": "2026-08-25T19:01:45.537Z",
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@omnitend/dashboard-for-laravel",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.40.0"
|
|
6
6
|
},
|
|
7
7
|
"components": {
|
|
8
8
|
"base": [
|
|
@@ -1478,7 +1478,7 @@
|
|
|
1478
1478
|
"name": "DXCurrencyInput",
|
|
1479
1479
|
"category": "extended",
|
|
1480
1480
|
"filePath": "resources/js/components/extended/DXCurrencyInput.vue",
|
|
1481
|
-
"description": "DXCurrencyInput — the money input leaf (#152). A currency-symbol-prefixed numeric input with the display/model split from #69: the MODEL is always a plain number (or null), while the displayed text is padded to the minor-unit precision on blur/seed (`3.8` -> `3.80`) and never reformatted mid-edit. Clearing the input emits `null` — never `NaN`, never `\"\"` — so \"no value\" is one predictable shape (#152). `minorUnits` (#116) edits an integer-minor-unit model (pence) as major units (pounds): display = model / 10^decimals, emit = round(input * 10^decimals). `min`/`max`/`step` and the displayed text are always in MAJOR units — only the model is scaled. DXField's `currency` field type renders this leaf; it exists standalone so a money input outside a form (an inline table correction, a quick filter) doesn't re-inline the `£`-prefix + toFixed + parse foot-guns.",
|
|
1481
|
+
"description": "DXCurrencyInput — the money input leaf (#152). A currency-symbol-prefixed numeric input with the display/model split from #69: the MODEL is always a plain number (or null), while the displayed text is padded to the minor-unit precision on blur/seed (`3.8` -> `3.80`) and never reformatted mid-edit. Clearing the input emits `null` — never `NaN`, never `\"\"` — so \"no value\" is one predictable shape (#152). `minorUnits` (#116) edits an integer-minor-unit model (pence) as major units (pounds): display = model / 10^decimals, emit = round(input * 10^decimals). `min`/`max`/`step` and the displayed text are always in MAJOR units — only the model is scaled. `append` puts a trailing affix after the input — the unit the price is per (\"Pint\", \"per case\", \"each\"). Without it a consumer needing a unit beside the amount has to hand-roll a `DInputGroup` + bare number input, which puts every foot-gun above straight back. A plain string covers most cases; the `#append` slot takes arbitrary content (a unit picker, a button) and wins over the prop, the same prop-with-slot-fallback shape `DXFieldShell` uses for `hint`. DXField's `currency` field type renders this leaf; it exists standalone so a money input outside a form (an inline table correction, a quick filter) doesn't re-inline the `£`-prefix + toFixed + parse foot-guns. On a DXField, reach the affix through the field's `inputProps` (`{ append: 'per case' }`) — it binds to this prop like any other.",
|
|
1482
1482
|
"props": [
|
|
1483
1483
|
{
|
|
1484
1484
|
"name": "modelValue",
|
|
@@ -1514,6 +1514,13 @@
|
|
|
1514
1514
|
"required": false,
|
|
1515
1515
|
"default": "undefined",
|
|
1516
1516
|
"description": "Input step in MAJOR units (default: one minor unit, e.g. 0.01)."
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
"name": "append",
|
|
1520
|
+
"type": "string",
|
|
1521
|
+
"required": false,
|
|
1522
|
+
"default": "undefined",
|
|
1523
|
+
"description": "Trailing affix shown after the input — the unit the amount is per\n (\"Pint\", \"per case\", \"each\"). Omit for no trailing cell. The `#append`\n slot overrides it when the affix needs to be more than a string."
|
|
1517
1524
|
}
|
|
1518
1525
|
],
|
|
1519
1526
|
"events": [
|
|
@@ -1523,7 +1530,13 @@
|
|
|
1523
1530
|
"properties": []
|
|
1524
1531
|
}
|
|
1525
1532
|
],
|
|
1526
|
-
"slots": [
|
|
1533
|
+
"slots": [
|
|
1534
|
+
{
|
|
1535
|
+
"name": "append",
|
|
1536
|
+
"description": "Trailing content after the input — arbitrary markup, overriding the `append` prop.",
|
|
1537
|
+
"bindings": []
|
|
1538
|
+
}
|
|
1539
|
+
],
|
|
1527
1540
|
"methods": []
|
|
1528
1541
|
},
|
|
1529
1542
|
{
|
|
@@ -3097,7 +3110,7 @@
|
|
|
3097
3110
|
"name": "size",
|
|
3098
3111
|
"type": "\"sm\"",
|
|
3099
3112
|
"required": false,
|
|
3100
|
-
"description": "`\"sm\"` renders a compact box (
|
|
3113
|
+
"description": "`\"sm\"` renders a compact box (a few px shorter than the default, which\nmatches the input height) for dense rows that repeat the switch per item (a\nper-line visibility toggle in a modal). Omit for the standard size, which\nlines the switch up with the `.form-control`\nheight so a switch lines up with inputs beside it."
|
|
3101
3114
|
}
|
|
3102
3115
|
],
|
|
3103
3116
|
"events": [
|
|
@@ -3414,7 +3427,13 @@
|
|
|
3414
3427
|
"name": "deleteGuard",
|
|
3415
3428
|
"type": "TSFunctionType",
|
|
3416
3429
|
"required": false,
|
|
3417
|
-
"description": "Guard run when Delete is clicked, before the confirm dialog and request.\nReturn a message for a non-deletable item to show it immediately (as a\ntoast) and skip both the confirm and the delete request; return\n`null`/`undefined` to proceed with the normal confirm + delete. Lets you\nshort-circuit a doomed delete (e.g. a record with dependents that the\nserver would reject) with an immediate, specific reason."
|
|
3430
|
+
"description": "Guard run when Delete is clicked, before the confirm dialog and request.\nReturn a message for a non-deletable item to show it immediately (as a\ntoast) and skip both the confirm and the delete request; return\n`null`/`undefined` to proceed with the normal confirm + delete. Lets you\nshort-circuit a doomed delete (e.g. a record with dependents that the\nserver would reject) with an immediate, specific reason.\n\nAWAITED, so it may return a promise — the same shape as `saveGuard`, so\nan `async` guard behaves the way its author expects. A guard that throws\nalso aborts, surfacing its message."
|
|
3431
|
+
},
|
|
3432
|
+
{
|
|
3433
|
+
"name": "saveGuard",
|
|
3434
|
+
"type": "TSFunctionType",
|
|
3435
|
+
"required": false,
|
|
3436
|
+
"description": "Guard run when the modal's Save/Create is clicked, before the request.\nReturn a message to abort the save — it is shown immediately (as a toast)\nand no request is made; return `null`/`undefined` to proceed. Covers both\nthe `editUrl` and `createUrl` paths.\n\nAWAITED (as `deleteGuard` is), so it may return a promise: the case it\nexists for is an async control in an `edit-value` slot — an image\nupload — that has not finished when Save is clicked. Without the guard the\nform submits the previous media map, the save SUCCEEDS, the toast says\nsaved and the modal closes: the image is lost with no error anywhere,\nbecause every individual step worked. Await the upload here and the save\nsimply waits for it.\n\n`item` is the row being edited, `null` in create mode (as in the\n`edit-value` slot's binding); `data` is the live form data. Aborting\nleaves the modal open with the user's edits intact. A guard that throws\nalso aborts, surfacing its message."
|
|
3418
3437
|
},
|
|
3419
3438
|
{
|
|
3420
3439
|
"name": "createUrl",
|
package/docs/public/docs-map.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Documentation Map
|
|
2
2
|
|
|
3
3
|
> Auto-generated hierarchical overview of all documentation
|
|
4
|
-
> Last updated: 2026-
|
|
4
|
+
> Last updated: 2026-08-25T19:01:45.587Z
|
|
5
5
|
|
|
6
6
|
This file provides a complete map of all available documentation for AI agents and developers.
|
|
7
7
|
|
package/package.json
CHANGED
package/resources/css/theme.scss
CHANGED
|
@@ -48,7 +48,11 @@ $color-contrast-light: #fff; // Light text color (for dark backgrounds)
|
|
|
48
48
|
// ----------------------------------------------------------------------------
|
|
49
49
|
|
|
50
50
|
$font-family-base: "Poppins", sans-serif;
|
|
51
|
-
$font-size-base:
|
|
51
|
+
$font-size-base: 1rem; // 16px — the default tier. Legacy omnitend rendered body
|
|
52
|
+
// and table text at 16px; the earlier 0.875rem (14px) shrank every ported page's
|
|
53
|
+
// text ~12% while rem-fixed chrome stayed put, so controls read oversized and
|
|
54
|
+
// text miniaturised (PO20). The small tier is Bootstrap's `.875em` (14px) via
|
|
55
|
+
// `.small`/`<small>`; the extra-small tier is `.75em` (12px, badges/fine print).
|
|
52
56
|
$font-weight-base: 400;
|
|
53
57
|
$line-height-base: 1.5;
|
|
54
58
|
|
|
@@ -12,9 +12,18 @@
|
|
|
12
12
|
`min`/`max`/`step` and the displayed text are always in MAJOR units — only
|
|
13
13
|
the model is scaled.
|
|
14
14
|
|
|
15
|
+
`append` puts a trailing affix after the input — the unit the price is per
|
|
16
|
+
("Pint", "per case", "each"). Without it a consumer needing a unit beside the
|
|
17
|
+
amount has to hand-roll a `DInputGroup` + bare number input, which puts every
|
|
18
|
+
foot-gun above straight back. A plain string covers most cases; the `#append`
|
|
19
|
+
slot takes arbitrary content (a unit picker, a button) and wins over the prop,
|
|
20
|
+
the same prop-with-slot-fallback shape `DXFieldShell` uses for `hint`.
|
|
21
|
+
|
|
15
22
|
DXField's `currency` field type renders this leaf; it exists standalone so a
|
|
16
23
|
money input outside a form (an inline table correction, a quick filter)
|
|
17
|
-
doesn't re-inline the `£`-prefix + toFixed + parse foot-guns.
|
|
24
|
+
doesn't re-inline the `£`-prefix + toFixed + parse foot-guns. On a DXField,
|
|
25
|
+
reach the affix through the field's `inputProps` (`{ append: 'per case' }`) —
|
|
26
|
+
it binds to this prop like any other.
|
|
18
27
|
-->
|
|
19
28
|
<template>
|
|
20
29
|
<DInputGroup>
|
|
@@ -30,6 +39,14 @@
|
|
|
30
39
|
@focus="handleFocus"
|
|
31
40
|
@blur="handleBlur"
|
|
32
41
|
/>
|
|
42
|
+
<!-- Trailing affix. Conditional so an input with no unit renders the
|
|
43
|
+
exact same markup as before (one prepend, no trailing cell). -->
|
|
44
|
+
<template v-if="append || $slots.append" #append>
|
|
45
|
+
<!-- @slot Trailing content after the input — arbitrary markup, overriding the `append` prop. -->
|
|
46
|
+
<slot name="append">
|
|
47
|
+
<span class="input-group-text">{{ append }}</span>
|
|
48
|
+
</slot>
|
|
49
|
+
</template>
|
|
33
50
|
</DInputGroup>
|
|
34
51
|
</template>
|
|
35
52
|
|
|
@@ -54,6 +71,10 @@ interface Props {
|
|
|
54
71
|
minorUnits?: boolean;
|
|
55
72
|
/** Input step in MAJOR units (default: one minor unit, e.g. 0.01). */
|
|
56
73
|
step?: string | number;
|
|
74
|
+
/** Trailing affix shown after the input — the unit the amount is per
|
|
75
|
+
* ("Pint", "per case", "each"). Omit for no trailing cell. The `#append`
|
|
76
|
+
* slot overrides it when the affix needs to be more than a string. */
|
|
77
|
+
append?: string;
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -62,6 +83,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
62
83
|
decimals: 2,
|
|
63
84
|
minorUnits: false,
|
|
64
85
|
step: undefined,
|
|
86
|
+
append: undefined,
|
|
65
87
|
});
|
|
66
88
|
|
|
67
89
|
const emit = defineEmits<{
|
|
@@ -64,9 +64,10 @@ interface Props {
|
|
|
64
64
|
*/
|
|
65
65
|
onVariant?: "success" | "neutral";
|
|
66
66
|
/**
|
|
67
|
-
* `"sm"` renders a compact box (
|
|
68
|
-
* dense rows that repeat the switch per item (a
|
|
69
|
-
* a modal). Omit for the standard size, which
|
|
67
|
+
* `"sm"` renders a compact box (a few px shorter than the default, which
|
|
68
|
+
* matches the input height) for dense rows that repeat the switch per item (a
|
|
69
|
+
* per-line visibility toggle in a modal). Omit for the standard size, which
|
|
70
|
+
* lines the switch up with the `.form-control`
|
|
70
71
|
* height so a switch lines up with inputs beside it.
|
|
71
72
|
*/
|
|
72
73
|
size?: "sm";
|
|
@@ -140,7 +141,8 @@ defineOptions({
|
|
|
140
141
|
|
|
141
142
|
/* Compact size (#… size="sm"): a shorter box for dense rows that repeat the
|
|
142
143
|
switch per item. `min-height: auto` drops the input-height floor so the box
|
|
143
|
-
shrinks to its padded content
|
|
144
|
+
shrinks to its padded content, a few px under the input-height default;
|
|
145
|
+
the smaller font pulls the
|
|
144
146
|
label in to match. The fixed-rem toggle itself is unchanged — only the box
|
|
145
147
|
chrome tightens. */
|
|
146
148
|
.dx-switch--sm :deep(.form-check) {
|
|
@@ -725,8 +725,38 @@ export interface Props<TItem = any> {
|
|
|
725
725
|
* `null`/`undefined` to proceed with the normal confirm + delete. Lets you
|
|
726
726
|
* short-circuit a doomed delete (e.g. a record with dependents that the
|
|
727
727
|
* server would reject) with an immediate, specific reason.
|
|
728
|
+
*
|
|
729
|
+
* AWAITED, so it may return a promise — the same shape as `saveGuard`, so
|
|
730
|
+
* an `async` guard behaves the way its author expects. A guard that throws
|
|
731
|
+
* also aborts, surfacing its message.
|
|
732
|
+
*/
|
|
733
|
+
deleteGuard?: (
|
|
734
|
+
item: TItem,
|
|
735
|
+
) => string | null | undefined | Promise<string | null | undefined>;
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Guard run when the modal's Save/Create is clicked, before the request.
|
|
739
|
+
* Return a message to abort the save — it is shown immediately (as a toast)
|
|
740
|
+
* and no request is made; return `null`/`undefined` to proceed. Covers both
|
|
741
|
+
* the `editUrl` and `createUrl` paths.
|
|
742
|
+
*
|
|
743
|
+
* AWAITED (as `deleteGuard` is), so it may return a promise: the case it
|
|
744
|
+
* exists for is an async control in an `edit-value` slot — an image
|
|
745
|
+
* upload — that has not finished when Save is clicked. Without the guard the
|
|
746
|
+
* form submits the previous media map, the save SUCCEEDS, the toast says
|
|
747
|
+
* saved and the modal closes: the image is lost with no error anywhere,
|
|
748
|
+
* because every individual step worked. Await the upload here and the save
|
|
749
|
+
* simply waits for it.
|
|
750
|
+
*
|
|
751
|
+
* `item` is the row being edited, `null` in create mode (as in the
|
|
752
|
+
* `edit-value` slot's binding); `data` is the live form data. Aborting
|
|
753
|
+
* leaves the modal open with the user's edits intact. A guard that throws
|
|
754
|
+
* also aborts, surfacing its message.
|
|
728
755
|
*/
|
|
729
|
-
|
|
756
|
+
saveGuard?: (
|
|
757
|
+
item: TItem | null,
|
|
758
|
+
data: Record<string, any>,
|
|
759
|
+
) => string | null | undefined | Promise<string | null | undefined>;
|
|
730
760
|
|
|
731
761
|
/** API endpoint for creating new items (e.g., "/api/products") — enables "New" button */
|
|
732
762
|
createUrl?: string;
|
|
@@ -2182,10 +2212,20 @@ const handleFilterChange = (fieldKey: string, value: string | string[]) => {
|
|
|
2182
2212
|
);
|
|
2183
2213
|
}
|
|
2184
2214
|
|
|
2185
|
-
//
|
|
2186
|
-
//
|
|
2215
|
+
// API mode: a changed filter is a fresh result set — go back to page 1,
|
|
2216
|
+
// exactly as the Inertia branch above does. Without this, typing a
|
|
2217
|
+
// filter while on page 3 of the unfiltered set requests page 3 of the
|
|
2218
|
+
// NARROWED set, which is usually past its end: the server returns an
|
|
2219
|
+
// empty page and the table shows "no rows match your filters" over a
|
|
2220
|
+
// non-zero total. Same exactly-one-fetch dance as the apiUrl/provider
|
|
2221
|
+
// swap watchers: a page change re-invokes the provider on its own;
|
|
2222
|
+
// only when already on page 1 does refresh() need to force it.
|
|
2187
2223
|
if (isProviderMode.value && tableRef.value) {
|
|
2188
|
-
|
|
2224
|
+
const wasOnFirstPage = apiCurrentPage.value === 1;
|
|
2225
|
+
apiCurrentPage.value = 1;
|
|
2226
|
+
if (wasOnFirstPage) {
|
|
2227
|
+
refresh();
|
|
2228
|
+
}
|
|
2189
2229
|
}
|
|
2190
2230
|
|
|
2191
2231
|
// Emit filterChange event for backward compatibility
|
|
@@ -30,8 +30,15 @@ export interface ResourceEditorProps<T = any> {
|
|
|
30
30
|
deleteUrl?: string;
|
|
31
31
|
/** `POST` endpoint for creating new items, e.g. `/api/products`. */
|
|
32
32
|
createUrl?: string;
|
|
33
|
-
/** Guard run before delete — a message short-circuits with a toast. */
|
|
34
|
-
deleteGuard?: (
|
|
33
|
+
/** Guard run before delete — a message short-circuits with a toast. Awaited. */
|
|
34
|
+
deleteGuard?: (
|
|
35
|
+
item: T,
|
|
36
|
+
) => string | null | undefined | Promise<string | null | undefined>;
|
|
37
|
+
/** Guard run before save — a message aborts with a toast. Awaited. */
|
|
38
|
+
saveGuard?: (
|
|
39
|
+
item: T | null,
|
|
40
|
+
data: Record<string, any>,
|
|
41
|
+
) => string | null | undefined | Promise<string | null | undefined>;
|
|
35
42
|
}
|
|
36
43
|
|
|
37
44
|
/**
|
|
@@ -299,18 +306,96 @@ export function useResourceEditor<T = any>(
|
|
|
299
306
|
showEditModal.value = true;
|
|
300
307
|
};
|
|
301
308
|
|
|
302
|
-
// Save from the edit modal
|
|
309
|
+
// Save from the edit modal (create or edit).
|
|
303
310
|
const save = async () => {
|
|
304
311
|
if (!editForm.value) return;
|
|
305
312
|
|
|
313
|
+
// Don't save — or evaluate the guard — until the full record has loaded
|
|
314
|
+
// (showUrl). The thin list row is missing fields the form and the guard
|
|
315
|
+
// may both depend on, so a guard keyed on one of them would see
|
|
316
|
+
// `undefined` and wave the save through, which is the silent success the
|
|
317
|
+
// guard exists to prevent. Same rule as `remove`.
|
|
318
|
+
if (editLoading.value) return;
|
|
319
|
+
|
|
320
|
+
// Not re-entrant. The footer button disables itself while
|
|
321
|
+
// `pendingAction` is set, but DXForm's own submit (Enter in a field)
|
|
322
|
+
// reaches here directly — and with an AWAITED save guard there is now a
|
|
323
|
+
// real window in which a second Save would fire a second request.
|
|
324
|
+
if (pendingAction.value) return;
|
|
325
|
+
|
|
306
326
|
pendingAction.value = 'save';
|
|
307
327
|
try {
|
|
328
|
+
if (!(await passesSaveGuard())) return;
|
|
308
329
|
await performSave();
|
|
309
330
|
} finally {
|
|
310
331
|
pendingAction.value = null;
|
|
311
332
|
}
|
|
312
333
|
};
|
|
313
334
|
|
|
335
|
+
/*
|
|
336
|
+
* BOTH guards run through here, which is the point of it: they must not
|
|
337
|
+
* diverge. Each is AWAITED. The case `saveGuard` exists for is a control
|
|
338
|
+
* that has not finished yet — an image upload still in flight when Save is
|
|
339
|
+
* clicked. The form would submit the previous media map, the request would
|
|
340
|
+
* SUCCEED, the toast would say saved and the modal would close: the image
|
|
341
|
+
* lost with no error anywhere, because every individual step worked.
|
|
342
|
+
* `deleteGuard` is awaited on the same path so an `async` guard does what
|
|
343
|
+
* its author expects — unawaited, its promise is merely truthy, which would
|
|
344
|
+
* block every delete with an unreadable message.
|
|
345
|
+
*
|
|
346
|
+
* Returns true to proceed. Aborting leaves the modal open and the form
|
|
347
|
+
* untouched — the user's edits are the thing being protected, so nothing is
|
|
348
|
+
* closed or reset on the way out. A guard that THROWS also aborts: a guard
|
|
349
|
+
* that could not decide is not permission to proceed.
|
|
350
|
+
*/
|
|
351
|
+
const passesGuard = async (
|
|
352
|
+
run: () => string | null | undefined | Promise<string | null | undefined>,
|
|
353
|
+
title: string,
|
|
354
|
+
fallbackMessage: string,
|
|
355
|
+
): Promise<boolean> => {
|
|
356
|
+
let message: string | null | undefined;
|
|
357
|
+
try {
|
|
358
|
+
message = await run();
|
|
359
|
+
} catch (error: any) {
|
|
360
|
+
message = error?.message ?? fallbackMessage;
|
|
361
|
+
}
|
|
362
|
+
if (!message) return true;
|
|
363
|
+
|
|
364
|
+
createToast?.({
|
|
365
|
+
title,
|
|
366
|
+
body: message,
|
|
367
|
+
variant: 'danger',
|
|
368
|
+
modelValue: 5000,
|
|
369
|
+
});
|
|
370
|
+
return false;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const passesSaveGuard = (): Promise<boolean> => {
|
|
374
|
+
const guard = props.saveGuard;
|
|
375
|
+
if (!guard) return Promise.resolve(true);
|
|
376
|
+
|
|
377
|
+
return passesGuard(
|
|
378
|
+
() =>
|
|
379
|
+
guard(
|
|
380
|
+
isCreateMode.value ? null : (selectedItem.value as T | null),
|
|
381
|
+
editForm.value.data,
|
|
382
|
+
),
|
|
383
|
+
'Cannot save',
|
|
384
|
+
'Could not save. Please try again.',
|
|
385
|
+
);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const passesDeleteGuard = (): Promise<boolean> => {
|
|
389
|
+
const guard = props.deleteGuard;
|
|
390
|
+
if (!guard) return Promise.resolve(true);
|
|
391
|
+
|
|
392
|
+
return passesGuard(
|
|
393
|
+
() => guard(selectedItem.value as T),
|
|
394
|
+
'Cannot delete',
|
|
395
|
+
'Could not delete. Please try again.',
|
|
396
|
+
);
|
|
397
|
+
};
|
|
398
|
+
|
|
314
399
|
const performSave = async () => {
|
|
315
400
|
// Create mode: POST to createUrl
|
|
316
401
|
if (isCreateMode.value && props.createUrl) {
|
|
@@ -441,27 +526,23 @@ export function useResourceEditor<T = any>(
|
|
|
441
526
|
// may depend on fields the thin list row doesn't carry.
|
|
442
527
|
if (editLoading.value) return;
|
|
443
528
|
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
if (
|
|
448
|
-
createToast?.({
|
|
449
|
-
title: 'Cannot delete',
|
|
450
|
-
body: guardMessage,
|
|
451
|
-
variant: 'danger',
|
|
452
|
-
modelValue: 5000,
|
|
453
|
-
});
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Confirm deletion
|
|
458
|
-
const itemName = (selectedItem.value as any).name || (selectedItem.value as any).title || singularItemName.value;
|
|
459
|
-
const confirmed = window.confirm(`Are you sure you want to delete "${itemName}"? This action cannot be undone.`);
|
|
460
|
-
|
|
461
|
-
if (!confirmed) return;
|
|
529
|
+
// Not re-entrant, for the same reason as `save`: the guard is awaited,
|
|
530
|
+
// so a second Delete arriving by a path that doesn't disable the button
|
|
531
|
+
// would run the guard — and the confirm — a second time.
|
|
532
|
+
if (pendingAction.value) return;
|
|
462
533
|
|
|
463
534
|
pendingAction.value = 'delete';
|
|
464
535
|
try {
|
|
536
|
+
// Delete guard: a non-null message means this item can't be deleted —
|
|
537
|
+
// show it immediately and skip the confirm and the request entirely.
|
|
538
|
+
if (!(await passesDeleteGuard())) return;
|
|
539
|
+
|
|
540
|
+
// Confirm deletion
|
|
541
|
+
const itemName = (selectedItem.value as any).name || (selectedItem.value as any).title || singularItemName.value;
|
|
542
|
+
const confirmed = window.confirm(`Are you sure you want to delete "${itemName}"? This action cannot be undone.`);
|
|
543
|
+
|
|
544
|
+
if (!confirmed) return;
|
|
545
|
+
|
|
465
546
|
const itemId = (selectedItem.value as any).id;
|
|
466
547
|
const url = props.deleteUrl.replace(':id', itemId);
|
|
467
548
|
|
package/dist/charts.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Chart entry point — `@omnitend/dashboard-for-laravel/charts` (#142).
|
|
3
|
-
*
|
|
4
|
-
* The chart components are the ONLY ones that need `chart.js` / `vue-chartjs`,
|
|
5
|
-
* so they live behind this separate subpath entry. The main entry
|
|
6
|
-
* (`@omnitend/dashboard-for-laravel`) never references the chart libraries, so
|
|
7
|
-
* they are genuinely OPTIONAL peers: a consumer that renders no charts installs
|
|
8
|
-
* neither and their build no longer fails resolving them.
|
|
9
|
-
*
|
|
10
|
-
* import { DXBarChart } from '@omnitend/dashboard-for-laravel/charts';
|
|
11
|
-
*
|
|
12
|
-
* Chart containers are styled by `.dx-chart` in the main stylesheet
|
|
13
|
-
* (`@omnitend/dashboard-for-laravel/style.css`), which chart consumers already
|
|
14
|
-
* import for the theme and the `--dx-chart-*` palette variables — so this entry
|
|
15
|
-
* ships no CSS of its own.
|
|
16
|
-
*/
|
|
17
|
-
export { default as DXBarChart } from "./components/charts/DXBarChart.vue";
|
|
18
|
-
export { default as DXLineChart } from "./components/charts/DXLineChart.vue";
|
|
19
|
-
export { default as DXDoughnutChart } from "./components/charts/DXDoughnutChart.vue";
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare var __VLS_7: string | number, __VLS_8: any;
|
|
2
|
-
type __VLS_Slots = {} & {
|
|
3
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
-
};
|
|
5
|
-
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
-
export default _default;
|
|
8
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
-
new (): {
|
|
10
|
-
$slots: S;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare var __VLS_7: string | number, __VLS_8: any;
|
|
2
|
-
type __VLS_Slots = {} & {
|
|
3
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
-
};
|
|
5
|
-
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
-
export default _default;
|
|
8
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
-
new (): {
|
|
10
|
-
$slots: S;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
type __VLS_Props = {
|
|
2
|
-
modelValue?: boolean | number;
|
|
3
|
-
};
|
|
4
|
-
declare var __VLS_7: string | number, __VLS_8: {};
|
|
5
|
-
type __VLS_Slots = {} & {
|
|
6
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
7
|
-
};
|
|
8
|
-
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
9
|
-
modelValue: boolean | number;
|
|
10
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
11
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
12
|
-
export default _default;
|
|
13
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
14
|
-
new (): {
|
|
15
|
-
$slots: S;
|
|
16
|
-
};
|
|
17
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare var __VLS_7: string | number, __VLS_8: any;
|
|
2
|
-
type __VLS_Slots = {} & {
|
|
3
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
-
};
|
|
5
|
-
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
-
export default _default;
|
|
8
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
-
new (): {
|
|
10
|
-
$slots: S;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { SelectOption } from "bootstrap-vue-next";
|
|
2
|
-
type AutocompleteOptions = readonly (object | string | number | boolean)[];
|
|
3
|
-
type FilterFunction = (options: SelectOption[], search: string) => SelectOption[];
|
|
4
|
-
interface Props {
|
|
5
|
-
/** Options passed straight through to BAutocomplete (see its `options` prop). */
|
|
6
|
-
options?: AutocompleteOptions;
|
|
7
|
-
/**
|
|
8
|
-
* A custom filter. Composed with `null-option` rather than replaced: when a
|
|
9
|
-
* null option is present it stays pinned and this runs on the rest.
|
|
10
|
-
*/
|
|
11
|
-
filterFunction?: FilterFunction;
|
|
12
|
-
/**
|
|
13
|
-
* Prepend a pinned, never-filtered, selectable "no selection" row whose value
|
|
14
|
-
* is `null` (#138). `true` → default label "None"; a string → that label.
|
|
15
|
-
* Selecting it sets the model to `null` (bvn reads that as no selection, so
|
|
16
|
-
* the ✕ hides and the placeholder shows). Off by default — unchanged
|
|
17
|
-
* behaviour. DXTable does NOT use this; it builds its own "All" option.
|
|
18
|
-
*/
|
|
19
|
-
nullOption?: boolean | string;
|
|
20
|
-
}
|
|
21
|
-
declare var __VLS_6: string | number, __VLS_7: any;
|
|
22
|
-
type __VLS_Slots = {} & {
|
|
23
|
-
[K in NonNullable<typeof __VLS_6>]?: (props: typeof __VLS_7) => any;
|
|
24
|
-
};
|
|
25
|
-
declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
26
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
27
|
-
export default _default;
|
|
28
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
29
|
-
new (): {
|
|
30
|
-
$slots: S;
|
|
31
|
-
};
|
|
32
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare var __VLS_7: string | number, __VLS_8: any;
|
|
2
|
-
type __VLS_Slots = {} & {
|
|
3
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
-
};
|
|
5
|
-
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
-
export default _default;
|
|
8
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
-
new (): {
|
|
10
|
-
$slots: S;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare var __VLS_7: string | number, __VLS_8: {};
|
|
2
|
-
type __VLS_Slots = {} & {
|
|
3
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
-
};
|
|
5
|
-
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
-
export default _default;
|
|
8
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
-
new (): {
|
|
10
|
-
$slots: S;
|
|
11
|
-
};
|
|
12
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
declare var __VLS_7: string | number, __VLS_8: any;
|
|
2
|
-
type __VLS_Slots = {} & {
|
|
3
|
-
[K in NonNullable<typeof __VLS_7>]?: (props: typeof __VLS_8) => any;
|
|
4
|
-
};
|
|
5
|
-
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
6
|
-
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
7
|
-
export default _default;
|
|
8
|
-
type __VLS_WithSlots<T, S> = T & {
|
|
9
|
-
new (): {
|
|
10
|
-
$slots: S;
|
|
11
|
-
};
|
|
12
|
-
};
|