React frontend for Kwartzlab's reimbursement request and purchase approval forms. Provides a user-friendly interface for submitting expenses with file attachments.
- Two form types with dynamic field switching:
- Reimbursement Request (with HST calculation)
- Purchase Approval
- Dynamic expense table with add/remove rows
- File upload with validation (type, size, total size)
- Real-time HST calculation for Reimbursement Requests
- Client-side validation matching backend rules
- hCaptcha integration
- Responsive design with Tailwind CSS
- Auto-clearing success/error messages
- Character counters for text fields
- Node.js 14+
- npm or yarn
- Backend API running (locally or deployed)
- hCaptcha site key
# Clone the repository
git clone <repository-url>
cd frontend
# Install dependencies
npm installCreate a .env file in the project root:
# Backend API URL (leave empty for local backend on port 5000)
REACT_APP_API_URL=
# hCaptcha Site Key (get from https://dashboard.hcaptcha.com/)
REACT_APP_HCAPTCHA_SITEKEY=your_hcaptcha_site_keyFor local development with backend on localhost:5000:
# Leave REACT_APP_API_URL empty or explicitly set it
REACT_APP_API_URL=http://localhost:5000
REACT_APP_HCAPTCHA_SITEKEY=your_site_keyImportant Notes:
- If
REACT_APP_API_URLis not set, it defaults tohttp://localhost:5000 - For production, set
REACT_APP_API_URLto your deployed backend URL - Get your hCaptcha site key from hCaptcha Dashboard
- The site key is public and safe to commit (unlike the secret key used in backend)
# Start development server
npm start
# Application will open at http://localhost:3000- Navigate to http://localhost:3000
- Select form type (Reimbursement Request or Purchase Approval)
- Fill in contact information
- Add expense line items
- Upload test files (optional)
- Complete hCaptcha
- Submit form
Make sure the backend is running on http://localhost:5000 for submissions to work.
frontend/
├── public/
│ ├── index.html # HTML template
│ ├── favicon.ico
│ └── ...
├── src/
│ ├── App.js # Main form component with state management
│ ├── App.css # Global styles
│ ├── index.js # React entry point
│ ├── index.css # Tailwind imports
│ ├── FormTypes.js # Form type definitions
│ ├── tableConfigs.js # Expense table configurations
│ └── components/
│ ├── Header.js # Form header with title and description
│ ├── ContactInfoSection.js # Name and email inputs
│ ├── ExpensesTable.js # Dynamic expense table
│ ├── FileUploadSection.js # File upload and display
│ └── CommentsSection.js # Optional comments field
├── .env # Environment variables (gitignored)
├── .env.example # Environment variable template
├── package.json # Dependencies and scripts
├── tailwind.config.js # Tailwind CSS configuration
└── postcss.config.js # PostCSS configuration
Defines the two form types and their metadata:
{
"Reimbursement Request": {
title: "Reimbursement Request",
blurb: "Description text...",
endpoint: "/submit"
},
"Purchase Approval": {
title: "Purchase Approval",
blurb: "Description text...",
endpoint: "/submit-PA"
}
}Defines expense table columns for each form type:
{
"Reimbursement Request": {
columns: [
{ key: 'approval', label: 'Approval/Project', type: 'text', required: true },
{ key: 'vendor', label: 'Vendor', type: 'text', required: true },
// ... more columns
],
initialExpense: { id: 1, approval: '', vendor: '', ... },
calculateAmount: (expense) => { /* HST calculation logic */ }
},
"Purchase Approval": {
columns: [ /* simplified columns */ ],
initialExpense: { id: 1, vendor: '', ... },
calculateAmount: (expense) => expense.amount
}
}The frontend validates all inputs before submission to provide immediate feedback:
- First Name / Last Name: Required, max 100 characters
- Email: Required, valid email format, max 255 characters
- Text fields (vendor, description, approval): Required, max 500 characters
- Amount: Required, positive number, max $1,000,000, cannot be zero
- HST (Reimbursement Request only): Required, select dropdown with 3 options
- Allowed types: .pdf, .png, .jpg, .jpeg, .gif, .bmp, .tiff, .xlsx, .xls, .csv, .doc, .docx, .txt
- Size limits:
- 10MB per file
- 50MB total across all files
- File type validation happens on selection
- Optional field
- Max 2000 characters
- Character counter displayed
- hCaptcha: Required, must be completed before submission
Users can switch between Reimbursement Request and Purchase Approval forms. When switching:
- Common fields are preserved (vendor, description, amount)
- Form-specific fields are added/removed
- HST calculation is enabled/disabled based on form type
For Reimbursement Requests, when "HST excluded from amount" is selected:
- Calculated Amount = Amount × 1.13
- Displayed in readonly field
- Used for total calculation
- Start with one empty row
- Add more rows with "Add Row" button
- Remove rows with trash icon (minimum 1 row required)
- Each row has unique ID for React key management
- Empty required fields highlighted in red
- Multiple file selection supported
- Files displayed with name and size
- Individual file removal with X icon
- Validation on selection (immediate feedback)
- Files sent as FormData multipart request
- Validation errors shown in red banner
- Success messages shown in green banner
- Messages auto-clear after 5 seconds
- Captcha resets on submission (success or failure)
# Create optimized production build
npm run build
# Build output will be in the 'build/' directoryThe build folder contains static files ready to deploy to:
- Netlify
- Vercel
- GitHub Pages
- Any static hosting service
When deploying, set these environment variables in your hosting platform:
# Your deployed backend URL
REACT_APP_API_URL=https://your-backend.run.app
# Your hCaptcha site key
REACT_APP_HCAPTCHA_SITEKEY=your_site_keyPlatform-specific instructions:
Netlify:
- Go to Site Settings → Build & Deploy → Environment
- Add variables there
Vercel:
- Go to Project Settings → Environment Variables
- Add variables there
The form is designed to be embedded in Squarespace via iframe:
- Deploy frontend to Netlify/Vercel
- In Squarespace, add a Code Block
- Insert iframe code:
<iframe
src="https://your-netlify-site.netlify.app"
width="100%"
height="1200px"
frameborder="0"
title="Reimbursement Form">
</iframe>Styling tips:
- The form has
max-w-4xl(896px max width) and centers itself - White background with padding
- Adjust iframe height based on typical form size
- Consider making height responsive with JavaScript if needed
Check browser console and Network tab:
// The request URL is logged on submission
console.log(url);
// Check fetch request in Network tab
// Should be POST to http://localhost:5000/submit or /submit-PA"Failed to fetch" or network error
- Backend not running on localhost:5000
- CORS not configured (add CORS to backend in production)
- Wrong REACT_APP_API_URL in .env
hCaptcha not loading
- Invalid REACT_APP_HCAPTCHA_SITEKEY
- Ad blocker blocking hCaptcha
- Network connectivity issues
Form not submitting
- Check browser console for validation errors
- Verify all required fields are filled
- Ensure hCaptcha is completed
- Check that backend is returning 200 status
Files not uploading
- File size too large (check limits)
- Invalid file type
- Backend validation rejecting files
Styles not loading
- Run
npm installto ensure Tailwind is installed - Check that
index.cssimports Tailwind directives - Restart development server after Tailwind config changes
- Proper
labelandinputassociations withhtmlForandid - ARIA labels on icon buttons
role="alert"andaria-live="polite"on messages- Semantic HTML elements
- Keyboard navigation support
- Focus management
- Chrome/Edge (last 2 versions)
- Firefox (last 2 versions)
- Safari (last 2 versions)
- Mobile browsers (iOS Safari, Chrome Android)
After local development is working:
- Test all form scenarios end-to-end with backend
- Verify validation messages are clear and helpful
- Test on mobile devices
- Test embedding in Squarespace
- Prepare for deployment (see deployment section - to be added)
# Start development server
npm start
# Run tests
npm test
# Build for production
npm run build
# Eject from Create React App (not recommended)
npm run ejectKey dependencies and their purposes:
- react - UI framework
- @hcaptcha/react-hcaptcha - Captcha integration
- lucide-react - Icon components
- tailwindcss - Utility-first CSS framework
See package.json for complete list.