@wishbone-media/spark 0.32.0 → 0.34.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/index.d.ts +1 -0
- package/dist/index.js +1134 -1056
- package/package.json +3 -1
- package/src/composables/index.js +1 -0
- package/src/composables/useCrudResource.js +109 -0
- package/src/composables/useFormSubmission.js +39 -16
- package/src/constants/australianStates.js +14 -0
- package/src/constants/index.js +1 -0
- package/src/index.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wishbone-media/spark",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@vitejs/plugin-vue": "^6.0.2",
|
|
42
42
|
"vite": "^7.2.2",
|
|
43
|
+
"vite-plugin-dts": "^4.5.4",
|
|
43
44
|
"vue": "^3.5.24"
|
|
44
45
|
},
|
|
45
46
|
"packageManager": "pnpm@10.11.1",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"release:minor": "npm version minor && npm publish && git push --follow-tags",
|
|
50
51
|
"release:major": "npm version major && npm publish && git push --follow-tags"
|
|
51
52
|
},
|
|
53
|
+
"types": "./dist/index.d.ts",
|
|
52
54
|
"exports": {
|
|
53
55
|
".": "./dist/index.js",
|
|
54
56
|
"./src/*": "./src/*",
|
package/src/composables/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { sparkOverlayService } from './sparkOverlayService.js'
|
|
|
4
4
|
export { useSparkOverlay } from './useSparkOverlay.js'
|
|
5
5
|
export { useSparkTableRouteSync } from './useSparkTableRouteSync.js'
|
|
6
6
|
export { useFormSubmission } from './useFormSubmission.js'
|
|
7
|
+
export { useCrudResource, useTableDelete } from './useCrudResource.js'
|
|
7
8
|
export { useSubNavigation } from './useSubNavigation.js'
|
|
8
9
|
export {
|
|
9
10
|
useFormDirtyGuard,
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ref, computed, onMounted } from 'vue'
|
|
2
|
+
import { useRoute } from 'vue-router'
|
|
3
|
+
import { getAxiosInstance } from '../plugins/axios.js'
|
|
4
|
+
import { sparkNotificationService } from './sparkNotificationService.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Composable for CRUD Save views — handles resource fetching, error state, and edit mode detection.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} endpoint - API endpoint name (e.g. 'tributes', 'public-holidays')
|
|
10
|
+
* @param {Object} options
|
|
11
|
+
* @param {string} options.queryParams - Optional query string for eager loading (e.g. 'with=brand.items')
|
|
12
|
+
* @param {Function} options.fetchFn - Custom fetch function. Receives { axios, resourceId, isEditMode, route }.
|
|
13
|
+
* Return value is assigned to data ref. Return undefined to skip assignment.
|
|
14
|
+
* @param {boolean} options.fetchOnCreate - Whether to call fetch() on mount in create mode (default: false)
|
|
15
|
+
* @returns {{ data, error, resourceId, isEditMode, fetch, handleSuccess }}
|
|
16
|
+
*/
|
|
17
|
+
export function useCrudResource(endpoint, options = {}) {
|
|
18
|
+
const { queryParams = '', fetchFn = null, fetchOnCreate = false } = options
|
|
19
|
+
const route = useRoute()
|
|
20
|
+
|
|
21
|
+
const data = ref(null)
|
|
22
|
+
const error = ref(null)
|
|
23
|
+
|
|
24
|
+
const resourceId = computed(() => route.params.id ?? null)
|
|
25
|
+
const isEditMode = computed(() => resourceId.value !== null)
|
|
26
|
+
|
|
27
|
+
async function fetch() {
|
|
28
|
+
if (!fetchOnCreate && !isEditMode.value) return
|
|
29
|
+
error.value = null
|
|
30
|
+
try {
|
|
31
|
+
if (fetchFn) {
|
|
32
|
+
const result = await fetchFn({
|
|
33
|
+
axios: getAxiosInstance(),
|
|
34
|
+
resourceId: resourceId.value,
|
|
35
|
+
isEditMode: isEditMode.value,
|
|
36
|
+
route,
|
|
37
|
+
})
|
|
38
|
+
if (result !== undefined) data.value = result
|
|
39
|
+
} else {
|
|
40
|
+
const axios = getAxiosInstance()
|
|
41
|
+
let url = `/${endpoint}/${resourceId.value}`
|
|
42
|
+
if (queryParams) url += `?${queryParams}`
|
|
43
|
+
const response = await axios.get(url)
|
|
44
|
+
data.value = response.data
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
error.value = err.response?.data?.message || err.message || 'Failed to load data.'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function handleSuccess() {
|
|
52
|
+
if (isEditMode.value) fetch()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onMounted(() => {
|
|
56
|
+
if (fetchOnCreate || isEditMode.value) fetch()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return { data, error, resourceId, isEditMode, fetch, handleSuccess }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Composable for SparkTable row deletion — handles axios delete, notifications, and table refresh.
|
|
64
|
+
*
|
|
65
|
+
* @param {Ref} tableRef - Template ref to the SparkTable component (used for .refresh())
|
|
66
|
+
* @param {Object} options
|
|
67
|
+
* @param {string} options.endpoint - API endpoint name (e.g. 'public-holidays', 'invoices')
|
|
68
|
+
* @param {string} options.successMessage - Notification on success (default: 'Deleted successfully.')
|
|
69
|
+
* @param {string} options.errorMessage - Notification on error (default: API message or 'Failed to delete.')
|
|
70
|
+
* @param {Function} options.onSuccess - Callback after successful delete (receives row)
|
|
71
|
+
* @param {Function} options.onError - Callback on error (receives error, row). Return true to skip default handling.
|
|
72
|
+
* @returns {{ handleDelete }}
|
|
73
|
+
*/
|
|
74
|
+
export function useTableDelete(tableRef, options = {}) {
|
|
75
|
+
const {
|
|
76
|
+
endpoint,
|
|
77
|
+
successMessage,
|
|
78
|
+
errorMessage,
|
|
79
|
+
onSuccess,
|
|
80
|
+
onError,
|
|
81
|
+
} = options
|
|
82
|
+
|
|
83
|
+
async function handleDelete(row) {
|
|
84
|
+
try {
|
|
85
|
+
const axios = getAxiosInstance()
|
|
86
|
+
await axios.delete(`/${endpoint}/${row.id}`)
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (onError) {
|
|
89
|
+
const handled = onError(err, row)
|
|
90
|
+
if (handled) return
|
|
91
|
+
}
|
|
92
|
+
sparkNotificationService.show({
|
|
93
|
+
type: 'danger',
|
|
94
|
+
message: errorMessage || err.response?.data?.message || 'Failed to delete.',
|
|
95
|
+
})
|
|
96
|
+
console.error(err)
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
sparkNotificationService.show({
|
|
101
|
+
type: 'success',
|
|
102
|
+
message: successMessage || 'Deleted successfully.',
|
|
103
|
+
})
|
|
104
|
+
if (onSuccess) onSuccess(row)
|
|
105
|
+
if (tableRef?.value?.refresh) tableRef.value.refresh()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return { handleDelete }
|
|
109
|
+
}
|
|
@@ -18,12 +18,13 @@ import { parseLaravelErrors, getFormLevelMessage, isValidationError } from '../u
|
|
|
18
18
|
* @param {Function} options.onError - Callback after error (receives error, can return true to skip default handling)
|
|
19
19
|
* @param {boolean} options.showNotification - Whether to show success notification (default: true)
|
|
20
20
|
* @param {boolean} options.setFieldErrors - Whether to set field-level errors on 422 (default: true)
|
|
21
|
-
* @param {boolean} options.setFormErrors - Whether to show form-level error notification on 422 (default:
|
|
21
|
+
* @param {boolean} options.setFormErrors - Whether to show form-level error notification on 422 (default: true)
|
|
22
22
|
*
|
|
23
23
|
* @returns {Object} Form submission helpers
|
|
24
24
|
*/
|
|
25
25
|
export function useFormSubmission(options = {}) {
|
|
26
26
|
const {
|
|
27
|
+
endpoint = null,
|
|
27
28
|
successMessage = 'Saved successfully!',
|
|
28
29
|
createMessage = 'Created successfully!',
|
|
29
30
|
updateMessage = 'Updated successfully!',
|
|
@@ -32,7 +33,7 @@ export function useFormSubmission(options = {}) {
|
|
|
32
33
|
onError = null,
|
|
33
34
|
showNotification = true,
|
|
34
35
|
setFieldErrors = true,
|
|
35
|
-
setFormErrors =
|
|
36
|
+
setFormErrors = true,
|
|
36
37
|
} = options
|
|
37
38
|
|
|
38
39
|
const router = useRouter()
|
|
@@ -63,20 +64,6 @@ export function useFormSubmission(options = {}) {
|
|
|
63
64
|
const response = await submitFn()
|
|
64
65
|
const data = response.data
|
|
65
66
|
|
|
66
|
-
// Show success notification
|
|
67
|
-
if (showNotification) {
|
|
68
|
-
const message = isEditMode
|
|
69
|
-
? updateMessage
|
|
70
|
-
: method === 'create'
|
|
71
|
-
? createMessage
|
|
72
|
-
: successMessage
|
|
73
|
-
|
|
74
|
-
sparkNotificationService.show({
|
|
75
|
-
type: 'success',
|
|
76
|
-
message,
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
|
|
80
67
|
// Execute success callback
|
|
81
68
|
if (onSuccess) {
|
|
82
69
|
await onSuccess(data)
|
|
@@ -90,6 +77,21 @@ export function useFormSubmission(options = {}) {
|
|
|
90
77
|
await router.push(location)
|
|
91
78
|
}
|
|
92
79
|
|
|
80
|
+
// Show success notification (after redirect so it appears on the destination page
|
|
81
|
+
// and isn't cleared by SparkNotificationOutlet's route-change detection)
|
|
82
|
+
if (showNotification) {
|
|
83
|
+
const message = isEditMode
|
|
84
|
+
? updateMessage
|
|
85
|
+
: method === 'create'
|
|
86
|
+
? createMessage
|
|
87
|
+
: successMessage
|
|
88
|
+
|
|
89
|
+
sparkNotificationService.toast({
|
|
90
|
+
type: 'success',
|
|
91
|
+
message,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
93
95
|
return { success: true, data, error: null }
|
|
94
96
|
} catch (error) {
|
|
95
97
|
const response = error.response
|
|
@@ -115,6 +117,7 @@ export function useFormSubmission(options = {}) {
|
|
|
115
117
|
type: 'danger',
|
|
116
118
|
message: getFormLevelMessage(response),
|
|
117
119
|
})
|
|
120
|
+
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
118
121
|
}
|
|
119
122
|
} else {
|
|
120
123
|
// Handle non-validation errors
|
|
@@ -122,6 +125,7 @@ export function useFormSubmission(options = {}) {
|
|
|
122
125
|
type: 'danger',
|
|
123
126
|
message: getFormLevelMessage(response),
|
|
124
127
|
})
|
|
128
|
+
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
return { success: false, data: null, error }
|
|
@@ -152,9 +156,28 @@ export function useFormSubmission(options = {}) {
|
|
|
152
156
|
})
|
|
153
157
|
}
|
|
154
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Convenience method for standard CRUD form submissions.
|
|
161
|
+
* Automatically constructs URL and HTTP method from endpoint + resourceId.
|
|
162
|
+
* Requires `endpoint` option to be set.
|
|
163
|
+
*
|
|
164
|
+
* @param {Object} config - Submission configuration
|
|
165
|
+
* @param {Object} config.payload - Request payload
|
|
166
|
+
* @param {Object} config.node - FormKit node
|
|
167
|
+
* @param {string|number} config.resourceId - Resource ID (for edit mode)
|
|
168
|
+
* @param {boolean} config.isEditMode - Whether this is an update
|
|
169
|
+
*/
|
|
170
|
+
async function submitCrud(config) {
|
|
171
|
+
const { payload, node, resourceId, isEditMode } = config
|
|
172
|
+
const url = isEditMode ? `/${endpoint}/${resourceId}` : `/${endpoint}`
|
|
173
|
+
const method = isEditMode ? 'put' : 'post'
|
|
174
|
+
return submitToApi({ url, payload, method, node, isEditMode })
|
|
175
|
+
}
|
|
176
|
+
|
|
155
177
|
return {
|
|
156
178
|
submitting,
|
|
157
179
|
submit,
|
|
158
180
|
submitToApi,
|
|
181
|
+
...(endpoint ? { submitCrud } : {}),
|
|
159
182
|
}
|
|
160
183
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Australian state/territory options for use in select dropdowns and filter controls.
|
|
3
|
+
* Each entry has { label, value } shape compatible with FormKit selects and SparkTable filters.
|
|
4
|
+
*/
|
|
5
|
+
export const AUSTRALIAN_STATES = [
|
|
6
|
+
{ label: 'Australian Capital Territory', value: 'ACT' },
|
|
7
|
+
{ label: 'New South Wales', value: 'NSW' },
|
|
8
|
+
{ label: 'Northern Territory', value: 'NT' },
|
|
9
|
+
{ label: 'Queensland', value: 'QLD' },
|
|
10
|
+
{ label: 'South Australia', value: 'SA' },
|
|
11
|
+
{ label: 'Tasmania', value: 'TAS' },
|
|
12
|
+
{ label: 'Victoria', value: 'VIC' },
|
|
13
|
+
{ label: 'Western Australia', value: 'WA' },
|
|
14
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AUSTRALIAN_STATES } from './australianStates.js'
|