Skip to content
This repository was archived by the owner on Jan 30, 2026. It is now read-only.

Commit 42faccc

Browse files
authored
feat: add import functionality for .docx files in the template builder (#15)
This commit introduces a new import feature that allows users to upload .docx files. It includes UI updates for the import button and error handling for invalid file types. The toolbar now displays an error message if the import fails, enhancing user experience.
1 parent 01f0bf9 commit 42faccc

2 files changed

Lines changed: 100 additions & 3 deletions

File tree

demo/src/App.css

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ header {
8383
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
8484
}
8585

86+
.toolbar-right {
87+
display: flex;
88+
align-items: center;
89+
gap: 0.75rem;
90+
}
91+
8692
.toolbar-left {
8793
display: flex;
8894
align-items: center;
@@ -113,6 +119,38 @@ header {
113119
background: #0051d5;
114120
}
115121

122+
.export-button:disabled,
123+
.import-button:disabled {
124+
cursor: not-allowed;
125+
opacity: 0.7;
126+
}
127+
128+
.import-button {
129+
padding: 0.5rem 1.25rem;
130+
background: white;
131+
color: #007aff;
132+
border: 1px solid #b0d4ff;
133+
border-radius: 8px;
134+
font-weight: 600;
135+
cursor: pointer;
136+
transition: background 0.2s, color 0.2s, border-color 0.2s;
137+
}
138+
139+
.import-button:hover {
140+
background: #e8f2ff;
141+
border-color: #007aff;
142+
}
143+
144+
.toolbar-error {
145+
margin-top: 0.75rem;
146+
padding: 0.75rem 1rem;
147+
background: #fef2f2;
148+
color: #b91c1c;
149+
border: 1px solid #fecaca;
150+
border-radius: 8px;
151+
font-size: 0.875rem;
152+
}
153+
116154
/* Template Builder Container */
117155
.superdoc-template-builder {
118156
background: white;

demo/src/App.tsx

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ export function App() {
4646
const [fields, setFields] = useState<TemplateField[]>([]);
4747
const [events, setEvents] = useState<string[]>([]);
4848
const [isDownloading, setIsDownloading] = useState(false);
49+
const [isImporting, setIsImporting] = useState(false);
50+
const [importError, setImportError] = useState<string | null>(null);
51+
const [documentSource, setDocumentSource] = useState<string | File>(
52+
"https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement.docx",
53+
);
4954
const builderRef = useRef<SuperDocTemplateBuilderHandle>(null);
55+
const fileInputRef = useRef<HTMLInputElement>(null);
56+
const importingRef = useRef(false);
5057

5158
const log = useCallback((msg: string) => {
5259
const time = new Date().toLocaleTimeString();
@@ -75,6 +82,12 @@ export function App() {
7582

7683
const handleReady = useCallback(() => {
7784
log('✓ Template builder ready');
85+
if (importingRef.current) {
86+
log('📄 Document imported');
87+
importingRef.current = false;
88+
setImportError(null);
89+
setIsImporting(false);
90+
}
7891
}, [log]);
7992

8093
const handleTrigger = useCallback(() => {
@@ -114,9 +127,35 @@ export function App() {
114127
};
115128

116129
const documentConfig = useMemo(() => ({
117-
source: "https://storage.googleapis.com/public_static_hosting/public_demo_docs/service_agreement.docx",
130+
source: documentSource,
118131
mode: 'editing' as const
119-
}), []);
132+
}), [documentSource]);
133+
134+
const handleImportButtonClick = useCallback(() => {
135+
if (isImporting) return;
136+
fileInputRef.current?.click();
137+
}, [isImporting]);
138+
139+
const handleFileInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
140+
const file = event.target.files?.[0];
141+
event.target.value = "";
142+
143+
if (!file) return;
144+
145+
const extension = file.name.split('.').pop()?.toLowerCase();
146+
if (extension !== 'docx') {
147+
const message = 'Invalid file type. Please choose a .docx file.';
148+
setImportError(message);
149+
log('⚠️ ' + message);
150+
return;
151+
}
152+
153+
importingRef.current = true;
154+
setImportError(null);
155+
setIsImporting(true);
156+
setDocumentSource(file);
157+
log(`📥 Importing "${file.name}"`);
158+
}, [log]);
120159

121160
const fieldsConfig = useMemo(() => ({
122161
available: availableFields,
@@ -160,16 +199,36 @@ export function App() {
160199
<span className="hint">Tab/Shift+Tab to navigate</span>
161200
</div>
162201
<div className="toolbar-right">
202+
<input
203+
type="file"
204+
accept=".docx"
205+
ref={fileInputRef}
206+
style={{ display: 'none' }}
207+
onChange={handleFileInputChange}
208+
/>
209+
<button
210+
onClick={handleImportButtonClick}
211+
className="import-button"
212+
disabled={isImporting || isDownloading}
213+
>
214+
{isImporting ? 'Importing…' : 'Import File'}
215+
</button>
163216
<button
164217
onClick={handleExportTemplate}
165218
className="export-button"
166-
disabled={isDownloading}
219+
disabled={isDownloading || isImporting}
167220
>
168221
{isDownloading ? "Exporting..." : "Export Template"}
169222
</button>
170223
</div>
171224
</div>
172225

226+
{importError && (
227+
<div className="toolbar-error" role="alert">
228+
{importError}
229+
</div>
230+
)}
231+
173232
<SuperDocTemplateBuilder
174233
ref={builderRef}
175234
document={documentConfig}

0 commit comments

Comments
 (0)