1- import { useState , useRef } from 'react'
1+ import { useState , useRef , useEffect } from 'react'
22import { INITIAL_STATE } from './constants/initialState'
33import { generateRandomData } from './utils/randomData'
4- import { exportPayslipToPdf , exportToPng , exportToZip } from './utils/pdfExport'
4+ import { exportPayslipToPdf , exportToPng , exportToZip , exportTeacherCardToPdf , getTeacherCardPdfBase64 } from './utils/pdfExport'
55import Editor from './components/Editor'
66import Preview from './components/Preview'
77import './index.css'
@@ -10,9 +10,38 @@ function App() {
1010 const [ state , setState ] = useState ( INITIAL_STATE )
1111 const [ docType , setDocType ] = useState ( 'payslip' ) // 'payslip', 'tax', 'employment'
1212 const [ mode , setMode ] = useState ( 'employee' ) // 'employee' or 'contractor'
13+ const [ cardStyle , setCardStyle ] = useState ( 'original' ) // 'original', 'modern', 'simple'
1314 const [ companyLogo , setCompanyLogo ] = useState ( null )
15+ const [ photoBase64 , setPhotoBase64 ] = useState ( null )
1416 const logoInputRef = useRef ( null )
1517
18+ // Cloudflare Worker URL for photo proxying
19+ const WORKER_URL = 'https://student-id-photo-proxy.thanhnguyxn.workers.dev' ;
20+
21+ const fetchPhoto = async ( gender = 'male' ) => {
22+ try {
23+ const response = await fetch ( `${ WORKER_URL } ?gender=${ gender } ` ) ;
24+ if ( ! response . ok ) throw new Error ( 'Photo fetch failed' ) ;
25+
26+ const blob = await response . blob ( ) ;
27+ const reader = new FileReader ( ) ;
28+ reader . onloadend = ( ) => {
29+ setPhotoBase64 ( reader . result ) ;
30+ } ;
31+ reader . readAsDataURL ( blob ) ;
32+ } catch ( error ) {
33+ console . error ( 'Failed to fetch photo:' , error ) ;
34+ // Fallback to Dicebear if worker fails
35+ const seed = Math . random ( ) . toString ( 36 ) . substring ( 7 ) ;
36+ setPhotoBase64 ( `https://api.dicebear.com/7.x/avataaars/svg?seed=${ seed } ` ) ;
37+ }
38+ } ;
39+
40+ // Fetch initial photo
41+ useEffect ( ( ) => {
42+ fetchPhoto ( ) ;
43+ } , [ ] ) ;
44+
1645 const handleChange = ( section , field , value ) => {
1746 setState ( prev => ( {
1847 ...prev ,
@@ -58,6 +87,11 @@ function App() {
5887 }
5988 }
6089
90+ // Expose PDF generation to window for Puppeteer
91+ useEffect ( ( ) => {
92+ window . getTeacherCardPdfBase64 = ( ) => getTeacherCardPdfBase64 ( state ) ;
93+ } , [ state ] ) ;
94+
6195 return (
6296 < div className = "app-container" >
6397 { /* Modern Navigation Bar */ }
@@ -125,18 +159,43 @@ function App() {
125159 >
126160 Teacher ID
127161 </ button >
162+
163+ { /* Style Selector */ }
164+ { ( docType === 'teacherCard' ) && (
165+ < select
166+ value = { cardStyle }
167+ onChange = { ( e ) => setCardStyle ( e . target . value ) }
168+ style = { {
169+ marginLeft : '1rem' ,
170+ padding : '8px 12px' ,
171+ borderRadius : '8px' ,
172+ border : 'none' ,
173+ background : 'rgba(255,255,255,0.2)' ,
174+ color : 'white' ,
175+ fontSize : '0.9rem' ,
176+ cursor : 'pointer'
177+ } }
178+ >
179+ < option value = "original" style = { { color : '#333' } } > 🎨 Original</ option >
180+ < option value = "modern" style = { { color : '#333' } } > ✨ Modern</ option >
181+ < option value = "simple" style = { { color : '#333' } } > 📄 Simple</ option >
182+ </ select >
183+ ) }
128184 </ div >
129185
130186 < div className = "nav-right" >
131187 < button
132188 className = "action-btn primary"
133- onClick = { ( ) => setState ( generateRandomData ( ) ) }
189+ onClick = { ( ) => {
190+ setState ( generateRandomData ( ) ) ;
191+ fetchPhoto ( Math . random ( ) > 0.5 ? 'male' : 'female' ) ;
192+ } }
134193 >
135194 🎲 Random
136195 </ button >
137196 < button
138197 className = "action-btn secondary"
139- onClick = { ( ) => exportPayslipToPdf ( state ) }
198+ onClick = { ( ) => docType === 'teacherCard' ? exportTeacherCardToPdf ( state ) : exportPayslipToPdf ( state ) }
140199 title = "Download current document as PDF"
141200 >
142201 📄 PDF
@@ -184,6 +243,8 @@ function App() {
184243 docType = { docType }
185244 mode = { mode }
186245 companyLogo = { companyLogo }
246+ cardStyle = { cardStyle }
247+ photoBase64 = { photoBase64 }
187248 />
188249 </ main >
189250 </ div >
0 commit comments