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:
65
server.js
65
server.js
@@ -80,13 +80,22 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
// Serve static files from the current directory
|
||||
app.use(
|
||||
express.static(__dirname, {
|
||||
maxAge: "1d",
|
||||
// Serve React app (production build) or legacy static files
|
||||
if (process.env.NODE_ENV === 'production' && require('fs').existsSync(path.join(__dirname, 'dist', 'client'))) {
|
||||
// Serve React production build
|
||||
app.use(express.static(path.join(__dirname, 'dist', 'client'), {
|
||||
maxAge: '1d',
|
||||
etag: true,
|
||||
})
|
||||
);
|
||||
}));
|
||||
} else {
|
||||
// Serve legacy static files from the current directory
|
||||
app.use(
|
||||
express.static(__dirname, {
|
||||
maxAge: "1d",
|
||||
etag: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Disable caching for HTML and JS files
|
||||
app.use((req, res, next) => {
|
||||
@@ -204,6 +213,50 @@ app.get(
|
||||
}
|
||||
})();
|
||||
|
||||
// ============ DATA PORTAL ENDPOINTS ============
|
||||
|
||||
/**
|
||||
* Get all per-diem rates
|
||||
* GET /api/rates/per-diem
|
||||
*/
|
||||
app.get("/api/rates/per-diem", async (req, res) => {
|
||||
try {
|
||||
const data = await dbService.getAllPerDiemRates();
|
||||
res.json({ success: true, data, count: data.length });
|
||||
} catch (error) {
|
||||
logger.error("Error fetching per-diem rates:", error);
|
||||
res.status(500).json({ error: "Failed to fetch per-diem rates" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all accommodation rates
|
||||
* GET /api/rates/accommodations
|
||||
*/
|
||||
app.get("/api/rates/accommodations", async (req, res) => {
|
||||
try {
|
||||
const data = await dbService.getAllAccommodations();
|
||||
res.json({ success: true, data, count: data.length });
|
||||
} catch (error) {
|
||||
logger.error("Error fetching accommodation rates:", error);
|
||||
res.status(500).json({ error: "Failed to fetch accommodation rates" });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get portal statistics
|
||||
* GET /api/stats
|
||||
*/
|
||||
app.get("/api/stats", async (req, res) => {
|
||||
try {
|
||||
const stats = await dbService.getStats();
|
||||
res.json({ success: true, stats });
|
||||
} catch (error) {
|
||||
logger.error("Error fetching stats:", error);
|
||||
res.status(500).json({ error: "Failed to fetch statistics" });
|
||||
}
|
||||
});
|
||||
|
||||
// ============ DATABASE SEARCH ENDPOINTS ============
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user