mirror of
https://github.com/mblanke/Gov_Travel_App.git
synced 2026-03-01 14:10:22 -05:00
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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user