@ramathibodi/nuxt-commons 0.1.37 → 0.1.38

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/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.37",
7
+ "version": "0.1.38",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.3",
10
10
  "unbuild": "2.0.0"
@@ -1,43 +1,76 @@
1
1
  <script lang="ts" setup>
2
- import {ref} from 'vue'
2
+ import { ref } from 'vue'
3
3
  import * as XLSX from 'xlsx'
4
- import {VBtn} from 'vuetify/components/VBtn'
5
- import {useAlert} from '../composables/alert'
4
+ import { VBtn } from 'vuetify/components/VBtn'
5
+ import { useAlert } from '../composables/alert'
6
6
 
7
7
  interface ExportButtonProps extends /* @vue-ignore */ InstanceType<typeof VBtn['$props']> {
8
8
  fileName?: string
9
+ sheetName?: string
9
10
  modelValue?: object[]
10
11
  }
11
12
 
12
13
  const props = withDefaults(defineProps<ExportButtonProps>(), {
13
14
  fileName: 'download',
15
+ sheetName: 'Sheet1',
14
16
  })
15
17
 
16
18
  const alert = useAlert()
17
19
  const loading = ref(false)
18
20
 
21
+ /**
22
+ * Triggers file export
23
+ */
19
24
  function exportFile() {
20
- if (props.modelValue && Array.isArray(props.modelValue)) {
25
+ if (props.modelValue && Array.isArray(props.modelValue) && props.modelValue.length > 0) {
21
26
  loading.value = true
22
- const workbook = XLSX.utils.book_new()
23
- const worksheet = XLSX.utils.json_to_sheet(serializeRoleField(props.modelValue))
24
- const fileName = `${props.fileName}.xlsx`
25
- XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
26
- XLSX.writeFile(workbook, fileName)
27
- loading.value = false
28
- }
29
- else {
27
+
28
+ try {
29
+ const workbook = XLSX.utils.book_new()
30
+ const worksheet = XLSX.utils.json_to_sheet(flattenNestedFields(props.modelValue))
31
+ const fileName = `${props.fileName}.xlsx`
32
+
33
+ XLSX.utils.book_append_sheet(workbook, worksheet, props.sheetName)
34
+ XLSX.writeFile(workbook, fileName)
35
+ } catch (error: any) {
36
+ alert?.addAlert({ message: `Export failed: ${error.message}`, alertType: 'error' })
37
+ } finally {
38
+ loading.value = false
39
+ }
40
+ } else {
30
41
  alert?.addAlert({ message: 'Invalid or no data to export', alertType: 'error' })
31
42
  }
32
43
  }
33
44
 
34
- const serializeRoleField = (items: any) => {
35
- return items.map((item:any) => {
36
- if (item.properties && typeof item.properties === 'object') {
37
- item.properties = JSON.stringify(item.properties);
45
+ /**
46
+ * Recursively flattens nested fields for export
47
+ * @param items - Array of objects to flatten
48
+ */
49
+ function flattenNestedFields(items: any[]) {
50
+ return items.map((item: any) => {
51
+ return flattenObject(item)
52
+ })
53
+ }
54
+
55
+ /**
56
+ * Recursively flattens an object, converting nested keys into dot-separated keys
57
+ * @param obj - Object to flatten
58
+ * @param parentKey - Parent key (for recursion)
59
+ * @param separator - Separator for nested keys
60
+ */
61
+ function flattenObject(obj: any, parentKey = '', separator = '.') {
62
+ return Object.keys(obj).reduce((acc: any, key: string) => {
63
+ const newKey = parentKey ? `${parentKey}${separator}${key}` : key
64
+ const value = obj[key]
65
+
66
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
67
+ Object.assign(acc, flattenObject(value, newKey, separator))
68
+ } else {
69
+ acc[newKey] = typeof value === 'object' ? JSON.stringify(value) : value
38
70
  }
39
- return item;
40
- });
71
+
72
+ return acc
73
+ }, {})
41
74
  }
42
75
  </script>
43
76
 
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" setup>
2
2
  import * as XLSX from 'xlsx'
3
- import {ref} from 'vue'
4
- import {useAlert} from '../composables/alert'
3
+ import { ref } from 'vue'
4
+ import { useAlert } from '../composables/alert'
5
5
 
6
6
  const alert = useAlert()
7
7
  const emit = defineEmits<{
@@ -23,15 +23,17 @@ function uploadedFile(files: File[] | File | undefined) {
23
23
 
24
24
  const fileExtension = files.name.slice(files.name.lastIndexOf('.')).toLowerCase()
25
25
  if (!['.xlsx', '.csv'].includes(fileExtension)) {
26
- alert?.addAlert({ message: 'Please upload a file with .csv or .xlsx extension only (' + files.name + ')', alertType: 'error' })
26
+ alert?.addAlert({ message: `Please upload a file with .csv or .xlsx extension only (${files.name})`, alertType: 'error' })
27
27
  return
28
28
  }
29
29
 
30
30
  const reader = new FileReader()
31
31
  reader.onload = (e: ProgressEvent<FileReader>) => {
32
32
  const workbook = XLSX.read(e.target?.result)
33
- const parseData = parsePropertiesField(XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]))
34
- emit('import', parseData)
33
+ const parsedData = parseAndAggregateColumns(
34
+ XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]])
35
+ )
36
+ emit('import', parsedData)
35
37
  loading.value = false
36
38
  fileBtnRef.value.reset()
37
39
  }
@@ -39,14 +41,63 @@ function uploadedFile(files: File[] | File | undefined) {
39
41
  reader.readAsArrayBuffer(files)
40
42
  }
41
43
 
42
- const parsePropertiesField = (items:any) => {
43
- return items.map((item : any) => {
44
- if (item.properties && typeof item.properties === 'string') {
45
- item.properties = JSON.parse(item.properties);
44
+ /**
45
+ * Recursively aggregates nested columns and properties.
46
+ * @param items - Array of data from the Excel sheet
47
+ */
48
+ const parseAndAggregateColumns = (items: any[]) => {
49
+ return items.map((item: any) => {
50
+ const aggregatedItem: any = {}
51
+
52
+ for (const key in item) {
53
+ if (key.includes('.')) {
54
+ // Extract root key and subKey
55
+ const [rootKey, ...subKeys] = key.split('.')
56
+
57
+ // Recursively aggregate subKeys
58
+ aggregatedItem[rootKey] = aggregatedItem[rootKey] || {}
59
+ assignNestedValue(aggregatedItem[rootKey], subKeys, parseIfJson(item[key]))
60
+ } else {
61
+ // Directly assign root-level properties
62
+ aggregatedItem[key] = parseIfJson(item[key])
63
+ }
46
64
  }
47
- return item;
48
- });
49
- };
65
+
66
+ return aggregatedItem
67
+ })
68
+ }
69
+
70
+ /**
71
+ * Recursively assigns a value to a nested key structure.
72
+ * @param obj - The object to assign to
73
+ * @param keys - Array of keys leading to the final property
74
+ * @param value - The value to assign
75
+ */
76
+ const assignNestedValue = (obj: any, keys: string[], value: any) => {
77
+ const [currentKey, ...remainingKeys] = keys
78
+ if (remainingKeys.length === 0) {
79
+ obj[currentKey] = value
80
+ } else {
81
+ obj[currentKey] = obj[currentKey] || {}
82
+ assignNestedValue(obj[currentKey], remainingKeys, value)
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Attempt to parse a value as JSON or array.
88
+ * @param value - The value to parse
89
+ */
90
+ const parseIfJson = (value: any) => {
91
+ if (typeof value === 'string') {
92
+ try {
93
+ return JSON.parse(value)
94
+ } catch {
95
+ // If parsing fails, return the original value
96
+ return value
97
+ }
98
+ }
99
+ return value
100
+ }
50
101
  </script>
51
102
 
52
103
  <template>