@wishbone-media/spark 0.32.0 → 0.33.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishbone-media/spark",
3
- "version": "0.32.0",
3
+ "version": "0.33.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/*",
@@ -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
+ }
@@ -24,6 +24,7 @@ import { parseLaravelErrors, getFormLevelMessage, isValidationError } from '../u
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!',
@@ -152,9 +153,28 @@ export function useFormSubmission(options = {}) {
152
153
  })
153
154
  }
154
155
 
156
+ /**
157
+ * Convenience method for standard CRUD form submissions.
158
+ * Automatically constructs URL and HTTP method from endpoint + resourceId.
159
+ * Requires `endpoint` option to be set.
160
+ *
161
+ * @param {Object} config - Submission configuration
162
+ * @param {Object} config.payload - Request payload
163
+ * @param {Object} config.node - FormKit node
164
+ * @param {string|number} config.resourceId - Resource ID (for edit mode)
165
+ * @param {boolean} config.isEditMode - Whether this is an update
166
+ */
167
+ async function submitCrud(config) {
168
+ const { payload, node, resourceId, isEditMode } = config
169
+ const url = isEditMode ? `/${endpoint}/${resourceId}` : `/${endpoint}`
170
+ const method = isEditMode ? 'put' : 'post'
171
+ return submitToApi({ url, payload, method, node, isEditMode })
172
+ }
173
+
155
174
  return {
156
175
  submitting,
157
176
  submit,
158
177
  submitToApi,
178
+ ...(endpoint ? { submitCrud } : {}),
159
179
  }
160
180
  }
@@ -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'
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './components/index.js'
2
2
  export * from './composables/index.js'
3
+ export * from './constants/index.js'
3
4
  export * from './containers/index.js'
4
5
  export * from './plugins/index.js'
5
6
  export * from './stores/index.js'