summaryrefslogtreecommitdiff
path: root/components/client-table-v2/import-utils.ts
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-12-01 15:22:38 +0900
committerjoonhoekim <26rote@gmail.com>2025-12-01 15:22:38 +0900
commitffb8e2e99e1d0c105b1c545ff7ab4d3149ec6c48 (patch)
tree1af87b9c19bc56ed1192a5b5947d22fa5f4dbd98 /components/client-table-v2/import-utils.ts
parentd674b066a9a3195d764f693885fb9f25d66263ed (diff)
(김준회) 서버측 where, order by 절 지원을 위한 v2 임시작업
Diffstat (limited to 'components/client-table-v2/import-utils.ts')
-rw-r--r--components/client-table-v2/import-utils.ts100
1 files changed, 100 insertions, 0 deletions
diff --git a/components/client-table-v2/import-utils.ts b/components/client-table-v2/import-utils.ts
new file mode 100644
index 00000000..bc7f4b44
--- /dev/null
+++ b/components/client-table-v2/import-utils.ts
@@ -0,0 +1,100 @@
+import ExcelJS from "exceljs"
+
+interface ImportExcelOptions {
+ file: File
+ /**
+ * Map Excel header names to data keys.
+ * Example: { "Name": "name", "Age": "age" }
+ */
+ columnMapping?: Record<string, string>
+ /**
+ * Row offset to start reading data (0-based).
+ * Default: 1 (assuming row 0 is header)
+ */
+ dataStartRow?: number
+}
+
+export interface ExcelImportResult<T = any> {
+ data: T[]
+ errors: string[]
+}
+
+/**
+ * Generic function to read an Excel file and convert it to an array of objects.
+ * Does NOT handle database insertion or validation logic.
+ */
+export async function importFromExcel<T = any>({
+ file,
+ columnMapping = {},
+ dataStartRow = 1,
+}: ImportExcelOptions): Promise<ExcelImportResult<T>> {
+ const workbook = new ExcelJS.Workbook()
+ const arrayBuffer = await file.arrayBuffer()
+
+ try {
+ await workbook.xlsx.load(arrayBuffer)
+ } catch (error) {
+ return { data: [], errors: ["Failed to parse Excel file. Please ensure it is a valid .xlsx file."] }
+ }
+
+ const worksheet = workbook.worksheets[0] // Read the first sheet
+ const data: T[] = []
+ const errors: string[] = []
+
+ if (!worksheet) {
+ return { data: [], errors: ["No worksheet found in the Excel file."] }
+ }
+
+ // 1. Read Header Row (assumed to be row 1 for mapping if no explicit mapping provided,
+ // or we can use it to validate mapping)
+ const headers: string[] = []
+ const headerRow = worksheet.getRow(1)
+ headerRow.eachCell((cell, colNumber) => {
+ headers[colNumber] = String(cell.value).trim()
+ })
+
+ // 2. Iterate Data Rows
+ worksheet.eachRow((row, rowNumber) => {
+ if (rowNumber <= dataStartRow) return // Skip header/pre-header rows
+
+ const rowData: any = {}
+ let hasData = false
+
+ row.eachCell((cell, colNumber) => {
+ const headerText = headers[colNumber]
+ if (!headerText) return
+
+ // Determine the key to use for this column
+ // Priority: Explicit mapping -> Header text as key
+ const key = columnMapping[headerText] || headerText
+
+ let cellValue = cell.value
+
+ // Handle ExcelJS object values (e.g. formula results, hyperlinks)
+ if (cellValue && typeof cellValue === 'object') {
+ if ('result' in cellValue) {
+ // Formula result
+ cellValue = (cellValue as any).result
+ } else if ('text' in cellValue) {
+ // Hyperlink text
+ cellValue = (cellValue as any).text
+ } else if ('richText' in cellValue) {
+ // Rich text
+ cellValue = (cellValue as any).richText.map((t: any) => t.text).join('')
+ }
+ }
+
+ if (cellValue !== null && cellValue !== undefined && String(cellValue).trim() !== '') {
+ hasData = true
+ rowData[key] = cellValue
+ }
+ })
+
+ if (hasData) {
+ data.push(rowData as T)
+ }
+ })
+
+ return { data, errors }
+}
+