Build premium data portal with React + Tailwind CSS

Frontend Features:
- Landing page with glassmorphism, animated counters, hero section
- Interactive data tables with search, sort, filter, CSV export
- Premium dark theme (navy + gold accents)
- Framer Motion animations and micro-interactions
- Responsive design with Inter + Playfair Display typography
- DataTable component with pagination and live search

Backend Updates:
- New API endpoints: /api/rates/per-diem, /api/rates/accommodations, /api/stats
- Database service methods for bulk data retrieval
- Production mode serves built React app from /dist/client
- Fallback to legacy HTML for development

Tech Stack:
- React 18 + TypeScript
- Vite 7 build tool
- Tailwind CSS 4 with @tailwindcss/postcss
- Framer Motion for animations
- Lucide React icons
- SQLite3 backend

Build Output:
- 351KB optimized JavaScript bundle
- 29KB CSS bundle
- Fully tree-shaken and minified
This commit is contained in:
2026-01-13 11:05:54 -05:00
parent 66b72d5f74
commit 4d915aa3ea
18 changed files with 1211 additions and 7 deletions

View File

@@ -266,6 +266,69 @@ class DatabaseService {
});
}
/**
* Get all per-diem rates
*/
async getAllPerDiemRates() {
const query = `
SELECT country, city_name as city, breakfast, lunch, dinner,
incidentals, currency
FROM travel_rates
WHERE country IS NOT NULL
AND country != 'Canada'
ORDER BY country, city_name
`;
return new Promise((resolve, reject) => {
this.db.all(query, [], (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
/**
* Get all accommodation rates
*/
async getAllAccommodations() {
const query = `
SELECT city_name as city, province, accommodation_rate as rate, currency
FROM travel_rates
WHERE country = 'Canada'
AND accommodation_rate IS NOT NULL
ORDER BY province, city_name
`;
return new Promise((resolve, reject) => {
this.db.all(query, [], (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
/**
* Get statistics
*/
async getStats() {
const queries = {
countries: `SELECT COUNT(DISTINCT country) as count FROM travel_rates WHERE country != 'Canada'`,
accommodations: `SELECT COUNT(*) as count FROM travel_rates WHERE country = 'Canada' AND accommodation_rate IS NOT NULL`,
perDiem: `SELECT COUNT(*) as count FROM travel_rates WHERE country != 'Canada'`,
};
const results = {};
for (const [key, query] of Object.entries(queries)) {
results[key] = await new Promise((resolve, reject) => {
this.db.get(query, [], (err, row) => {
if (err) reject(err);
else resolve(row.count);
});
});
}
return results;
}
close() {
if (this.db) {
this.db.close();