pgo-ui 1.0.21 → 1.0.23

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": "pgo-ui",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "description": "A Vue 3 component library with PGO design system",
5
5
  "private": false,
6
6
  "type": "module",
@@ -90,12 +90,12 @@ const { language } = inject('i18n')
90
90
 
91
91
  //footer props
92
92
  footerClass: { type: String, default: '' },
93
- footerBg: { type: String, default: '' },
93
+ footerBg: { type: String, default: 'vts-bg-surface' },
94
94
  footerText: { type: String, default: 'text-input-text' },
95
95
  footerTextSize: { type: String, default: '' },
96
96
  footerBd: { type: String, default: '' },
97
97
  footerMargin: { type: String, default: '' },
98
- footerPadding: { type: String, default: 'px-6 py-4' },
98
+ footerPadding: { type: String, default: 'px-4 py-4' },
99
99
 
100
100
  })
101
101
 
@@ -388,6 +388,9 @@ import ConfirmationModal from './ConfirmationModal.vue'
388
388
 
389
389
  import { textAlign, initializeFunctions } from '../../pgo-components/lib/componentConfig.js'
390
390
 
391
+ const api = inject('api')
392
+ const snackbar = inject('snackbar')
393
+
391
394
  const props = defineProps({
392
395
  // Data
393
396
  items: {
@@ -658,8 +661,8 @@ const initializeAllFunctions = () => {
658
661
  emit,
659
662
  props,
660
663
  console,
661
- snackbar: inject('snackbar', null),
662
- api: inject('api', null),
664
+ snackbar: snackbar,
665
+ api: api,
663
666
  this: {
664
667
  variables: tableVariables
665
668
  }
@@ -888,11 +891,8 @@ const confirmUpdate = async () => {
888
891
 
889
892
  // If updateUrl is provided, send API request
890
893
  if (props.updateUrl) {
891
- const api = inject('api', null)
892
- if (api) {
893
894
  const url = `${props.updateUrl}/${item[props.itemKey]}`
894
895
  await api.patch(url, updateData)
895
- }
896
896
  }
897
897
 
898
898
  // Update local data
@@ -45,28 +45,29 @@
45
45
  @focus="handleFocus"
46
46
  @blur="handleBlur"
47
47
  />
48
- <Button
49
- type="button"
50
- label="buttons.search"
51
- variant="tonal"
52
- prepend-icon="magnifying-glass"
53
- size="xs"
54
- icon-type="soild"
55
- :icon-rotate="props.iconRotate ? props.iconRotate : (computedLang === 'dv' ? 90 : 0)"
56
- :disabled="disabled || loading"
57
- @click="handleSubmit"
58
- />
48
+
59
49
  <!-- :icon-rotate="props.iconRotate ?? (computedLang === 'dv' ? 180 : 0)" -->
60
50
  </div>
61
51
  </template>
62
52
 
63
53
 
64
54
  <!-- Loading spinner in append slot -->
65
- <template v-if="loading" #append>
66
- <svg class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
55
+ <template #append>
56
+ <svg v-if="loading" class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
67
57
  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
68
58
  <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 714 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
69
59
  </svg>
60
+ <Button
61
+ type="button"
62
+ label="buttons.search"
63
+ variant="tonal"
64
+ prepend-icon="magnifying-glass"
65
+ size="xs"
66
+ icon-type="soild"
67
+ :icon-rotate="props.iconRotate ? props.iconRotate : (computedLang === 'dv' ? 90 : 0)"
68
+ :disabled="disabled || loading"
69
+ @click="handleSubmit"
70
+ />
70
71
  </template>
71
72
  </Base>
72
73
  </div>
@@ -46,6 +46,7 @@
46
46
  <HeroIcon :size="iconSizes[size]" :name="append" :type="iconType == 'outline' ? 'outline' : 'solid'" />
47
47
  </slot>
48
48
  </div>
49
+ <!-- <slot name="" -->
49
50
  </div>
50
51
  </div>
51
52
  <div v-if="messagesToShow.length" :class="['.vts-mt-1 text-xs', selectLanguage === 'dv' ? 'text-right' : 'text-left', 'text-input-text']">
@@ -3,11 +3,12 @@
3
3
  v-model="open"
4
4
  persistent
5
5
  padding="p-3"
6
+ :bg="bg"
6
7
  >
7
8
  <Banner
8
9
  v-if="form?.banner"
9
10
  v-bind="form?.banner"
10
- bannerClass="mb-4"
11
+ bannerClass="mb-4"
11
12
  />
12
13
  <Form
13
14
  v-model="valid"
@@ -19,11 +20,12 @@
19
20
  type="hidden"
20
21
  />
21
22
  <!-- Render grouped fields -->
23
+ <!-- -->
22
24
  <template v-if="form.groups">
23
25
  <template v-for="(group, id) in form.groups" :key="id">
24
26
  <div v-if="shouldShowGroup(group)" class="mb-4">
25
- <div :class="['relative grid border border-gray-300 rounded px-4 py-4 ', gridColsMap[group.numberOfColumns] || gridColsMap[2], 'gap-2']">
26
- <div v-if="group.title" :class="['absolute top-0 -translate-y-1/2 bg-white px-2 text-sm font-semibold mb-2', lang === 'dv' ? 'right-4' : 'left-4']">
27
+ <div :class="['relative grid border border-gray-300 rounded px-4 pt-6 pb-4', bg, gridColsMap[group.numberOfColumns] || gridColsMap[2], 'gap-2']">
28
+ <div v-if="group.title" :class="['absolute top-0 -translate-y-1/2 bg-inherit px-2 text-sm font-semibold mb-2', lang === 'dv' ? 'right-4' : 'left-4']">
27
29
  {{ group.title ? useLanguageSelected(group.title, lang) : '' }}
28
30
  </div>
29
31
  <template
@@ -90,7 +92,7 @@
90
92
  </div>
91
93
  </Form>
92
94
  <template #footer>
93
- <div class="flex justify-end gap-2">
95
+ <div class="flex justify-end gap-2 ">
94
96
  <Button v-if="mode == 'edit'"
95
97
  label="buttons.edit"
96
98
  color="primary"
@@ -148,7 +150,9 @@
148
150
  lang:{ type: String, default: 'dv' },
149
151
  crudLink:{ type: String },
150
152
  editItemId: { type: [String, Number], default: null },
151
- mode: { type: String, default: 'create', enum: ['create', 'edit'] }
153
+ mode: { type: String, default: 'create', enum: ['create', 'edit'] },
154
+
155
+ bg: { type: String, default: 'bg-background-color' },
152
156
  });
153
157
 
154
158
  const api = inject('api');
@@ -234,17 +238,17 @@
234
238
  formData[field.key] = false;
235
239
  break;
236
240
  case 'array':
237
- formData[field.key] = [];
241
+ formData[field.key] = null;
238
242
  break;
239
243
  case 'object':
240
- formData[field.key] = {};
244
+ formData[field.key] = null;
241
245
  break;
242
246
  case 'string':
243
247
  default:
244
248
  if (field.inputType === 'checkbox') {
245
249
  formData[field.key] = false; // Default checkboxes to false
246
250
  } else {
247
- formData[field.key] = '';
251
+ formData[field.key] = null;
248
252
  }
249
253
  break;
250
254
  }
@@ -721,7 +725,7 @@
721
725
  return;
722
726
  }
723
727
 
724
- if (!props.crudLink) {
728
+ if (!props.form?.crudLink) {
725
729
  console.error('No crudLink defined in form config');
726
730
  snackbar?.show({ message: 'Form configuration error', variant: 'error' });
727
731
  return;
@@ -733,8 +737,8 @@
733
737
  // Filter out non-database fields before submission
734
738
  const submitData = getDbFieldsOnly(formData);
735
739
 
736
- const isFullUrl = /^https?:\/\//i.test(props.crudLink)
737
- let url = isFullUrl ? props.crudLink : baseUrl + '/' + props.crudLink
740
+ const isFullUrl = /^https?:\/\//i.test(props.form?.crudLink)
741
+ let url = isFullUrl ? props.form?.crudLink : baseUrl + '/' + props.form?.crudLink
738
742
  let completeUrl = ''
739
743
  if (props.mode === 'edit' && props.editItemId) {
740
744
  url += `/${props.editItemId}`;
@@ -754,7 +758,8 @@
754
758
 
755
759
  } catch (error) {
756
760
  console.error('Form submission error:', error);
757
-
761
+ const errorMessage = error.response?.data?.message || 'Form submission failed';
762
+ snackbar?.show({ message: errorMessage, variant: 'error' });
758
763
  if (error.response?.status === 422 || error.response?.data?.errors) {
759
764
  const backendErrors = error.response.data.errors;
760
765
 
@@ -765,7 +770,6 @@
765
770
  }
766
771
 
767
772
  const errorMessage = error.response.data.message || 'Validation failed';
768
- snackbar?.show({ message: errorMessage, variant: 'error' });
769
773
  emit('submit:error', error);
770
774
  }
771
775
  } finally {
@@ -806,8 +810,9 @@
806
810
  }
807
811
 
808
812
  try {
809
- const isFullUrl = /^https?:\/\//i.test(props.crudLink)
810
- let url = isFullUrl ? props.crudLink : baseUrl + '/' + props.crudLink
813
+ console.log('form:', props.form);
814
+ const isFullUrl = /^https?:\/\//i.test(props.form?.crudLink)
815
+ let url = isFullUrl ? props.form?.crudLink : baseUrl + '/' + props.form?.crudLink
811
816
 
812
817
  const response = await api.get(`${url}/${props.editItemId}`)
813
818
 
@@ -856,6 +861,10 @@
856
861
  if (newForm.functions) {
857
862
  initializeAllFunctions();
858
863
  }
864
+ // Only fetch data if we're in edit mode and have the necessary data
865
+ if (props.mode === 'edit' && props.editItemId && newForm.crudLink) {
866
+ fetchData();
867
+ }
859
868
  }
860
869
  }, { deep: true });
861
870
 
@@ -880,7 +889,7 @@
880
889
  // Initialize on mount
881
890
  onMounted(() => {
882
891
  if (props.mode === 'edit' && props.editItemId) {
883
- fetchData()
892
+ // fetchData()
884
893
  } else {
885
894
  initializeFormData()
886
895
  }
@@ -1,5 +1,6 @@
1
1
  <template>
2
- <form @submit.prevent="handleSubmit">
2
+ <form @submit.prevent="handleSubmit" enctype="multipart/form-data">
3
+
3
4
  <slot
4
5
  :isValid="isValid"
5
6
  :validate="validate"
package/src/main.js CHANGED
@@ -11,7 +11,6 @@ const baseUrl = import.meta.env.VITE_API_BASE_URL
11
11
  const app = createApp(App)
12
12
 
13
13
  app.use(router)
14
- // app.provide('api', api);
15
14
  app.provide('baseUrl', baseUrl);
16
15
 
17
16
  app.use(PgoUiux)
@@ -62,7 +62,7 @@ export const colorMap = {
62
62
  tonal: 'bg-blue-50 vts-text-info vts-transition-colors hover:bg-blue-100'
63
63
  },
64
64
  gray: {
65
- contained: 'vts-bg-surface-elevated vts-text vts-transition-colors vts-hover-bg-surface',
65
+ contained: 'bg-gray-200 text-gray-700 vts-transition-colors hover:bg-gray-300',
66
66
  outlined: 'vts-border vts-border-color vts-text-secondary vts-bg-transparent vts-transition-colors vts-hover-bg-surface',
67
67
  text: 'vts-text-secondary vts-bg-transparent vts-transition-colors vts-hover-bg-surface',
68
68
  tonal: 'vts-bg-border-light vts-text-secondary vts-transition-colors vts-hover-bg-surface'
@@ -57,13 +57,14 @@ const fetchData = async (queryParams = {}) => {
57
57
  // const response = await api.get(`/ccs/${modelName.value}?${params}`) // uncomment this line to use real API
58
58
  // const response = person // comment this line to use real API
59
59
  console
60
- if (!response || !response.component) {
60
+ if (!response || !response.componentSettings) {
61
61
  console.error('Invalid response:', response)
62
62
  errormsg.value = response.error
63
63
  snackbar.show({ message: errormsg.value, variant: 'error' })
64
64
  }
65
65
  items.value = response
66
66
  DisplayComponent.value = response.component
67
+ // DisplayComponent.value = componentName.value
67
68
 
68
69
  console.log('DisplayComponent:', DisplayComponent.value)
69
70
 
@@ -0,0 +1,385 @@
1
+ <template>
2
+ <div class="mx-auto m-4">
3
+ <template v-if="items">
4
+ <slot name="topSlot" />
5
+
6
+ <!-- <hr class="border-input-border my-2" /> -->
7
+ <Card
8
+ card-class="border-none"
9
+ bg="vts-bg-surface-elevated"
10
+ padding="p-3"
11
+ margin="mb-4"
12
+ shadow="shadow-lg"
13
+ >
14
+ <slot name="toolbar">
15
+ <Toolbar
16
+ v-if="items.componentSettings?.toolbar"
17
+ :elevation="false"
18
+ color="none"
19
+ variant="contained"
20
+ :title="useLanguageSelected(items.componentSettings?.toolbar?.title)"
21
+ :quick-filters="items.componentSettings?.toolbar?.filters"
22
+ :filter-section="items.componentSettings?.filterSection ? true : false"
23
+ :filter-values="quickFilterValues"
24
+ :filter-section-values="filterSectionValues"
25
+ @update:filter-values="quickFilterValues = $event"
26
+ @show-filter-section="showFilters = !showFilters"
27
+ @search="handleSearch"
28
+ @refresh="handleRefresh"
29
+ @create-button="handleCreateButton"
30
+ />
31
+ </slot>
32
+ <Transition
33
+ enter-active-class="transition-all duration-300 ease-out"
34
+ enter-from-class="transform -translate-y-4 opacity-0"
35
+ enter-to-class="transform translate-y-0 opacity-100"
36
+ leave-active-class="transition-all duration-200 ease-in"
37
+ leave-from-class="transform translate-y-0 opacity-100"
38
+ leave-to-class="transform -translate-y-4 opacity-0"
39
+ >
40
+
41
+ <Card
42
+ v-if="items?.componentSettings?.filterSection && showFilters"
43
+ card-class="border-dashed"
44
+ rounded="lg"
45
+ bg=""
46
+ margin="mt-2"
47
+ >
48
+ <slot name="filterSection">
49
+ <FilterSection
50
+ dir=""
51
+ grid
52
+ :grid-columns="2"
53
+ v-model="filterSectionValues"
54
+ rounded=""
55
+ :filters="items.componentSettings.filterSection.filters"
56
+ :buttons="items.componentSettings.filterSection.buttons"
57
+ @search="handleSearch"
58
+ @submit="handleSearch"
59
+ @close="showFilters = false"
60
+ />
61
+ </slot>
62
+ </Card>
63
+ </Transition>
64
+ </Card>
65
+ <slot name="table" >
66
+ <DataTable
67
+ v-if="props.items?.componentSettings?.table?.headers.length > 0"
68
+ dir=""
69
+ :items="tableData.data"
70
+ :settings="props.items?.componentSettings?.table"
71
+ :server-side-options="options"
72
+ :loading="tableLoading"
73
+ title=""
74
+ :lang="lang"
75
+ @update:options="handleOptionsUpdate"
76
+ :show-actions="items.componentSettings?.table?.showActions ?? true"
77
+ :show-view="items.componentSettings?.table?.actions?.showView ?? true"
78
+ :show-edit="items.componentSettings?.table?.actions?.showEdit ?? true"
79
+ :show-delete="items.componentSettings?.table?.actions?.showDelete ?? true"
80
+ :inline-edit="items.componentSettings?.table?.showInlineEdit ?? true"
81
+ @inline-update="handleInlineUpdate"
82
+ @view="handleView"
83
+ @edit="handleEdit"
84
+ @delete="handleDelete"
85
+ />
86
+ <div v-else class="p-8">
87
+ <p>No headers found</p>
88
+ <!-- <pre>{{ JSON.stringify(items.componentSettings, null, 2) }}</pre> -->
89
+ </div>
90
+ </slot>
91
+
92
+
93
+ </template>
94
+ <div v-else class="flex items-center justify-center p-8">
95
+ <p>No data</p>
96
+ </div>
97
+
98
+ <!-- FORM -->
99
+ <slot name="form">
100
+ <DynamicForm
101
+ v-if="showDynamicForm && items.componentSettings?.form"
102
+ :form="items.componentSettings?.form"
103
+ rounded="sm"
104
+ :title="useLanguageSelected(formMode == 'edit' ? items.componentSettings?.form?.editTitle : items.componentSettings?.form?.createTitle) || ''"
105
+ @close="showDynamicForm = false"
106
+ @submit:success="handleFormSubmit"
107
+ :lang="lang"
108
+ :crud-link="props.items?.componentSettings?.meta?.crudLink"
109
+ :edit-item-id="editItemId"
110
+ :mode="formMode"
111
+ />
112
+ </slot>
113
+ <!-- Delete Confirmation Modal -->
114
+ <slot name="delete">
115
+ <ConfirmationModal
116
+ v-model="showConfirmation"
117
+ :item="DeleteItem"
118
+ :title="items.componentSettings?.table?.delete?.title || 'Delete Confirmation'"
119
+ :subtitle="items.componentSettings?.table?.delete?.subtitle || 'Are you sure you want to delete this item?'"
120
+ type="warning"
121
+ @confirm="handleDeleteConfirm"
122
+ @cancel="showConfirmation = false; DeleteItem = null"
123
+ >
124
+ <div
125
+ v-if="items.componentSettings?.table?.delete?.message"
126
+ v-for="(message, key) in items.componentSettings?.table?.delete?.message"
127
+ :key="key"
128
+ >
129
+ <p><b>{{ useLanguageSelected(message) }}:</b> {{ DeleteItem[key] }}</p>
130
+ </div>
131
+ </ConfirmationModal>
132
+ </slot>
133
+ <slot name="bottomSlot" />
134
+ </div>
135
+ </template>
136
+
137
+ <script setup>
138
+ import { ref, computed, watch, inject } from 'vue'
139
+ import { DynamicForm, Toolbar, FilterSection, DataTable, SearchBox, ConfirmationModal } from '../../components/pgo'
140
+ import { useLanguageSelected } from '../lib/componentConfig'
141
+
142
+ const api = inject('api')
143
+ const baseUrl = inject('baseUrl')
144
+ const snackbar = inject('snackbar')
145
+ // const route = useRoute()
146
+
147
+ const search = ref('')
148
+
149
+ const props = defineProps({
150
+ items: {
151
+ type: Object,
152
+ required: true
153
+ },
154
+ loading: {
155
+ type: Boolean,
156
+ default: false
157
+ },
158
+ lang:{ type: String, default: 'dv' }
159
+ })
160
+
161
+ const tableLoading = ref(props.loading)
162
+
163
+ const emit = defineEmits(['update:queryParams', 'items'])
164
+
165
+ const showFilters = ref(false)
166
+ // const searchbar = ref({})
167
+ const filterSectionValues = ref({})
168
+ const quickFilterValues = ref({})
169
+ const tableData = ref({})
170
+ const showConfirmation = ref(false)
171
+ const DeleteItem = ref(null)
172
+ const showDynamicForm = ref(false)
173
+ const formMode = ref('')
174
+ const editItemId = ref(null)
175
+
176
+ // Manage options internally
177
+ const options = ref({
178
+ page: 1,
179
+ itemsPerPage: 10,
180
+ sortBy: [],
181
+ sortDesc: [],
182
+ itemsLength: 0
183
+ })
184
+
185
+ const fetchData = async (queryParams = {}) => {
186
+ try {
187
+ tableLoading.value = true
188
+
189
+ // Build query string from params
190
+ const params = new URLSearchParams({
191
+ ...queryParams
192
+ })
193
+
194
+ // Get the datalink from props
195
+ const datalink = props.items?.componentSettings?.table?.datalink || ''
196
+
197
+ // Check if datalink is a full URL
198
+ const isFullUrl = /^https?:\/\//i.test(datalink)
199
+ const url = isFullUrl ? datalink : baseUrl + '/' + datalink
200
+
201
+ const response = await api.get(url + (params.toString() ? '&' + params.toString() : ''))
202
+ // const response = await api.get(`${props.items.componentSettings?.table.datalink}` + (params.toString() ? '&' + params.toString() : '')) // uncomment this line to use real API)
203
+ // const response = person // comment this line to use real API
204
+ tableData.value = response
205
+ if (response?.pagination) {
206
+ options.value = {
207
+ ...options.value,
208
+ page: response.pagination.current_page,
209
+ itemsPerPage: response.pagination.per_page,
210
+ itemsLength: response?.pagination.total
211
+ }
212
+ }
213
+
214
+ console.log('Table Fetched data:', tableData.value)
215
+ // snackbar.show({ message: 'Data fetched successfully', variant: 'success' })
216
+ } catch (error) {
217
+ console.error('Error fetching data:', error)
218
+ snackbar.show({ message: 'Error fetching data', variant: 'error' })
219
+ } finally {
220
+ tableLoading.value = false
221
+ }
222
+ }
223
+
224
+ // Update options from server response
225
+ watch(() => props.items, (newItems) => {
226
+ // Only fetch if componentSettings and table.datalink exist
227
+ if (newItems?.componentSettings?.table?.datalink) {
228
+ fetchData()
229
+ if (newItems?.componentSettings?.table?.pagination) {
230
+ options.value = {
231
+ ...options.value,
232
+ page: newItems.componentSettings.table.pagination.current_page,
233
+ }
234
+ }
235
+ }
236
+ emit('items', newItems)
237
+ }, { immediate: true, deep: true })
238
+
239
+
240
+ // Handle options update from DataTable
241
+ const handleOptionsUpdate = (newOptions) => {
242
+ // console.log('Options updated:', newOptions)
243
+
244
+ // Update local options
245
+ options.value = { ...options.value, ...newOptions }
246
+
247
+ // Build query params
248
+ const queryParams = {
249
+ page: newOptions.page,
250
+ per_page: newOptions.itemsPerPage
251
+ }
252
+
253
+ // Add sorting if exists
254
+ if (newOptions.sortBy && newOptions.sortBy.length > 0) {
255
+ const sortParams = newOptions.sortBy.map((field, index) => {
256
+ const isDesc = newOptions.sortDesc[index]
257
+ return isDesc ? `-${field}` : field
258
+ })
259
+ queryParams.sort = sortParams.join(',')
260
+ }
261
+ fetchData(queryParams)
262
+ // Emit to parent ComponentRenderer
263
+ // emit('update:queryParams', queryParams)
264
+ }
265
+
266
+ const handleInlineUpdate = async ({ item, key, value, updateData }) => {
267
+ console.log('Inline update:', { item, key, value, updateData })
268
+ // Optionally refetch data after update
269
+ // await fetchData()
270
+ }
271
+
272
+ const handleSearch = (filters) => {
273
+ // Transform filters object into filter=key:value,key:value format
274
+ let filterString = ''
275
+ if (filters && typeof filters === 'object') {
276
+ const filterPairs = []
277
+ Object.entries(filters).forEach(([key, value]) => {
278
+ if (value !== null && value !== undefined && value !== '') {
279
+ filterPairs.push(`${key}:${value}`)
280
+ }
281
+ })
282
+ if (filterPairs.length > 0) {
283
+ filterString = filterPairs.join(',')
284
+ }
285
+ }
286
+
287
+ // Build query params
288
+ const queryParams = {
289
+ page: 1,
290
+ per_page: options.value.itemsPerPage
291
+ }
292
+
293
+ if (filterString) {
294
+ queryParams.filter = filterString
295
+ }
296
+
297
+ // Update options and emit
298
+ options.value = { ...options.value, page: 1 }
299
+ fetchData(queryParams)
300
+ // emit('update:queryParams', queryParams)
301
+ }
302
+ const handleFormSubmit = (msg) => {
303
+ snackbar.show({ message: msg, variant: 'success' })
304
+ showDynamicForm.value = false;
305
+ editItemId.value = null
306
+ handleRefresh()
307
+ }
308
+ const handleRefresh = (msg) => {
309
+ fetchData()
310
+ }
311
+ // Handle quick search from searchbar
312
+ const handleQuickSearch = (filters) => {
313
+
314
+ console.log('Quick search filters:', filters)
315
+ // Transform filters object into filter={columnName}:{value} format
316
+ const filterParams = {}
317
+ if (filters && typeof filters === 'object') {
318
+ Object.entries(filters).forEach(([key, value]) => {
319
+ if (value !== null && value !== undefined && value !== '') {
320
+ filterParams[`filter[${key}]`] = value
321
+ }
322
+ })
323
+ }
324
+ // Reset to page 1 and fetch with filters
325
+ const newOptions = { ...options.value, ...filters, page: 1 }
326
+ options.value = newOptions
327
+ // emit('update:queryParams', newOptions)
328
+ fetchData(newOptions)
329
+ }
330
+ const handleCreateButton = () => {
331
+ editItemId.value = null
332
+ showDynamicForm.value = true
333
+ formMode.value = 'create'
334
+
335
+ }
336
+
337
+ const handleView = (item) => {
338
+ // Navigate to detail view or open modal
339
+ }
340
+ const handleEdit = (item) => {
341
+ showDynamicForm.value = true
342
+ editItemId.value = item.id
343
+ formMode.value = 'edit'
344
+ // Navigate to detail view or open modal
345
+ }
346
+ const handleDelete = (item) => {
347
+ DeleteItem.value = item
348
+ showConfirmation.value = true
349
+ console.log('Delete item:', item)
350
+ // Navigate to detail view or open modal
351
+ }
352
+
353
+ const handleDeleteConfirm = (item) => {
354
+ showConfirmation.value = false
355
+ console.log('Confirmed delete for item:', item)
356
+ deleteItemRequest(item.id)
357
+ }
358
+
359
+ const deleteItemRequest = async (id) => {
360
+ try {
361
+ // Get the delete link from componentSettings
362
+ const deleteLink = props.items?.componentSettings?.meta?.crudLink
363
+ if (!deleteLink) {
364
+ console.error('Delete link not defined in componentSettings')
365
+ snackbar.show({ message: 'Delete action not configured', variant: 'error' })
366
+ return
367
+ }
368
+
369
+ // Replace {id} in the template with the actual id
370
+ // const deleteLink = deleteLinkTemplate.replace('{id}', id)
371
+
372
+ // Check if deleteLink is a full URL
373
+ const isFullUrl = /^https?:\/\//i.test(deleteLink)
374
+ const url = isFullUrl ? deleteLink : baseUrl + '/' + deleteLink
375
+
376
+ await api.delete(url+`/${id}`)
377
+ snackbar.show({ message: 'Item deleted successfully', variant: 'success' })
378
+ fetchData() // Refresh the table after deletion
379
+ } catch (error) {
380
+ console.error('Error deleting item:', error)
381
+ snackbar.show({ message: 'Error deleting item', variant: 'error' })
382
+ }
383
+ // Perform delete action, e.g. call API to delete item
384
+ }
385
+ </script>