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.
- package/README.md +40 -0
- package/fn/aggAlias.js +21 -0
- package/fn/moveComponents.js +129 -0
- package/fn/postinstall.js +66 -0
- package/fn/upVersion.js +12 -0
- package/fn/updateTsconfig.js +10 -0
- package/next.config.mjs +6 -0
- package/package.json +39 -0
- package/public/bg-login.jpg +0 -0
- package/public/codigo_guardado.js +1 -0
- package/public/css/style.css +751 -0
- package/public/dboard/logo.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/fonts/TimesNewerRoman-Bold.otf +0 -0
- package/public/fonts/TimesNewerRoman-BoldItalic.otf +0 -0
- package/public/fonts/TimesNewerRoman-Italic.otf +0 -0
- package/public/fonts/TimesNewerRoman-Regular.otf +0 -0
- package/public/header-pdf.jpg +0 -0
- package/public/home/bgHome.jpg +0 -0
- package/public/home/bgHome2.jpg +0 -0
- package/public/home/download.jpg +0 -0
- package/public/home/undraw_elements_re_25t9.svg +1 -0
- package/public/home/undraw_project_completed_re_jr7u.svg +1 -0
- package/public/home/undraw_shared_goals_re_jvqd.svg +1 -0
- package/public/home/undraw_spread_love_re_v3cl.svg +1 -0
- package/public/home/undraw_target_re_fi8j.svg +1 -0
- package/public/home/undraw_visionary_technology_re_jfp7.svg +1 -0
- package/public/logo.png +0 -0
- package/public/marca/logo.svg +1 -0
- package/src/components/PDF/components/DocumentoTemplate.tsx +140 -0
- package/src/components/PDF/components/PDFContent.tsx +192 -0
- package/src/components/PDF/core/Etiquetas.tsx +152 -0
- package/src/components/PDF/core/Grid.tsx +101 -0
- package/src/components/PDF/core/Img.tsx +22 -0
- package/src/components/PDF/core/LayoutPDF.tsx +186 -0
- package/src/components/PDF/core/Listas.tsx +10 -0
- package/src/components/PDF/core/MakePDF.tsx +50 -0
- package/src/components/PDF/core/PageElements.tsx +50 -0
- package/src/components/PDF/core/Position.tsx +33 -0
- package/src/components/PDF/core/Tablet.tsx +121 -0
- package/src/components/PDF/core/index.tsx +56 -0
- package/src/components/PDF/lib/pdfParser.ts +620 -0
- package/src/components/PDF/services/apiService.ts +17 -0
- package/src/components/PDF/templates/AllTemplate.tsx +134 -0
- package/src/components/PDF/templates/BusinessCardTemplate.tsx +139 -0
- package/src/components/PDF/templates/CertificateTemplate.tsx +31 -0
- package/src/components/PDF/templates/HeaderFooterTemplate.tsx +61 -0
- package/src/components/PDF/templates/InvoiceTemplate.tsx +53 -0
- package/src/components/PDF/templates/ProposalTemplate.tsx +246 -0
- package/src/components/PDF/templates/ReportTemplate.tsx +57 -0
- package/src/components/PDF/templates/ResumeTemplate.tsx +170 -0
- package/src/components/PDF/templates/TablasTemplate.tsx +307 -0
- package/src/components/PDF/templates/index.ts +9 -0
- package/src/components/PDF/view/ColorPicker.tsx +147 -0
- package/src/components/PDF/view/Header.tsx +102 -0
- package/src/components/PDF/view/HomePDF.tsx +177 -0
- package/src/components/PDF/view/SettingsPanel.tsx +703 -0
- package/src/pages/AllTemplate.tsx +53 -0
- package/src/pages/Documento2.tsx +45 -0
- package/src/pages/_app.tsx +6 -0
- package/src/pages/_document.tsx +13 -0
- package/src/pages/api/generatePDF.ts +74 -0
- package/src/pages/api/hello.ts +13 -0
- package/src/pages/api/readFile.ts +18 -0
- package/src/pages/api/readTemplateFile.ts +26 -0
- package/src/pages/api/save.ts +22 -0
- package/src/pages/api/saveFile.ts +20 -0
- package/src/pages/index.tsx +13 -0
- package/src/pages/template/[template].tsx +250 -0
- package/tsconfig.json +63 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type React from "react"
|
|
4
|
+
import { useState, useRef, useEffect } from "react"
|
|
5
|
+
import { Palette } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
interface ColorPickerProps {
|
|
8
|
+
onColorSelect?: (color: string) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ColorPicker: React.FC<ColorPickerProps> = ({ onColorSelect }) => {
|
|
12
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
13
|
+
const [selectedColor, setSelectedColor] = useState("#3366cc")
|
|
14
|
+
const [recentColors, setRecentColors] = useState<string[]>(["#3366cc", "#dc3545", "#28a745", "#ffc107", "#17a2b8"])
|
|
15
|
+
const [copied, setCopied] = useState(false)
|
|
16
|
+
const colorPickerRef = useRef<HTMLDivElement>(null)
|
|
17
|
+
|
|
18
|
+
// Predefined color palette
|
|
19
|
+
const colorPalette = [
|
|
20
|
+
"#000000",
|
|
21
|
+
"#343a40",
|
|
22
|
+
"#495057",
|
|
23
|
+
"#6c757d",
|
|
24
|
+
"#adb5bd",
|
|
25
|
+
"#ced4da",
|
|
26
|
+
"#dee2e6",
|
|
27
|
+
"#e9ecef",
|
|
28
|
+
"#f8f9fa",
|
|
29
|
+
"#ffffff",
|
|
30
|
+
"#007bff",
|
|
31
|
+
"#0056b3",
|
|
32
|
+
"#6610f2",
|
|
33
|
+
"#6f42c1",
|
|
34
|
+
"#e83e8c",
|
|
35
|
+
"#dc3545",
|
|
36
|
+
"#fd7e14",
|
|
37
|
+
"#ffc107",
|
|
38
|
+
"#28a745",
|
|
39
|
+
"#20c997",
|
|
40
|
+
"#17a2b8",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
// Close color picker when clicking outside
|
|
45
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
46
|
+
if (colorPickerRef.current && !colorPickerRef.current.contains(event.target as Node)) {
|
|
47
|
+
setIsOpen(false)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
document.addEventListener("mousedown", handleClickOutside)
|
|
52
|
+
return () => {
|
|
53
|
+
document.removeEventListener("mousedown", handleClickOutside)
|
|
54
|
+
}
|
|
55
|
+
}, [])
|
|
56
|
+
|
|
57
|
+
const handleColorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
58
|
+
const color = e.target.value
|
|
59
|
+
setSelectedColor(color)
|
|
60
|
+
if (onColorSelect) {
|
|
61
|
+
onColorSelect(color)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const selectColor = (color: string) => {
|
|
66
|
+
setSelectedColor(color)
|
|
67
|
+
if (onColorSelect) {
|
|
68
|
+
onColorSelect(color)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Add to recent colors if not already there
|
|
72
|
+
if (!recentColors.includes(color)) {
|
|
73
|
+
setRecentColors((prev) => [color, ...prev.slice(0, 4)])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const copyToClipboard = () => {
|
|
78
|
+
navigator.clipboard.writeText(selectedColor)
|
|
79
|
+
setCopied(true)
|
|
80
|
+
setTimeout(() => setCopied(false), 1500)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className="color-picker-container" ref={colorPickerRef}>
|
|
85
|
+
<button className="btn btn-icon tooltip" data-tooltip="Color Picker" onClick={() => setIsOpen(!isOpen)}>
|
|
86
|
+
<Palette size={20} style={{ color: selectedColor }} />
|
|
87
|
+
</button>
|
|
88
|
+
|
|
89
|
+
{isOpen && (
|
|
90
|
+
<div className="color-picker-panel">
|
|
91
|
+
<div className="color-picker-header">
|
|
92
|
+
<h3>Color Picker</h3>
|
|
93
|
+
<div className="selected-color-preview" style={{ backgroundColor: selectedColor }}></div>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="color-input-container">
|
|
97
|
+
<input type="color" value={selectedColor} onChange={handleColorChange} className="color-input" />
|
|
98
|
+
<div className="hex-value-container">
|
|
99
|
+
<input
|
|
100
|
+
type="text"
|
|
101
|
+
value={selectedColor}
|
|
102
|
+
onChange={(e) => selectColor(e.target.value)}
|
|
103
|
+
className="hex-input"
|
|
104
|
+
/>
|
|
105
|
+
<button className="copy-button" onClick={copyToClipboard}>
|
|
106
|
+
{copied ? "Copied!" : "Copy"}
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div className="color-section">
|
|
112
|
+
<h4>Recent Colors</h4>
|
|
113
|
+
<div className="color-grid">
|
|
114
|
+
{recentColors.map((color, index) => (
|
|
115
|
+
<div
|
|
116
|
+
key={`recent-${index}`}
|
|
117
|
+
className="color-swatch"
|
|
118
|
+
style={{ backgroundColor: color }}
|
|
119
|
+
onClick={() => selectColor(color)}
|
|
120
|
+
title={color}
|
|
121
|
+
></div>
|
|
122
|
+
))}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div className="color-section">
|
|
127
|
+
<h4>Color Palette</h4>
|
|
128
|
+
<div className="color-grid">
|
|
129
|
+
{colorPalette.map((color, index) => (
|
|
130
|
+
<div
|
|
131
|
+
key={`palette-${index}`}
|
|
132
|
+
className="color-swatch"
|
|
133
|
+
style={{ backgroundColor: color }}
|
|
134
|
+
onClick={() => selectColor(color)}
|
|
135
|
+
title={color}
|
|
136
|
+
></div>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
</div>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default ColorPicker
|
|
147
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type React from "react"
|
|
4
|
+
import { useState } from "react"
|
|
5
|
+
import Link from "next/link"
|
|
6
|
+
import { Save, FileText, Settings } from "lucide-react"
|
|
7
|
+
import ColorPicker from "./ColorPicker"
|
|
8
|
+
import SettingsPanel, { type LayoutSettings } from "./SettingsPanel"
|
|
9
|
+
|
|
10
|
+
interface HeaderProps {
|
|
11
|
+
title: string
|
|
12
|
+
showSaveModal: () => void
|
|
13
|
+
selectedTemplate?: string
|
|
14
|
+
onTemplateChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void
|
|
15
|
+
isTemplatePage?: boolean
|
|
16
|
+
currentCode?: string
|
|
17
|
+
onApplySettings?: (settings: LayoutSettings) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Header: React.FC<HeaderProps> = ({
|
|
21
|
+
title,
|
|
22
|
+
showSaveModal,
|
|
23
|
+
selectedTemplate,
|
|
24
|
+
onTemplateChange,
|
|
25
|
+
isTemplatePage = false,
|
|
26
|
+
currentCode = "",
|
|
27
|
+
onApplySettings = () => { },
|
|
28
|
+
}) => {
|
|
29
|
+
const [isSettingsPanelOpen, setIsSettingsPanelOpen] = useState(false)
|
|
30
|
+
|
|
31
|
+
const handleColorSelect = (color: string) => {
|
|
32
|
+
// You can implement additional functionality here if needed
|
|
33
|
+
console.log("Selected color:", color)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleTemplateChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
37
|
+
const value = event.target.value
|
|
38
|
+
const urls: { [key: string]: string } = {
|
|
39
|
+
allTemplate: '/template/allTemplate',
|
|
40
|
+
invoice: '/template/invoice',
|
|
41
|
+
report: '/template/report',
|
|
42
|
+
certificate: '/template/certificate',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (value !== 'default' && urls[value]) {
|
|
46
|
+
window.open(urls[value], '_blank')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (onTemplateChange) {
|
|
50
|
+
onTemplateChange(event)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<header className="header">
|
|
57
|
+
<div className="header-title">
|
|
58
|
+
<FileText size={24} />
|
|
59
|
+
{title}
|
|
60
|
+
</div>
|
|
61
|
+
<div className="header-tools">
|
|
62
|
+
<ColorPicker onColorSelect={handleColorSelect} />
|
|
63
|
+
<button
|
|
64
|
+
className="btn btn-icon tooltip"
|
|
65
|
+
data-tooltip="Configuración"
|
|
66
|
+
onClick={() => setIsSettingsPanelOpen(true)}
|
|
67
|
+
>
|
|
68
|
+
<Settings size={20} />
|
|
69
|
+
</button>
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
<>
|
|
73
|
+
<select value={selectedTemplate} onChange={handleTemplateChange} className="template-select">
|
|
74
|
+
<option value="default">Template por defecto</option>
|
|
75
|
+
<option value="allTemplate">AllTemplate</option>
|
|
76
|
+
<option value="invoice">Factura</option>
|
|
77
|
+
<option value="report">Reporte</option>
|
|
78
|
+
<option value="certificate">Certificado</option>
|
|
79
|
+
</select>
|
|
80
|
+
</>
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
<button className="btn btn-primary" onClick={showSaveModal}>
|
|
85
|
+
<Save size={20} />
|
|
86
|
+
Guardar
|
|
87
|
+
</button>
|
|
88
|
+
</div>
|
|
89
|
+
</header>
|
|
90
|
+
|
|
91
|
+
<SettingsPanel
|
|
92
|
+
isOpen={isSettingsPanelOpen}
|
|
93
|
+
onClose={() => setIsSettingsPanelOpen(false)}
|
|
94
|
+
onApplySettings={onApplySettings}
|
|
95
|
+
currentCode={currentCode}
|
|
96
|
+
/>
|
|
97
|
+
</>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default Header
|
|
102
|
+
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type React from "react"
|
|
2
|
+
import { useState, useEffect, useRef } from "react"
|
|
3
|
+
import { Editor } from "@monaco-editor/react"
|
|
4
|
+
import dynamic from "next/dynamic"
|
|
5
|
+
import PDFContent from "../components/PDFContent"
|
|
6
|
+
import Header from "./Header"
|
|
7
|
+
import { Save } from "lucide-react"
|
|
8
|
+
import type { LayoutSettings } from "./SettingsPanel"
|
|
9
|
+
|
|
10
|
+
const PDFViewer = dynamic(() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer), { ssr: false })
|
|
11
|
+
|
|
12
|
+
const PlaygroundPDF: React.FC = () => {
|
|
13
|
+
const [code, setCode] = useState<string>("")
|
|
14
|
+
const [showModal, setShowModal] = useState<boolean>(false)
|
|
15
|
+
const [filename, setFilename] = useState<string>("Documento2.tsx")
|
|
16
|
+
const [selectedTemplate, setSelectedTemplate] = useState<string>("default")
|
|
17
|
+
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const fetchTemplate = async () => {
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch("/api/readFile")
|
|
23
|
+
const data = await response.json()
|
|
24
|
+
setCode(data.content)
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("Error al leer el archivo:", error)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fetchTemplate()
|
|
31
|
+
}, [])
|
|
32
|
+
|
|
33
|
+
const saveDocument = async () => {
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch("/api/saveFile", {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
body: JSON.stringify({ filename, content: code }),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const result = await response.json()
|
|
42
|
+
console.log(result.message)
|
|
43
|
+
setShowModal(false)
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("Error al guardar el archivo:", error)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const handleEditorChange = (value: string | undefined) => {
|
|
50
|
+
if (timeoutRef.current) {
|
|
51
|
+
clearTimeout(timeoutRef.current)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
timeoutRef.current = setTimeout(() => {
|
|
55
|
+
setCode(value || "")
|
|
56
|
+
}, 1000)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const handleTemplateChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
60
|
+
setSelectedTemplate(event.target.value)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const handleApplySettings = (settings: LayoutSettings) => {
|
|
64
|
+
const updatedCode = generateUpdatedLayoutCode(code, settings)
|
|
65
|
+
setCode(updatedCode)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const generateUpdatedLayoutCode = (originalCode: string, settings: LayoutSettings): string => {
|
|
69
|
+
try {
|
|
70
|
+
const layoutPDFRegex = /<LayoutPDF[^>]*>/
|
|
71
|
+
const layoutPDFMatch = originalCode.match(layoutPDFRegex)
|
|
72
|
+
|
|
73
|
+
if (!layoutPDFMatch) {
|
|
74
|
+
console.error("Could not find LayoutPDF component in the code")
|
|
75
|
+
return originalCode
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let newProps = `size="${settings.pageSize}" orientation="${settings.orientation}"`
|
|
79
|
+
|
|
80
|
+
if (settings.backgroundColor !== "white") {
|
|
81
|
+
newProps += ` backgroundColor="${settings.backgroundColor}"`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (settings.customPadding) {
|
|
85
|
+
newProps += ` paddingTop={${settings.paddingTop}} paddingRight={${settings.paddingRight}} paddingBottom={${settings.paddingBottom}} paddingLeft={${settings.paddingLeft}}`
|
|
86
|
+
} else {
|
|
87
|
+
newProps += ` padding={${settings.padding}}`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (settings.fontFamily !== "Times New Roman") {
|
|
91
|
+
newProps += ` fontFamily="${settings.fontFamily}"`
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (settings.fontSize !== 12) {
|
|
95
|
+
newProps += ` fontSize={${settings.fontSize}}`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (settings.lineHeight !== 1.5) {
|
|
99
|
+
newProps += ` lineHeight={${settings.lineHeight}}`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
newProps += ` showPageNumbers={${settings.showPageNumbers}}`
|
|
103
|
+
|
|
104
|
+
const newLayoutPDFTag = `<LayoutPDF ${newProps}>`
|
|
105
|
+
|
|
106
|
+
return originalCode.replace(layoutPDFRegex, newLayoutPDFTag)
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error("Error updating layout code:", error)
|
|
109
|
+
return originalCode
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log("code", code)
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<>
|
|
117
|
+
<Header
|
|
118
|
+
title="PDF Editor"
|
|
119
|
+
showSaveModal={() => setShowModal(true)}
|
|
120
|
+
selectedTemplate={selectedTemplate}
|
|
121
|
+
onTemplateChange={handleTemplateChange}
|
|
122
|
+
currentCode={code}
|
|
123
|
+
onApplySettings={handleApplySettings}
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
<div className="container-playground">
|
|
127
|
+
<div className="container-editor fade-in">
|
|
128
|
+
<Editor
|
|
129
|
+
theme="vs-dark"
|
|
130
|
+
value={code}
|
|
131
|
+
defaultLanguage="javascript"
|
|
132
|
+
onChange={handleEditorChange}
|
|
133
|
+
options={{
|
|
134
|
+
minimap: { enabled: true },
|
|
135
|
+
fontSize: 14,
|
|
136
|
+
wordWrap: "on",
|
|
137
|
+
automaticLayout: true,
|
|
138
|
+
lineNumbers: "on",
|
|
139
|
+
scrollBeyondLastLine: false,
|
|
140
|
+
smoothScrolling: true,
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
<div className="container-render fade-in">
|
|
145
|
+
{typeof window !== "undefined" && (
|
|
146
|
+
<PDFViewer width="100%" height="100%">
|
|
147
|
+
<PDFContent content={code} />
|
|
148
|
+
</PDFViewer>
|
|
149
|
+
)}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div className={`modal ${showModal ? "show" : ""}`}>
|
|
154
|
+
<div className="modal-content">
|
|
155
|
+
<h2>Guardar Documento</h2>
|
|
156
|
+
<input
|
|
157
|
+
type="text"
|
|
158
|
+
value={filename}
|
|
159
|
+
onChange={(e) => setFilename(e.target.value)}
|
|
160
|
+
placeholder="Nombre del archivo"
|
|
161
|
+
/>
|
|
162
|
+
<div>
|
|
163
|
+
<button className="btn btn-primary" onClick={saveDocument}>
|
|
164
|
+
<Save size={20} />
|
|
165
|
+
Guardar
|
|
166
|
+
</button>
|
|
167
|
+
<button className="btn btn-outline" onClick={() => setShowModal(false)}>
|
|
168
|
+
Cancelar
|
|
169
|
+
</button>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default PlaygroundPDF
|