react-pdf-levelup 1.0.4

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.
Files changed (70) hide show
  1. package/README.md +40 -0
  2. package/fn/aggAlias.js +21 -0
  3. package/fn/moveComponents.js +129 -0
  4. package/fn/postinstall.js +66 -0
  5. package/fn/upVersion.js +12 -0
  6. package/fn/updateTsconfig.js +10 -0
  7. package/next.config.mjs +6 -0
  8. package/package.json +39 -0
  9. package/public/bg-login.jpg +0 -0
  10. package/public/codigo_guardado.js +1 -0
  11. package/public/css/style.css +751 -0
  12. package/public/dboard/logo.png +0 -0
  13. package/public/favicon.ico +0 -0
  14. package/public/fonts/TimesNewerRoman-Bold.otf +0 -0
  15. package/public/fonts/TimesNewerRoman-BoldItalic.otf +0 -0
  16. package/public/fonts/TimesNewerRoman-Italic.otf +0 -0
  17. package/public/fonts/TimesNewerRoman-Regular.otf +0 -0
  18. package/public/header-pdf.jpg +0 -0
  19. package/public/home/bgHome.jpg +0 -0
  20. package/public/home/bgHome2.jpg +0 -0
  21. package/public/home/download.jpg +0 -0
  22. package/public/home/undraw_elements_re_25t9.svg +1 -0
  23. package/public/home/undraw_project_completed_re_jr7u.svg +1 -0
  24. package/public/home/undraw_shared_goals_re_jvqd.svg +1 -0
  25. package/public/home/undraw_spread_love_re_v3cl.svg +1 -0
  26. package/public/home/undraw_target_re_fi8j.svg +1 -0
  27. package/public/home/undraw_visionary_technology_re_jfp7.svg +1 -0
  28. package/public/logo.png +0 -0
  29. package/public/marca/logo.svg +1 -0
  30. package/src/components/PDF/components/DocumentoTemplate.tsx +140 -0
  31. package/src/components/PDF/components/PDFContent.tsx +192 -0
  32. package/src/components/PDF/core/Etiquetas.tsx +152 -0
  33. package/src/components/PDF/core/Grid.tsx +101 -0
  34. package/src/components/PDF/core/Img.tsx +22 -0
  35. package/src/components/PDF/core/LayoutPDF.tsx +186 -0
  36. package/src/components/PDF/core/Listas.tsx +10 -0
  37. package/src/components/PDF/core/MakePDF.tsx +50 -0
  38. package/src/components/PDF/core/PageElements.tsx +50 -0
  39. package/src/components/PDF/core/Position.tsx +33 -0
  40. package/src/components/PDF/core/Tablet.tsx +121 -0
  41. package/src/components/PDF/core/index.tsx +56 -0
  42. package/src/components/PDF/lib/pdfParser.ts +620 -0
  43. package/src/components/PDF/services/apiService.ts +17 -0
  44. package/src/components/PDF/templates/AllTemplate.tsx +134 -0
  45. package/src/components/PDF/templates/BusinessCardTemplate.tsx +139 -0
  46. package/src/components/PDF/templates/CertificateTemplate.tsx +31 -0
  47. package/src/components/PDF/templates/HeaderFooterTemplate.tsx +61 -0
  48. package/src/components/PDF/templates/InvoiceTemplate.tsx +53 -0
  49. package/src/components/PDF/templates/ProposalTemplate.tsx +246 -0
  50. package/src/components/PDF/templates/ReportTemplate.tsx +57 -0
  51. package/src/components/PDF/templates/ResumeTemplate.tsx +170 -0
  52. package/src/components/PDF/templates/TablasTemplate.tsx +307 -0
  53. package/src/components/PDF/templates/index.ts +9 -0
  54. package/src/components/PDF/view/ColorPicker.tsx +147 -0
  55. package/src/components/PDF/view/Header.tsx +102 -0
  56. package/src/components/PDF/view/HomePDF.tsx +177 -0
  57. package/src/components/PDF/view/SettingsPanel.tsx +703 -0
  58. package/src/pages/AllTemplate.tsx +53 -0
  59. package/src/pages/Documento2.tsx +45 -0
  60. package/src/pages/_app.tsx +6 -0
  61. package/src/pages/_document.tsx +13 -0
  62. package/src/pages/api/generatePDF.ts +74 -0
  63. package/src/pages/api/hello.ts +13 -0
  64. package/src/pages/api/readFile.ts +18 -0
  65. package/src/pages/api/readTemplateFile.ts +26 -0
  66. package/src/pages/api/save.ts +22 -0
  67. package/src/pages/api/saveFile.ts +20 -0
  68. package/src/pages/index.tsx +13 -0
  69. package/src/pages/template/[template].tsx +250 -0
  70. package/tsconfig.json +63 -0
@@ -0,0 +1,620 @@
1
+ // lib/pdfParser.ts
2
+ export interface ParsedNode {
3
+ type: string
4
+ content: (string | ParsedNode | any)[]
5
+ props?: Record<string, any>
6
+ }
7
+
8
+ export const parseJSX = (jsx: string): ParsedNode[] => {
9
+ // Extrae el contexto del código antes del return (variables, etc.)
10
+
11
+ const extractCodeContext = (fullCode: string) => {
12
+ try {
13
+ // Creamos un objeto para almacenar variables
14
+ const context: Record<string, any> = {}
15
+
16
+ // Extraemos el código antes del return
17
+ const beforeReturn = fullCode.split("return")[0]
18
+
19
+ // Buscamos declaraciones de variables (const|let|var)
20
+ const varRegex = /(const|let|var)\s+(\w+)\s*=\s*([^;]+);?/g
21
+ let match
22
+
23
+ while ((match = varRegex.exec(beforeReturn)) !== null) {
24
+ const [_, declarationType, varName, varValue] = match
25
+
26
+ try {
27
+ // Evaluamos el valor de la variable y lo guardamos en el contexto
28
+ context[varName] = new Function(`return ${varValue}`)()
29
+ } catch (e) {
30
+ console.error(`Error evaluating variable ${varName}:`, e)
31
+ }
32
+ }
33
+
34
+ return context
35
+ } catch (error) {
36
+ console.error("Error extracting code context:", error)
37
+ return {}
38
+ }
39
+ }
40
+
41
+ // Obtenemos el contexto a partir del código completo
42
+ const codeContext = extractCodeContext(jsx)
43
+
44
+ // Helper function to access nested properties and array elements
45
+ const getNestedProperty = (obj: any, path: string) => {
46
+ try {
47
+ // Handle array indexing like data.departmentData[0].responsable
48
+ const arrayIndexRegex = /(\w+)\[(\d+)\]\.(\w+)/
49
+ const arrayMatch = path.match(arrayIndexRegex)
50
+
51
+ if (arrayMatch) {
52
+ const [_, arrayName, indexStr, propName] = arrayMatch
53
+ const index = Number.parseInt(indexStr, 10)
54
+
55
+ // Handle nested arrays like data.departmentData
56
+ if (arrayName.includes(".")) {
57
+ const parts = arrayName.split(".")
58
+ let current = obj
59
+
60
+ for (const part of parts) {
61
+ if (current && typeof current === "object") {
62
+ current = current[part]
63
+ } else {
64
+ return undefined
65
+ }
66
+ }
67
+
68
+ if (Array.isArray(current) && current.length > index) {
69
+ return current[index][propName]
70
+ }
71
+ }
72
+ // Handle direct arrays like departmentData
73
+ else if (obj[arrayName] && Array.isArray(obj[arrayName]) && obj[arrayName].length > index) {
74
+ return obj[arrayName][index][propName]
75
+ }
76
+
77
+ return undefined
78
+ }
79
+
80
+ // Handle regular nested properties like data.departmentData
81
+ if (path.includes(".")) {
82
+ const parts = path.split(".")
83
+ let current = obj
84
+
85
+ for (const part of parts) {
86
+ if (current && typeof current === "object") {
87
+ current = current[part]
88
+ } else {
89
+ return undefined
90
+ }
91
+ }
92
+
93
+ return current
94
+ }
95
+
96
+ // Handle simple properties
97
+ return obj[path]
98
+ } catch (error) {
99
+ console.error(`Error accessing nested property: ${path}`, error)
100
+ return undefined
101
+ }
102
+ }
103
+
104
+ // Find all data arrays that might be used in map functions
105
+ const findDataArrays = () => {
106
+ const arrays: Record<string, any[]> = {}
107
+
108
+ // Look for arrays in the context
109
+ for (const [key, value] of Object.entries(codeContext)) {
110
+ if (Array.isArray(value)) {
111
+ arrays[key] = value
112
+ } else if (value && typeof value === "object") {
113
+ // Look for arrays in nested objects (like data.departmentData)
114
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
115
+ if (Array.isArray(nestedValue)) {
116
+ arrays[`${key}.${nestedKey}`] = nestedValue
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ return arrays
123
+ }
124
+
125
+ const dataArrays = findDataArrays()
126
+
127
+ // Process array indexing expressions like data.departmentData[0].responsable
128
+ const processArrayIndexing = (text: string) => {
129
+ try {
130
+ const arrayIndexRegex = /(\w+(?:\.\w+)*)\[(\d+)\]\.(\w+)/g
131
+
132
+ return text.replace(arrayIndexRegex, (match, arrayPath, indexStr, propName) => {
133
+ try {
134
+ const index = Number.parseInt(indexStr, 10)
135
+
136
+ // Get the array from the context
137
+ let array
138
+ if (arrayPath.includes(".")) {
139
+ // Handle nested properties like data.departmentData
140
+ array = getNestedProperty(codeContext, arrayPath)
141
+ } else {
142
+ array = codeContext[arrayPath]
143
+ }
144
+
145
+ if (Array.isArray(array) && array.length > index && array[index][propName] !== undefined) {
146
+ return JSON.stringify(array[index][propName])
147
+ }
148
+
149
+ return match
150
+ } catch (error) {
151
+ console.error(`Error processing array indexing: ${match}`, error)
152
+ return match
153
+ }
154
+ })
155
+ } catch (error) {
156
+ console.error("Error in processArrayIndexing:", error)
157
+ return text
158
+ }
159
+ }
160
+
161
+ // Función para procesar expresiones utilizando el contexto extraído
162
+ const processExpressions = (text: string, localContext: Record<string, any> = {}) => {
163
+ try {
164
+ // First process array indexing expressions
165
+ let processedText = processArrayIndexing(text)
166
+
167
+ // Special handling for row references
168
+ if (processedText.includes("row.")) {
169
+ const rowRefs = processedText.match(/row\.(\w+)/g)
170
+ if (rowRefs) {
171
+ for (const rowRef of rowRefs) {
172
+ const prop = rowRef.split(".")[1]
173
+
174
+ if (localContext.row && localContext.row[prop] !== undefined) {
175
+ const value = JSON.stringify(localContext.row[prop])
176
+ processedText = processedText.replace(new RegExp(rowRef, "g"), value)
177
+ } else {
178
+ // Recorremos los data arrays y concatenamos los valores de todos los elementos
179
+ for (const array of Object.values(dataArrays)) {
180
+ if (array.length > 0 && array[0][prop] !== undefined) {
181
+ const values = array.map((item) => JSON.stringify(item[prop])).join(", ") // Puedes cambiar el delimitador según lo necesites
182
+ processedText = processedText.replace(new RegExp(rowRef, "g"), values)
183
+ break
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ // Busca expresiones del tipo {data.hola}, {variable}, etc.
192
+ return processedText.replace(/\{([^}]+)\}/g, (match, expression) => {
193
+ console.log(match, expression)
194
+ try {
195
+ // First check if the expression contains array indexing
196
+ if (expression.match(/\w+(?:\.\w+)*\[\d+\]\.\w+/)) {
197
+ try {
198
+ // Process the array indexing
199
+ const processedExpr = processArrayIndexing(expression)
200
+ // Try to evaluate the processed expression
201
+ return new Function(`return ${processedExpr}`)()
202
+ } catch (e) {
203
+ console.error(`Error evaluating array indexing expression: ${expression}`, e)
204
+ }
205
+ }
206
+
207
+ // Handle row references in expressions
208
+ if (expression.includes("row.")) {
209
+ const rowRefs = expression.match(/row\.(\w+)/g)
210
+ if (rowRefs) {
211
+ let processedExpr = expression
212
+
213
+ for (const rowRef of rowRefs) {
214
+ const prop = rowRef.split(".")[1]
215
+
216
+ // If we have a row in the local context, use it
217
+ if (localContext.row && localContext.row[prop] !== undefined) {
218
+ processedExpr = processedExpr.replace(new RegExp(rowRef, "g"), JSON.stringify(localContext.row[prop]))
219
+ }
220
+ // Otherwise, try to find it in the data arrays
221
+ else {
222
+ // Look through all data arrays to find a matching property
223
+ for (const array of Object.values(dataArrays)) {
224
+ if (array.length > 0 && array[0][prop] !== undefined) {
225
+ // Use the first item's property as a fallback
226
+ processedExpr = processedExpr.replace(new RegExp(rowRef, "g"), JSON.stringify(array[0][prop]))
227
+ break
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ // Try to evaluate the processed expression
234
+ try {
235
+ return new Function(`return ${processedExpr}`)()
236
+ } catch (e) {
237
+ console.error(`Error evaluating processed row expression: ${processedExpr}`, e)
238
+ return match
239
+ }
240
+ }
241
+ }
242
+
243
+ // Maneja valores booleanos y numéricos directamente
244
+ if (expression === "true") return true
245
+ if (expression === "false") return false
246
+ if (!isNaN(Number(expression))) return Number(expression)
247
+
248
+ // Combine global context with local context
249
+ const combinedContext = { ...codeContext, ...localContext }
250
+ const keys = Object.keys(combinedContext)
251
+ const values = Object.values(combinedContext)
252
+ const evaluator = new Function(...keys, `return ${expression}`)
253
+
254
+ return evaluator(...values)
255
+ } catch (e) {
256
+ console.error(`Error evaluating expression ${expression}:`, e)
257
+ return match // Si falla, se retorna la expresión sin evaluar
258
+ }
259
+ })
260
+ } catch (error) {
261
+ console.error("Error processing expressions:", error)
262
+ return text
263
+ }
264
+ }
265
+
266
+ // Function to detect and process map expressions in JSX
267
+ const detectAndProcessMapExpressions = (jsxContent: string): string => {
268
+ // Expresión regular mejorada para capturar correctamente las expresiones map
269
+ // Esta versión es más robusta para manejar diferentes formatos de código
270
+ const mapRegex = /\{([\s\S]*?)\.map$$\(([\s\S]*?)(?:,\s*([\s\S]*?))?$$\s*=>\s*([\s\S]*?)\)\}/g
271
+
272
+ return jsxContent.replace(mapRegex, (match, arrayExpr, itemParam, indexParam, mapBody) => {
273
+ try {
274
+ // Limpiar espacios en blanco
275
+ const arrayName = arrayExpr.trim()
276
+ itemParam = itemParam.trim()
277
+
278
+ console.log("Array name:", arrayName)
279
+ console.log("Item param:", itemParam)
280
+ console.log("Map body:", mapBody)
281
+
282
+ // Obtener el array del contexto
283
+ let array
284
+ if (arrayName.includes(".")) {
285
+ // Manejar propiedades anidadas como data.departmentData
286
+ array = getNestedProperty(codeContext, arrayName)
287
+ } else {
288
+ array = codeContext[arrayName]
289
+ }
290
+
291
+ if (!Array.isArray(array)) {
292
+ console.error(`Array ${arrayName} not found in context or not an array`)
293
+ return match
294
+ }
295
+
296
+ console.log("Array data:", array)
297
+
298
+ // Procesar cada elemento del array y generar JSX
299
+ let result = ""
300
+ array.forEach((item, index) => {
301
+ // Crear un contexto local con el elemento actual
302
+ const localContext = {
303
+ [itemParam]: item,
304
+ row: item, // Agregar row como alias para el elemento actual
305
+ index: index,
306
+ }
307
+
308
+ // Clonar el cuerpo del map para esta iteración
309
+ let processedBody = mapBody
310
+
311
+ // Reemplazar todas las ocurrencias de itemParam con las propiedades reales del elemento
312
+ const itemRegex = new RegExp(`${itemParam}\\.([\\w]+)`, "g")
313
+ processedBody = processedBody.replace(itemRegex, (_, prop) => {
314
+ return JSON.stringify(item[prop])
315
+ })
316
+
317
+ // Reemplazar todas las ocurrencias de row con las propiedades reales del elemento
318
+ const rowRegex = /row\.([\w]+)/g
319
+ processedBody = processedBody.replace(rowRegex, (_, prop) => {
320
+ return JSON.stringify(item[prop])
321
+ })
322
+
323
+ // Reemplazar referencias al índice si es necesario
324
+ if (indexParam) {
325
+ const indexRegex = new RegExp(`${indexParam}`, "g")
326
+ processedBody = processedBody.replace(indexRegex, index.toString())
327
+ }
328
+
329
+ // Agregar una key única a cada elemento si no tiene una
330
+ if (!processedBody.includes("key={") && processedBody.trim().startsWith("<")) {
331
+ const tagEnd = processedBody.indexOf(">")
332
+ if (tagEnd > 0) {
333
+ processedBody = processedBody.substring(0, tagEnd) + ` key={${index}}` + processedBody.substring(tagEnd)
334
+ }
335
+ }
336
+
337
+ result += processedBody
338
+ })
339
+
340
+ return result
341
+ } catch (error) {
342
+ console.error("Error processing map expression:", error)
343
+ return match
344
+ }
345
+ })
346
+ }
347
+
348
+ // Pre-process the JSX to handle map expressions
349
+ const processedJSX = detectAndProcessMapExpressions(jsx)
350
+
351
+ // Función auxiliar para manejar tokens de texto o expresión
352
+ const handleTextToken = (token: string, parent: ParsedNode) => {
353
+ const trimmedToken = token.trim()
354
+
355
+ // Si el token es una expresión completa, se evalúa directamente
356
+ if (trimmedToken.startsWith("{") && trimmedToken.endsWith("}")) {
357
+ const expressionContent = trimmedToken.slice(1, -1)
358
+
359
+ // Handle array indexing expressions
360
+ if (expressionContent.match(/\w+(?:\.\w+)*\[\d+\]\.\w+/)) {
361
+ try {
362
+ // Process the array indexing
363
+ const processedExpr = processArrayIndexing(expressionContent)
364
+ // Try to evaluate the processed expression
365
+ const result = new Function(`return ${processedExpr}`)()
366
+ parent.content.push(result)
367
+ return
368
+ } catch (e) {
369
+ console.error(`Error evaluating array indexing expression: ${expressionContent}`, e)
370
+ }
371
+ }
372
+
373
+ // Handle row references in the expression
374
+ if (expressionContent.includes("row.")) {
375
+ try {
376
+ // Process the expression with row references
377
+ const processedExpr = processExpressions(`{${expressionContent}}`)
378
+ // If it's a string (which means it was processed), remove the quotes
379
+ if (typeof processedExpr === "string" && processedExpr.startsWith('"') && processedExpr.endsWith('"')) {
380
+ parent.content.push(processedExpr.slice(1, -1))
381
+ } else {
382
+ parent.content.push(processedExpr)
383
+ }
384
+ return
385
+ } catch (e) {
386
+ console.error(`Error processing row expression: ${expressionContent}`, e)
387
+ }
388
+ }
389
+
390
+ // Para expresiones normales (no mapeo)
391
+ try {
392
+ const keys = Object.keys(codeContext)
393
+ const values = Object.values(codeContext)
394
+ const evaluator = new Function(...keys, `return ${expressionContent}`)
395
+ const result = evaluator(...values)
396
+
397
+ if (Array.isArray(result)) {
398
+ result.forEach((item) => {
399
+ // Si el item es un objeto con 'type', asumimos que es un nodo
400
+ if (item && typeof item === "object" && "type" in item) {
401
+ parent.content.push(item)
402
+ } else {
403
+ // De lo contrario, lo agregamos como texto o lo dejamos como esté
404
+ parent.content.push(item)
405
+ }
406
+ })
407
+ } else {
408
+ parent.content.push(result)
409
+ }
410
+ } catch (e) {
411
+ console.error(`Error evaluating expression ${expressionContent}:`, e)
412
+ }
413
+ } else {
414
+ // Si es texto normal (que pueda contener expresiones inline) se procesa
415
+ const processedText = processExpressions(token)
416
+ const trimmedText = processedText.trim()
417
+ if (trimmedText) {
418
+ parent.content.push(trimmedText)
419
+ }
420
+ }
421
+ }
422
+
423
+ // Extraemos la parte JSX (entre <LayoutPDF> y </LayoutPDF>)
424
+ const jsxMatch = processedJSX.match(/<LayoutPDF[\s\S]*?>([\s\S]*?)<\/LayoutPDF>/)
425
+ const jsxContent = jsxMatch ? jsxMatch[1] : processedJSX
426
+
427
+ const tokens = jsxContent.match(/<[^>]+>|[^<]+/g) || []
428
+ const nodes: ParsedNode[] = []
429
+ const stack: ParsedNode[] = []
430
+
431
+ // Procesamos el componente LayoutPDF por separado para extraer sus props
432
+ const layoutPDFMatch = processedJSX.match(/<LayoutPDF([^>]*)>/)
433
+ if (layoutPDFMatch) {
434
+ const layoutProps: Record<string, any> = {}
435
+ const propsString = layoutPDFMatch[1]
436
+
437
+ // Extraemos props regulares (valores en cadena)
438
+ const regularProps = Array.from(propsString.matchAll(/(\w+)="([^"]*)"/g))
439
+ regularProps.forEach(([_, key, value]) => {
440
+ layoutProps[key] = value
441
+ })
442
+
443
+ // Extraemos props booleanas
444
+ const booleanProps = Array.from(propsString.matchAll(/(\w+)=\{(true|false)\}/g))
445
+ booleanProps.forEach(([_, key, value]) => {
446
+ layoutProps[key] = value === "true"
447
+ })
448
+
449
+ // Extraemos props numéricas
450
+ const numericProps = Array.from(propsString.matchAll(/(\w+)=\{(\d+(?:\.\d+)?)\}/g))
451
+ numericProps.forEach(([_, key, value]) => {
452
+ layoutProps[key] = Number(value)
453
+ })
454
+
455
+ // Creamos el nodo LayoutPDF
456
+ const layoutNode: ParsedNode = {
457
+ type: "LayoutPDF",
458
+ content: [],
459
+ props: layoutProps,
460
+ }
461
+
462
+ // Procesamos cada token dentro de LayoutPDF
463
+ tokens.forEach((token) => {
464
+ if (token.startsWith("</")) {
465
+ // Etiqueta de cierre
466
+ if (stack.length > 0) {
467
+ const node = stack.pop()!
468
+ if (stack.length === 0) {
469
+ layoutNode.content.push(node)
470
+ } else {
471
+ stack[stack.length - 1].content.push(node)
472
+ }
473
+ }
474
+ } else if (token.startsWith("<")) {
475
+ // Etiqueta de apertura
476
+ const tagMatch = token.match(/<(\w+)([^>]*)>/) || []
477
+ const tagName = tagMatch[1]
478
+ const propsString = tagMatch[2] || ""
479
+ const props: Record<string, any> = {}
480
+
481
+ // Extraemos atributos regulares
482
+ const regularProps = Array.from(propsString.matchAll(/(\w+)="([^"]*)"/g))
483
+ regularProps.forEach(([_, key, value]) => {
484
+ props[key] = processExpressions(value)
485
+ })
486
+
487
+ // Extraemos objetos de estilo
488
+ const styleMatch = propsString.match(/style=\{\{([^}]*)\}\}/)
489
+ if (styleMatch) {
490
+ try {
491
+ const styleString = `{${styleMatch[1]}}`
492
+ const styleObj = new Function(`return ${styleString}`)()
493
+ props.style = styleObj
494
+ } catch (error) {
495
+ console.error("Error parsing style object:", error)
496
+ }
497
+ }
498
+
499
+ // Extract key prop if it exists
500
+ const keyMatch = propsString.match(/key=\{([^}]*)\}/)
501
+ let keyValue = undefined
502
+ if (keyMatch) {
503
+ try {
504
+ const keyString = keyMatch[1]
505
+ keyValue = new Function(`return ${keyString}`)()
506
+ // Remove key from props as it will be handled separately
507
+ delete props.key
508
+ } catch (error) {
509
+ console.error("Error parsing key prop:", error)
510
+ }
511
+ }
512
+
513
+ const node: ParsedNode = {
514
+ type: tagName,
515
+ content: [],
516
+ props,
517
+ }
518
+
519
+ // Add key as a separate property if it exists
520
+ if (keyValue !== undefined) {
521
+ // Ensure props exists before setting a property on it
522
+ if (!node.props) {
523
+ node.props = {}
524
+ }
525
+ node.props.key = keyValue
526
+ }
527
+
528
+ stack.push(node)
529
+ } else {
530
+ // Contenido de texto o expresión
531
+ if (stack.length > 0) {
532
+ handleTextToken(token, stack[stack.length - 1])
533
+ } else {
534
+ handleTextToken(token, layoutNode)
535
+ }
536
+ }
537
+ })
538
+
539
+ nodes.push(layoutNode)
540
+ return nodes
541
+ }
542
+
543
+ // Si no se encontró LayoutPDF, se procesan los tokens normalmente
544
+ tokens.forEach((token) => {
545
+ if (token.startsWith("</")) {
546
+ // Etiqueta de cierre
547
+ if (stack.length > 0) {
548
+ const node = stack.pop()!
549
+ if (stack.length === 0) {
550
+ nodes.push(node)
551
+ } else {
552
+ stack[stack.length - 1].content.push(node)
553
+ }
554
+ }
555
+ } else if (token.startsWith("<")) {
556
+ // Etiqueta de apertura
557
+ const tagMatch = token.match(/<(\w+)([^>]*)>/) || []
558
+ const tagName = tagMatch[1]
559
+ const propsString = tagMatch[2] || ""
560
+ const props: Record<string, any> = {}
561
+
562
+ // Procesamos atributos regulares
563
+ const regularProps = Array.from(propsString.matchAll(/(\w+)="([^"]*)"/g))
564
+ regularProps.forEach(([_, key, value]) => {
565
+ props[key] = processExpressions(value)
566
+ })
567
+
568
+ // Procesamos objetos de estilo
569
+ const styleMatch = propsString.match(/style=\{\{([^}]*)\}\}/)
570
+ if (styleMatch) {
571
+ try {
572
+ const styleString = `{${styleMatch[1]}}`
573
+ const styleObj = new Function(`return ${styleString}`)()
574
+ props.style = styleObj
575
+ } catch (error) {
576
+ console.error("Error parsing style object:", error)
577
+ }
578
+ }
579
+
580
+ // Extract key prop if it exists
581
+ const keyMatch = propsString.match(/key=\{([^}]*)\}/)
582
+ let keyValue = undefined
583
+ if (keyMatch) {
584
+ try {
585
+ const keyString = keyMatch[1]
586
+ keyValue = new Function(`return ${keyString}`)()
587
+ // Remove key from props as it will be handled separately
588
+ delete props.key
589
+ } catch (error) {
590
+ console.error("Error parsing key prop:", error)
591
+ }
592
+ }
593
+
594
+ const node: ParsedNode = {
595
+ type: tagName,
596
+ content: [],
597
+ props,
598
+ }
599
+
600
+ // Add key as a separate property if it exists
601
+ if (keyValue !== undefined) {
602
+ // Ensure props exists before setting a property on it
603
+ if (!node.props) {
604
+ node.props = {}
605
+ }
606
+ node.props.key = keyValue
607
+ }
608
+
609
+ stack.push(node)
610
+ } else {
611
+ // Contenido de texto o expresión
612
+ if (stack.length > 0) {
613
+ handleTextToken(token, stack[stack.length - 1])
614
+ }
615
+ }
616
+ })
617
+
618
+ return nodes
619
+ }
620
+
@@ -0,0 +1,17 @@
1
+ // services/apiService.ts
2
+ export const fetchTemplate = async (): Promise<{ content: string }> => {
3
+ const response = await fetch("/api/readFile");
4
+ const data = await response.json();
5
+ return data;
6
+ };
7
+
8
+ export const saveDocumentAPI = async (filename: string, content: string): Promise<any> => {
9
+ const response = await fetch("/api/saveFile", {
10
+ method: "POST",
11
+ headers: { "Content-Type": "application/json" },
12
+ body: JSON.stringify({ filename, content }),
13
+ });
14
+ const result = await response.json();
15
+ return result;
16
+ };
17
+