@stonecrop/desktop 0.13.14 → 0.15.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/README.md +3 -4
- package/dist/desktop.css +1 -1
- package/dist/desktop.js +6139 -3120
- package/dist/desktop.js.map +1 -1
- package/package.json +28 -25
- package/src/components/ActionSet.vue +1 -1
- package/src/components/Desktop.vue +103 -14
- package/dist/actions-Bg0Mh5s7.js +0 -137
- package/dist/actions-Bg0Mh5s7.js.map +0 -1
- package/dist/desktop.umd.cjs +0 -85
- package/dist/desktop.umd.cjs.map +0 -1
- package/dist/index-C3nXTrEd.js +0 -1895
- package/dist/index-C3nXTrEd.js.map +0 -1
- package/dist/index.js +0 -6
- package/dist/operation-log-DB-dGNT9-C9cIr4-e.js +0 -592
- package/dist/operation-log-DB-dGNT9-C9cIr4-e.js.map +0 -1
- package/dist/plugins/index.js +0 -17
- package/dist/router.js +0 -6
- package/dist/src/tsdoc-metadata.json +0 -11
- package/dist/types/index.js +0 -0
package/package.json
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/desktop",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"name": "Tyler Matteson",
|
|
8
|
-
"email": "tyler@agritheory.com"
|
|
3
|
+
"version": "0.15.0",
|
|
4
|
+
"description": "Desktop-specific components for Stonecrop UI",
|
|
5
|
+
"bugs": {
|
|
6
|
+
"url": "https://github.com/agritheory/stonecrop/issues"
|
|
9
7
|
},
|
|
10
8
|
"repository": {
|
|
11
9
|
"type": "git",
|
|
12
10
|
"url": "https://github.com/agritheory/stonecrop",
|
|
13
11
|
"directory": "desktop"
|
|
14
12
|
},
|
|
15
|
-
"
|
|
16
|
-
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Tyler Matteson",
|
|
16
|
+
"email": "tyler@agritheory.com"
|
|
17
17
|
},
|
|
18
|
-
"
|
|
18
|
+
"sideEffects": [
|
|
19
|
+
"**/*.css"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
19
22
|
"exports": {
|
|
20
23
|
".": {
|
|
21
24
|
"types": "./dist/desktop.d.ts",
|
|
@@ -24,22 +27,17 @@
|
|
|
24
27
|
"./styles": "./dist/desktop.css",
|
|
25
28
|
"./types": "./dist/src/types/index.d.ts"
|
|
26
29
|
},
|
|
30
|
+
"types": "./dist/desktop.d.ts",
|
|
27
31
|
"files": [
|
|
28
32
|
"dist/*",
|
|
29
33
|
"src/*"
|
|
30
34
|
],
|
|
31
|
-
"sideEffects": [
|
|
32
|
-
"**/*.css"
|
|
33
|
-
],
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"@stonecrop/aform": "0.
|
|
36
|
-
"@stonecrop/
|
|
37
|
-
"@stonecrop/
|
|
38
|
-
"@stonecrop/
|
|
39
|
-
"@stonecrop/themes": "0.
|
|
40
|
-
},
|
|
41
|
-
"peerDependencies": {
|
|
42
|
-
"vue": "^3.5.33"
|
|
36
|
+
"@stonecrop/aform": "0.15.0",
|
|
37
|
+
"@stonecrop/atable": "0.15.0",
|
|
38
|
+
"@stonecrop/stonecrop": "0.15.0",
|
|
39
|
+
"@stonecrop/schema": "0.15.0",
|
|
40
|
+
"@stonecrop/themes": "0.15.0"
|
|
43
41
|
},
|
|
44
42
|
"devDependencies": {
|
|
45
43
|
"@microsoft/api-documenter": "^7.30.5",
|
|
@@ -59,15 +57,19 @@
|
|
|
59
57
|
"vitest": "^4.1.5",
|
|
60
58
|
"vue": "^3.5.33",
|
|
61
59
|
"vue-router": "^5.0.6",
|
|
60
|
+
"vue-tsc": "^3.3.6",
|
|
62
61
|
"stonecrop-rig": "0.7.0"
|
|
63
62
|
},
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
"
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"pinia": "^3.0.4",
|
|
65
|
+
"vue": "^3.5.33"
|
|
67
66
|
},
|
|
68
67
|
"engines": {
|
|
69
68
|
"node": ">=24.0.0"
|
|
70
69
|
},
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
},
|
|
71
73
|
"scripts": {
|
|
72
74
|
"_phase:build": "heft build && vite build && rushx docs",
|
|
73
75
|
"build": "heft build && vite build && rushx docs",
|
|
@@ -75,7 +77,8 @@
|
|
|
75
77
|
"lint": "oxlint",
|
|
76
78
|
"lint:fix": "oxlint --fix",
|
|
77
79
|
"test": "vitest run --coverage.enabled false",
|
|
78
|
-
"test:
|
|
79
|
-
"test:
|
|
80
|
+
"test:coverage": "vitest run --coverage",
|
|
81
|
+
"test:types": "vue-tsc --noEmit",
|
|
82
|
+
"test:watch": "vitest watch"
|
|
80
83
|
}
|
|
81
84
|
}
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
<ActionSet :elements="actionElements" @action-click="handleActionClick" />
|
|
5
5
|
|
|
6
6
|
<!-- Main content using AForm -->
|
|
7
|
-
<AForm
|
|
7
|
+
<AForm
|
|
8
|
+
v-if="currentViewSchema.length > 0"
|
|
9
|
+
v-model:data="currentViewData"
|
|
10
|
+
:schema="currentViewSchema"
|
|
11
|
+
:errors="fieldErrors" />
|
|
8
12
|
<div v-else-if="!stonecrop" class="loading"><p>Initializing Stonecrop...</p></div>
|
|
9
13
|
<div v-else class="loading">
|
|
10
14
|
<p>Loading {{ currentView }} data...</p>
|
|
@@ -31,7 +35,7 @@
|
|
|
31
35
|
</template>
|
|
32
36
|
|
|
33
37
|
<script setup lang="ts">
|
|
34
|
-
import { useStonecrop } from '@stonecrop/stonecrop'
|
|
38
|
+
import { useStonecrop, useValidationStore } from '@stonecrop/stonecrop'
|
|
35
39
|
import { AForm, type AFormLinkNavigator, type ResolvedField, type ResolvedTable } from '@stonecrop/aform'
|
|
36
40
|
import type { ColumnSchema } from '@stonecrop/schema'
|
|
37
41
|
import { computed, onMounted, onUnmounted, provide, ref, unref, watch } from 'vue'
|
|
@@ -105,6 +109,24 @@ const emit = defineEmits<{
|
|
|
105
109
|
|
|
106
110
|
const { stonecrop } = useStonecrop()
|
|
107
111
|
|
|
112
|
+
// Field-validation store (advisory, client-side). Pinia is a declared peerDependency, kept external
|
|
113
|
+
// in this package's build (rollupOptions), so `useValidationStore()` resolves the host app's single
|
|
114
|
+
// active Pinia directly — the old `getCurrentInstance().$pinia` workaround for the bundled-Pinia bug
|
|
115
|
+
// is no longer needed. Still guarded: validation is optional and Desktop predates it, so a host that
|
|
116
|
+
// mounts Desktop without Pinia disables validation gracefully instead of crashing on mount.
|
|
117
|
+
let validationStore: ReturnType<typeof useValidationStore> | null = null
|
|
118
|
+
try {
|
|
119
|
+
validationStore = useValidationStore()
|
|
120
|
+
} catch {
|
|
121
|
+
validationStore = null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Inline field errors handed to AForm. Only surfaced in the record form view (currentView is
|
|
125
|
+
// defined below); empty elsewhere so a previous record's errors never bleed into a list view.
|
|
126
|
+
const fieldErrors = computed<Record<string, string[]>>(() =>
|
|
127
|
+
currentView.value === 'record' ? (validationStore?.errorsByField ?? {}) : {}
|
|
128
|
+
)
|
|
129
|
+
|
|
108
130
|
// State
|
|
109
131
|
const loading = ref(false)
|
|
110
132
|
const commandPaletteOpen = ref(false)
|
|
@@ -214,20 +236,46 @@ const currentViewData = computed<Record<string, any>>({
|
|
|
214
236
|
// schema fields absent from the record as undefined; writing them would silently
|
|
215
237
|
// clear values that exist in HST. Explicit null is allowed (intentional clear).
|
|
216
238
|
const hstStore = stonecrop.value.getStore()
|
|
239
|
+
const changedFields: string[] = []
|
|
217
240
|
for (const [fieldname, value] of Object.entries(flatData)) {
|
|
218
241
|
if (value === undefined) continue
|
|
219
242
|
const fieldPath = `${currentDoctype.value}.${currentRecordId.value}.${fieldname}`
|
|
220
243
|
const currentValue = hstStore.has(fieldPath) ? hstStore.get(fieldPath) : undefined
|
|
221
244
|
if (currentValue !== value) {
|
|
222
245
|
hstStore.set(fieldPath, value)
|
|
246
|
+
changedFields.push(fieldname)
|
|
223
247
|
}
|
|
224
248
|
}
|
|
249
|
+
|
|
250
|
+
// Advisory field-validation runs on the fields that actually changed (honors each
|
|
251
|
+
// trigger's `on` set). Driven here, after the HST writes, so HST stays value-only.
|
|
252
|
+
if (changedFields.length > 0) {
|
|
253
|
+
driveFieldValidation(changedFields)
|
|
254
|
+
}
|
|
225
255
|
} catch (error) {
|
|
226
256
|
console.warn('HST update failed:', error)
|
|
227
257
|
}
|
|
228
258
|
},
|
|
229
259
|
})
|
|
230
260
|
|
|
261
|
+
// Advisory field-validation: run the doctype's triggers for the fields that changed on this edit.
|
|
262
|
+
// Snapshots the post-edit record as a plain, flat object so validators read siblings read-only
|
|
263
|
+
// (the core store freezes it). Fire-and-forget — the reactive error store repaints AForm on resolve.
|
|
264
|
+
function driveFieldValidation(changedFields: string[]) {
|
|
265
|
+
if (!validationStore || !stonecrop.value || !currentDoctype.value || !currentRecordId.value) return
|
|
266
|
+
|
|
267
|
+
const doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)
|
|
268
|
+
const triggers = doctype?.getTriggers()
|
|
269
|
+
if (!triggers || Object.keys(triggers).length === 0) return
|
|
270
|
+
|
|
271
|
+
const node = stonecrop.value.getRecordById(currentDoctype.value, currentRecordId.value)
|
|
272
|
+
const record = { ...(node?.get('') as Record<string, unknown>) }
|
|
273
|
+
|
|
274
|
+
for (const field of changedFields) {
|
|
275
|
+
void validationStore.validateField(triggers, field, record)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
231
279
|
// Computed properties for current route context.
|
|
232
280
|
// When a routeAdapter is provided it takes full precedence over the registry's internal router.
|
|
233
281
|
const route = computed(() => (routeAdapter ? null : unref(stonecrop.value?.registry.router?.currentRoute)))
|
|
@@ -353,8 +401,10 @@ const getAvailableTransitions = () => {
|
|
|
353
401
|
|
|
354
402
|
// Each transition emits an 'action' event. The host app decides what to do
|
|
355
403
|
// (call the server, trigger an FSM actor, update HST, etc.).
|
|
356
|
-
return transitions.map(({ name
|
|
357
|
-
label
|
|
404
|
+
return transitions.map(({ name }) => ({
|
|
405
|
+
// Prefer the workflow action's human-readable label (WorkflowMeta format);
|
|
406
|
+
// fall back to the raw transition name for XState workflows with no action meta.
|
|
407
|
+
label: doctype.getActionMeta(name)?.label ?? name,
|
|
358
408
|
action: () => {
|
|
359
409
|
emit('action', {
|
|
360
410
|
name,
|
|
@@ -370,6 +420,39 @@ const getAvailableTransitions = () => {
|
|
|
370
420
|
}
|
|
371
421
|
}
|
|
372
422
|
|
|
423
|
+
// Helper: stateless Commands available for the current record — side-effect actions that
|
|
424
|
+
// change no workflow state. Surfaced in the same Actions dropdown as transitions; each emits
|
|
425
|
+
// the same 'action' event, so the host's handler runs a Command's clientHandler identically.
|
|
426
|
+
const getAvailableCommands = () => {
|
|
427
|
+
if (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {
|
|
428
|
+
return []
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
const doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)
|
|
433
|
+
if (!doctype?.workflow) return []
|
|
434
|
+
|
|
435
|
+
const currentState = stonecrop.value.getRecordState(currentDoctype.value, currentRecordId.value)
|
|
436
|
+
const commands = doctype.getAvailableCommands(currentState)
|
|
437
|
+
const recordData = currentViewData.value || {}
|
|
438
|
+
|
|
439
|
+
return commands.map(({ name }) => ({
|
|
440
|
+
label: doctype.getActionMeta(name)?.label ?? name,
|
|
441
|
+
action: () => {
|
|
442
|
+
emit('action', {
|
|
443
|
+
name,
|
|
444
|
+
doctype: currentDoctype.value,
|
|
445
|
+
recordId: currentRecordId.value,
|
|
446
|
+
data: recordData,
|
|
447
|
+
})
|
|
448
|
+
},
|
|
449
|
+
}))
|
|
450
|
+
} catch (error) {
|
|
451
|
+
console.warn('Error getting available commands:', error)
|
|
452
|
+
return []
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
373
456
|
const actionElements = computed(() => {
|
|
374
457
|
const elements: ActionElements[] = []
|
|
375
458
|
|
|
@@ -382,14 +465,14 @@ const actionElements = computed(() => {
|
|
|
382
465
|
})
|
|
383
466
|
break
|
|
384
467
|
case 'record': {
|
|
385
|
-
// Populate the Actions dropdown with every FSM transition
|
|
386
|
-
// record's current state. Clicking
|
|
387
|
-
const
|
|
388
|
-
if (
|
|
468
|
+
// Populate the Actions dropdown with every FSM transition AND stateless Command
|
|
469
|
+
// available in the record's current state. Clicking either emits 'action'.
|
|
470
|
+
const recordActions = [...getAvailableTransitions(), ...getAvailableCommands()]
|
|
471
|
+
if (recordActions.length > 0) {
|
|
389
472
|
elements.push({
|
|
390
473
|
type: 'dropdown',
|
|
391
474
|
label: 'Actions',
|
|
392
|
-
actions:
|
|
475
|
+
actions: recordActions,
|
|
393
476
|
})
|
|
394
477
|
}
|
|
395
478
|
break
|
|
@@ -556,7 +639,7 @@ const getDoctypesSchema = (): ResolvedField[] => {
|
|
|
556
639
|
{
|
|
557
640
|
fieldname: 'doctype',
|
|
558
641
|
label: 'Doctype',
|
|
559
|
-
|
|
642
|
+
component: 'ATextInput',
|
|
560
643
|
align: 'left' as const,
|
|
561
644
|
edit: false,
|
|
562
645
|
width: '20ch',
|
|
@@ -564,7 +647,7 @@ const getDoctypesSchema = (): ResolvedField[] => {
|
|
|
564
647
|
{
|
|
565
648
|
fieldname: 'display_name',
|
|
566
649
|
label: 'Name',
|
|
567
|
-
|
|
650
|
+
component: 'ATextInput',
|
|
568
651
|
align: 'left' as const,
|
|
569
652
|
edit: false,
|
|
570
653
|
width: '30ch',
|
|
@@ -572,7 +655,7 @@ const getDoctypesSchema = (): ResolvedField[] => {
|
|
|
572
655
|
{
|
|
573
656
|
fieldname: 'record_count',
|
|
574
657
|
label: 'Records',
|
|
575
|
-
|
|
658
|
+
component: 'ANumericInput',
|
|
576
659
|
align: 'center' as const,
|
|
577
660
|
edit: false,
|
|
578
661
|
width: '15ch',
|
|
@@ -580,7 +663,7 @@ const getDoctypesSchema = (): ResolvedField[] => {
|
|
|
580
663
|
{
|
|
581
664
|
fieldname: 'actions',
|
|
582
665
|
label: 'Actions',
|
|
583
|
-
|
|
666
|
+
component: 'ATextInput',
|
|
584
667
|
align: 'center' as const,
|
|
585
668
|
edit: false,
|
|
586
669
|
width: '20ch',
|
|
@@ -612,7 +695,7 @@ const getRecordsSchema = (): ResolvedField[] => {
|
|
|
612
695
|
component: 'ATable',
|
|
613
696
|
schema: [
|
|
614
697
|
...(flattenFieldsets(schema) as ColumnSchema[]),
|
|
615
|
-
{ fieldname: 'actions', label: 'Actions',
|
|
698
|
+
{ fieldname: 'actions', label: 'Actions', component: 'ATextInput' },
|
|
616
699
|
],
|
|
617
700
|
config: { view: 'list' as const, fullWidth: true },
|
|
618
701
|
} satisfies ResolvedTable,
|
|
@@ -787,6 +870,12 @@ watch(
|
|
|
787
870
|
{ immediate: true }
|
|
788
871
|
)
|
|
789
872
|
|
|
873
|
+
// Clear advisory validation errors when the target record changes, so errors from a previously
|
|
874
|
+
// edited record never bleed into a newly opened one.
|
|
875
|
+
watch([currentDoctype, currentRecordId], () => {
|
|
876
|
+
validationStore?.clearAll()
|
|
877
|
+
})
|
|
878
|
+
|
|
790
879
|
// Stonecrop reactive computed properties update automatically when the instance
|
|
791
880
|
// becomes available — no manual watcher needed.
|
|
792
881
|
|
package/dist/actions-Bg0Mh5s7.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { L as o, I as c } from "./index-C3nXTrEd.js";
|
|
2
|
-
import { reactive as g } from "vue";
|
|
3
|
-
const l = g([]);
|
|
4
|
-
let f = 0;
|
|
5
|
-
function a(i, e = "info") {
|
|
6
|
-
const t = {
|
|
7
|
-
id: `notif-${++f}`,
|
|
8
|
-
message: i,
|
|
9
|
-
type: e,
|
|
10
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
11
|
-
};
|
|
12
|
-
l.push(t);
|
|
13
|
-
const n = ["warning", "error"].includes(e) ? 8e3 : 5e3;
|
|
14
|
-
setTimeout(() => {
|
|
15
|
-
const r = l.findIndex((d) => d.id === t.id);
|
|
16
|
-
r > -1 && l.splice(r, 1);
|
|
17
|
-
}, n);
|
|
18
|
-
}
|
|
19
|
-
function p(i) {
|
|
20
|
-
const e = l.findIndex((t) => t.id === i);
|
|
21
|
-
e > -1 && l.splice(e, 1);
|
|
22
|
-
}
|
|
23
|
-
o("validateName", (i) => {
|
|
24
|
-
const { fieldname: e, afterValue: t } = i;
|
|
25
|
-
if (!t || typeof t != "string") {
|
|
26
|
-
a(`${e} cannot be empty`, "warning");
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
t.length < 2 ? a(`${e} must be at least 2 characters long`, "warning") : t.length > 50 ? a(`${e} is too long (max 50 characters)`, "warning") : a(`${e} validated successfully`, "success");
|
|
30
|
-
});
|
|
31
|
-
o("updateFullName", (i) => {
|
|
32
|
-
const { path: e, afterValue: t } = i;
|
|
33
|
-
e.split("."), a(`Full name updated due to ${i.fieldname} change to "${t}"`, "info");
|
|
34
|
-
});
|
|
35
|
-
o("validatePhoneFormat", (i) => {
|
|
36
|
-
const { afterValue: e } = i;
|
|
37
|
-
if (!e) return;
|
|
38
|
-
/^\+?[\d\s\-\(\)]+$/.test(e) ? e.length < 10 ? a("Phone number seems too short", "warning") : a("Phone number format is valid", "success") : a("Phone number format is invalid", "error");
|
|
39
|
-
});
|
|
40
|
-
o("notifyPhoneChange", (i) => {
|
|
41
|
-
const { beforeValue: e, afterValue: t } = i;
|
|
42
|
-
e !== t && a(`Phone number changed from "${e || "empty"}" to "${t || "empty"}"`, "info");
|
|
43
|
-
});
|
|
44
|
-
o("validateEmailFormat", (i) => {
|
|
45
|
-
const { afterValue: e } = i;
|
|
46
|
-
if (!e) return;
|
|
47
|
-
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e) ? a("Email format is valid", "success") : a("Email format is invalid", "error");
|
|
48
|
-
});
|
|
49
|
-
o("validateNotes", (i) => {
|
|
50
|
-
const { afterValue: e } = i;
|
|
51
|
-
e && typeof e == "string" && (e.length > 500 ? a("Notes are quite long. Consider keeping them concise.", "warning") : e.length > 50 && a("Good notes provided", "success"));
|
|
52
|
-
});
|
|
53
|
-
o("validateSubject", (i) => {
|
|
54
|
-
const { afterValue: e } = i;
|
|
55
|
-
if (!e || typeof e != "string") {
|
|
56
|
-
a("Issue subject is required", "error");
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
e.length < 5 ? a("Issue subject should be more descriptive (min 5 characters)", "warning") : e.length > 100 ? a("Issue subject is too long (max 100 characters)", "warning") : a("Issue subject looks good", "success");
|
|
60
|
-
});
|
|
61
|
-
o("onStatusChange", (i) => {
|
|
62
|
-
const { beforeValue: e, afterValue: t } = i;
|
|
63
|
-
e !== t && (a(`Issue status changed from "${e || "none"}" to "${t}"`, "info"), t === "Resolved" ? a("🎉 Issue marked as resolved! Consider adding resolution notes.", "success") : t === "Closed" ? a("📁 Issue closed. Thank you for your work!", "info") : t === "In Progress" && a("⚡ Issue is now in progress. Good luck!", "info"));
|
|
64
|
-
});
|
|
65
|
-
o("onPriorityChange", (i) => {
|
|
66
|
-
const { beforeValue: e, afterValue: t } = i;
|
|
67
|
-
e !== t && (a(`Priority changed from "${e || "none"}" to "${t}"`, "info"), t === "Critical" ? a("🚨 CRITICAL priority set! This needs immediate attention.", "warning") : t === "High" && a("⚠️ High priority issue - please address soon.", "warning"));
|
|
68
|
-
});
|
|
69
|
-
o("validatePriorityStatus", (i) => {
|
|
70
|
-
const { afterValue: e } = i;
|
|
71
|
-
e === "Critical" && a("Critical priority issues should have immediate attention", "info");
|
|
72
|
-
});
|
|
73
|
-
o("validateDescription", (i) => {
|
|
74
|
-
const { afterValue: e } = i;
|
|
75
|
-
e && typeof e == "string" && (e.length > 1e3 ? a("Description is quite long. Consider breaking it into smaller sections.", "info") : e.length > 50 && a("Good detailed description provided", "success"));
|
|
76
|
-
});
|
|
77
|
-
o("validateFutureDate", (i) => {
|
|
78
|
-
const { afterValue: e } = i;
|
|
79
|
-
if (e) {
|
|
80
|
-
const t = new Date(e), n = /* @__PURE__ */ new Date();
|
|
81
|
-
n.setHours(0, 0, 0, 0), t < n ? a("⚠️ Selected date is in the past", "warning") : t.getTime() === n.getTime() ? a("📅 Due date is today", "info") : a("📅 Future date selected", "success");
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
o("updateTimestamp", (i) => {
|
|
85
|
-
a(`Record timestamp updated due to ${i.fieldname} change`, "info");
|
|
86
|
-
});
|
|
87
|
-
o("logFieldChange", (i) => {
|
|
88
|
-
const { path: e, fieldname: t, beforeValue: n, afterValue: r, operation: d, timestamp: u } = i;
|
|
89
|
-
console.log("🔄 Field Change Log:", {
|
|
90
|
-
path: e,
|
|
91
|
-
field: t,
|
|
92
|
-
from: n,
|
|
93
|
-
to: r,
|
|
94
|
-
operation: d,
|
|
95
|
-
timestamp: u.toISOString()
|
|
96
|
-
}), a(`Field "${t}" changed`, "info");
|
|
97
|
-
});
|
|
98
|
-
const s = c();
|
|
99
|
-
s && (s.registerDoctypeActions(
|
|
100
|
-
"todo-form",
|
|
101
|
-
/* @__PURE__ */ new Map([
|
|
102
|
-
["first_name", ["validateName", "updateFullName", "logFieldChange"]],
|
|
103
|
-
["last_name", ["validateName", "updateFullName", "logFieldChange"]],
|
|
104
|
-
["phone", ["validatePhoneFormat", "notifyPhoneChange", "logFieldChange"]],
|
|
105
|
-
["email", ["validateEmailFormat", "logFieldChange"]],
|
|
106
|
-
["notes", ["validateNotes", "logFieldChange"]]
|
|
107
|
-
])
|
|
108
|
-
), s.registerDoctypeActions(
|
|
109
|
-
"todo-list",
|
|
110
|
-
/* @__PURE__ */ new Map([
|
|
111
|
-
["first_name", ["validateName", "updateFullName", "logFieldChange"]],
|
|
112
|
-
["last_name", ["validateName", "updateFullName", "logFieldChange"]],
|
|
113
|
-
["phone", ["validatePhoneFormat", "notifyPhoneChange", "logFieldChange"]]
|
|
114
|
-
])
|
|
115
|
-
), s.registerDoctypeActions(
|
|
116
|
-
"issue-form",
|
|
117
|
-
/* @__PURE__ */ new Map([
|
|
118
|
-
["subject", ["validateSubject", "logFieldChange"]],
|
|
119
|
-
["status", ["onStatusChange", "validatePriorityStatus", "updateTimestamp", "logFieldChange"]],
|
|
120
|
-
["priority", ["onPriorityChange", "validatePriorityStatus", "logFieldChange"]],
|
|
121
|
-
["description", ["validateDescription", "logFieldChange"]],
|
|
122
|
-
["due_date", ["validateFutureDate", "updateTimestamp", "logFieldChange"]]
|
|
123
|
-
])
|
|
124
|
-
), s.registerDoctypeActions(
|
|
125
|
-
"issue-list",
|
|
126
|
-
/* @__PURE__ */ new Map([
|
|
127
|
-
["subject", ["validateSubject", "logFieldChange"]],
|
|
128
|
-
["status", ["onStatusChange", "logFieldChange"]],
|
|
129
|
-
["priority", ["onPriorityChange", "logFieldChange"]]
|
|
130
|
-
])
|
|
131
|
-
));
|
|
132
|
-
export {
|
|
133
|
-
a as addNotification,
|
|
134
|
-
l as notifications,
|
|
135
|
-
p as removeNotification
|
|
136
|
-
};
|
|
137
|
-
//# sourceMappingURL=actions-Bg0Mh5s7.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"actions-Bg0Mh5s7.js","sources":["../../examples/desktop/actions.ts"],"sourcesContent":["/**\n * Field trigger action functions for the Desktop example\n * This demonstrates the field trigger system in action\n */\nimport { registerGlobalAction, getGlobalTriggerEngine, type FieldChangeContext } from '@stonecrop/stonecrop'\nimport { reactive } from 'vue'\n\n// ============= DEBUGGING AND NOTIFICATIONS =============\n\n// Notification interface\ninterface Notification {\n\tid: string\n\tmessage: string\n\ttype: 'info' | 'success' | 'warning' | 'error'\n\ttimestamp: Date\n}\n\n// Reactive notifications array for UI\nexport const notifications = reactive<Notification[]>([])\n\nlet notificationId = 0\n\nexport function addNotification(message: string, type: 'info' | 'success' | 'warning' | 'error' = 'info') {\n\tconst notification = {\n\t\tid: `notif-${++notificationId}`,\n\t\tmessage,\n\t\ttype,\n\t\ttimestamp: new Date(),\n\t}\n\tnotifications.push(notification)\n\n\t// Auto-remove after 5 seconds for info/success, 8 seconds for warnings/errors\n\tconst timeout = ['warning', 'error'].includes(type) ? 8000 : 5000\n\tsetTimeout(() => {\n\t\tconst index = notifications.findIndex(n => n.id === notification.id)\n\t\tif (index > -1) {\n\t\t\tnotifications.splice(index, 1)\n\t\t}\n\t}, timeout)\n}\n\nexport function removeNotification(id: string) {\n\tconst index = notifications.findIndex(n => n.id === id)\n\tif (index > -1) {\n\t\tnotifications.splice(index, 1)\n\t}\n}\n\n// ============= NAME VALIDATION ACTIONS =============\n\nregisterGlobalAction('validateName', (context: FieldChangeContext) => {\n\tconst { fieldname, afterValue } = context\n\n\tif (!afterValue || typeof afterValue !== 'string') {\n\t\taddNotification(`${fieldname} cannot be empty`, 'warning')\n\t\treturn\n\t}\n\n\tif (afterValue.length < 2) {\n\t\taddNotification(`${fieldname} must be at least 2 characters long`, 'warning')\n\t} else if (afterValue.length > 50) {\n\t\taddNotification(`${fieldname} is too long (max 50 characters)`, 'warning')\n\t} else {\n\t\taddNotification(`${fieldname} validated successfully`, 'success')\n\t}\n})\n\nregisterGlobalAction('updateFullName', (context: FieldChangeContext) => {\n\tconst { path, afterValue } = context\n\tconst pathParts = path.split('.')\n\n\t// This would typically update a computed full_name field\n\t// For demo purposes, we'll just show a notification\n\taddNotification(`Full name updated due to ${context.fieldname} change to \"${afterValue}\"`, 'info')\n})\n\n// ============= PHONE VALIDATION ACTIONS =============\n\nregisterGlobalAction('validatePhoneFormat', (context: FieldChangeContext) => {\n\tconst { afterValue } = context\n\n\tif (!afterValue) return\n\n\t// Simple phone validation - you could use a more sophisticated library\n\tconst phoneRegex = /^\\+?[\\d\\s\\-\\(\\)]+$/\n\n\tif (!phoneRegex.test(afterValue)) {\n\t\taddNotification('Phone number format is invalid', 'error')\n\t} else if (afterValue.length < 10) {\n\t\taddNotification('Phone number seems too short', 'warning')\n\t} else {\n\t\taddNotification('Phone number format is valid', 'success')\n\t}\n})\n\nregisterGlobalAction('notifyPhoneChange', (context: FieldChangeContext) => {\n\tconst { beforeValue, afterValue } = context\n\n\tif (beforeValue !== afterValue) {\n\t\taddNotification(`Phone number changed from \"${beforeValue || 'empty'}\" to \"${afterValue || 'empty'}\"`, 'info')\n\t}\n})\n\n// ============= EMAIL VALIDATION ACTIONS =============\n\nregisterGlobalAction('validateEmailFormat', (context: FieldChangeContext) => {\n\tconst { afterValue } = context\n\n\tif (!afterValue) return\n\n\t// Simple email validation\n\tconst emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n\tif (!emailRegex.test(afterValue)) {\n\t\taddNotification('Email format is invalid', 'error')\n\t} else {\n\t\taddNotification('Email format is valid', 'success')\n\t}\n})\n\n// ============= NOTES VALIDATION ACTIONS =============\n\nregisterGlobalAction('validateNotes', (context: FieldChangeContext) => {\n\tconst { afterValue } = context\n\n\tif (afterValue && typeof afterValue === 'string') {\n\t\tif (afterValue.length > 500) {\n\t\t\taddNotification('Notes are quite long. Consider keeping them concise.', 'warning')\n\t\t} else if (afterValue.length > 50) {\n\t\t\taddNotification('Good notes provided', 'success')\n\t\t}\n\t}\n})\n\n// ============= ISSUE-SPECIFIC ACTIONS =============\n\nregisterGlobalAction('validateSubject', (context: FieldChangeContext) => {\n\tconst { afterValue } = context\n\n\tif (!afterValue || typeof afterValue !== 'string') {\n\t\taddNotification('Issue subject is required', 'error')\n\t\treturn\n\t}\n\n\tif (afterValue.length < 5) {\n\t\taddNotification('Issue subject should be more descriptive (min 5 characters)', 'warning')\n\t} else if (afterValue.length > 100) {\n\t\taddNotification('Issue subject is too long (max 100 characters)', 'warning')\n\t} else {\n\t\taddNotification('Issue subject looks good', 'success')\n\t}\n})\n\nregisterGlobalAction('onStatusChange', (context: FieldChangeContext) => {\n\tconst { beforeValue, afterValue } = context\n\n\tif (beforeValue !== afterValue) {\n\t\taddNotification(`Issue status changed from \"${beforeValue || 'none'}\" to \"${afterValue}\"`, 'info')\n\n\t\t// Demo: Automatic actions based on status\n\t\tif (afterValue === 'Resolved') {\n\t\t\taddNotification('🎉 Issue marked as resolved! Consider adding resolution notes.', 'success')\n\t\t} else if (afterValue === 'Closed') {\n\t\t\taddNotification('📁 Issue closed. Thank you for your work!', 'info')\n\t\t} else if (afterValue === 'In Progress') {\n\t\t\taddNotification('⚡ Issue is now in progress. Good luck!', 'info')\n\t\t}\n\t}\n})\n\nregisterGlobalAction('onPriorityChange', (context: FieldChangeContext) => {\n\tconst { beforeValue, afterValue } = context\n\n\tif (beforeValue !== afterValue) {\n\t\taddNotification(`Priority changed from \"${beforeValue || 'none'}\" to \"${afterValue}\"`, 'info')\n\n\t\tif (afterValue === 'Critical') {\n\t\t\taddNotification('🚨 CRITICAL priority set! This needs immediate attention.', 'warning')\n\t\t} else if (afterValue === 'High') {\n\t\t\taddNotification('⚠️ High priority issue - please address soon.', 'warning')\n\t\t}\n\t}\n})\n\nregisterGlobalAction('validatePriorityStatus', (context: FieldChangeContext) => {\n\t// This would typically check other fields in the record\n\t// For demo, we'll just validate the priority itself\n\tconst { afterValue } = context\n\n\tif (afterValue === 'Critical') {\n\t\t// In a real app, you might check if status is appropriate for critical priority\n\t\taddNotification('Critical priority issues should have immediate attention', 'info')\n\t}\n})\n\nregisterGlobalAction('validateDescription', (context: FieldChangeContext) => {\n\tconst { afterValue } = context\n\n\tif (afterValue && typeof afterValue === 'string') {\n\t\tif (afterValue.length > 1000) {\n\t\t\taddNotification('Description is quite long. Consider breaking it into smaller sections.', 'info')\n\t\t} else if (afterValue.length > 50) {\n\t\t\taddNotification('Good detailed description provided', 'success')\n\t\t}\n\t}\n})\n\nregisterGlobalAction('validateFutureDate', (context: FieldChangeContext) => {\n\tconst { afterValue } = context\n\n\tif (afterValue) {\n\t\tconst selectedDate = new Date(afterValue)\n\t\tconst today = new Date()\n\t\ttoday.setHours(0, 0, 0, 0) // Reset time for date comparison\n\n\t\tif (selectedDate < today) {\n\t\t\taddNotification('⚠️ Selected date is in the past', 'warning')\n\t\t} else if (selectedDate.getTime() === today.getTime()) {\n\t\t\taddNotification('📅 Due date is today', 'info')\n\t\t} else {\n\t\t\taddNotification('📅 Future date selected', 'success')\n\t\t}\n\t}\n})\n\n// ============= GENERIC ACTIONS =============\n\nregisterGlobalAction('updateTimestamp', (context: FieldChangeContext) => {\n\t// This would typically update a \"modified\" or \"last_updated\" field\n\taddNotification(`Record timestamp updated due to ${context.fieldname} change`, 'info')\n})\n\nregisterGlobalAction('logFieldChange', (context: FieldChangeContext) => {\n\tconst { path, fieldname, beforeValue, afterValue, operation, timestamp } = context\n\n\tconsole.log('🔄 Field Change Log:', {\n\t\tpath,\n\t\tfield: fieldname,\n\t\tfrom: beforeValue,\n\t\tto: afterValue,\n\t\toperation,\n\t\ttimestamp: timestamp.toISOString(),\n\t})\n\n\t// Show a subtle notification for field changes\n\taddNotification(`Field \"${fieldname}\" changed`, 'info')\n})\n\n// ============= DOCTYPE FIELD TRIGGER REGISTRATION =============\n// Register field triggers for actual doctype names that HST will use\n// HST should generate paths like \"todo-form.123.first_name\" using doctype.slug\n\nconst engine = getGlobalTriggerEngine()\nif (engine) {\n\t// Register field triggers for actual doctype names from the server\n\t// These match the doctype names defined in server.ts\n\n\t// Todo form field triggers (doctype: \"todo-form\")\n\tengine.registerDoctypeActions(\n\t\t'todo-form',\n\t\tnew Map([\n\t\t\t['first_name', ['validateName', 'updateFullName', 'logFieldChange']],\n\t\t\t['last_name', ['validateName', 'updateFullName', 'logFieldChange']],\n\t\t\t['phone', ['validatePhoneFormat', 'notifyPhoneChange', 'logFieldChange']],\n\t\t\t['email', ['validateEmailFormat', 'logFieldChange']],\n\t\t\t['notes', ['validateNotes', 'logFieldChange']],\n\t\t])\n\t)\n\n\t// Todo list field triggers (doctype: \"todo-list\")\n\tengine.registerDoctypeActions(\n\t\t'todo-list',\n\t\tnew Map([\n\t\t\t['first_name', ['validateName', 'updateFullName', 'logFieldChange']],\n\t\t\t['last_name', ['validateName', 'updateFullName', 'logFieldChange']],\n\t\t\t['phone', ['validatePhoneFormat', 'notifyPhoneChange', 'logFieldChange']],\n\t\t])\n\t)\n\n\t// Issue form field triggers (doctype: \"issue-form\")\n\tengine.registerDoctypeActions(\n\t\t'issue-form',\n\t\tnew Map([\n\t\t\t['subject', ['validateSubject', 'logFieldChange']],\n\t\t\t['status', ['onStatusChange', 'validatePriorityStatus', 'updateTimestamp', 'logFieldChange']],\n\t\t\t['priority', ['onPriorityChange', 'validatePriorityStatus', 'logFieldChange']],\n\t\t\t['description', ['validateDescription', 'logFieldChange']],\n\t\t\t['due_date', ['validateFutureDate', 'updateTimestamp', 'logFieldChange']],\n\t\t])\n\t)\n\n\t// Issue list field triggers (doctype: \"issue-list\")\n\tengine.registerDoctypeActions(\n\t\t'issue-list',\n\t\tnew Map([\n\t\t\t['subject', ['validateSubject', 'logFieldChange']],\n\t\t\t['status', ['onStatusChange', 'logFieldChange']],\n\t\t\t['priority', ['onPriorityChange', 'logFieldChange']],\n\t\t])\n\t)\n}\n"],"names":["notifications","reactive","notificationId","addNotification","message","type","notification","timeout","index","n","removeNotification","id","registerGlobalAction","context","fieldname","afterValue","path","beforeValue","selectedDate","today","operation","timestamp","engine","getGlobalTriggerEngine"],"mappings":"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"}
|