diff --git a/.gitignore b/.gitignore index 8274070..4bd3524 100644 --- a/.gitignore +++ b/.gitignore @@ -1,49 +1,4 @@ -# .gitignore - -# Node -node_modules/ +node_modules npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -package-lock.json -yarn.lock -.pnp/ -.pnp.js - -# Build Output -dist/ -build/ -out/ -.next/ -*.log - -# Environment Variables -.env -.env.local -.env.*.local - -# Python -__pycache__/ -*.pyc -*.pyo -*.pyd -env/ -venv/ -.venv/ - -# OS Files .DS_Store -Thumbs.db - -# IDE Files -.vscode/ -.idea/ - -# Cache & Temp -.cache/ -coverage/ -tmp/ - -# TypeScript -*.tsbuildinfo +frontend/dist diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..db13b1d --- /dev/null +++ b/backend/package.json @@ -0,0 +1,17 @@ +{ + "name": "backend", + "version": "1.0.0", + "description": "DelegateOS backend server", + "main": "src/index.js", + "scripts": { + "start": "node src/index.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "type": "commonjs", + "dependencies": { + "cors": "^2.8.5", + "express": "^4.18.2" + } +} diff --git a/backend/src/api/committees.js b/backend/src/api/committees.js new file mode 100644 index 0000000..9076823 --- /dev/null +++ b/backend/src/api/committees.js @@ -0,0 +1,8 @@ +const express = require('express'); +const { getCommittees } = require('../controllers/committeeController'); + +const router = express.Router(); + +router.get('/committees', getCommittees); + +module.exports = router; diff --git a/backend/src/controllers/committeeController.js b/backend/src/controllers/committeeController.js new file mode 100644 index 0000000..01617b7 --- /dev/null +++ b/backend/src/controllers/committeeController.js @@ -0,0 +1,5 @@ +const committees = require('../models/committee'); + +exports.getCommittees = (req, res) => { + res.json(committees); +}; diff --git a/backend/src/index.js b/backend/src/index.js new file mode 100644 index 0000000..70c322d --- /dev/null +++ b/backend/src/index.js @@ -0,0 +1,19 @@ +const express = require('express'); +const cors = require('cors'); +const committeesRouter = require('./api/committees'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +app.use(cors()); +app.use(express.json()); + +app.use(committeesRouter); + +app.get('/', (req, res) => { + res.send('DelegateOS backend running'); +}); + +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); +}); diff --git a/backend/src/models/committee.js b/backend/src/models/committee.js new file mode 100644 index 0000000..94249b9 --- /dev/null +++ b/backend/src/models/committee.js @@ -0,0 +1,4 @@ +module.exports = [ + { id: 1, name: "UNGA", description: "General Assembly" }, + { id: 2, name: "UNSC", description: "Security Council" } +]; diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..a4f34fa --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + DelegateOS + + +
+ + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..7b12ae4 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,20 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.2" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.4", + "vite": "^5.4.10" + } +} diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..c979769 --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,52 @@ +.app { + max-width: 960px; + margin: 0 auto; + padding: 24px; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 0; + border-bottom: 1px solid #e5e7eb; + margin-bottom: 24px; +} + +.nav { + display: flex; + gap: 16px; +} + +.nav-link { + font-weight: 600; + padding: 8px 12px; + border-radius: 6px; +} + +.nav-link:hover { + background-color: #e5e7eb; +} + +.content h1 { + margin-top: 0; +} + +.table { + width: 100%; + border-collapse: collapse; + background-color: white; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); +} + +.table th, +.table td { + padding: 12px; + border: 1px solid #e5e7eb; + text-align: left; +} + +.error { + color: #b91c1c; + font-weight: 600; +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..d77866b --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,33 @@ +import { Routes, Route, Link } from 'react-router-dom'; +import Committees from './pages/Committees'; +import './App.css'; + +function Home() { + return ( +
+

Welcome to DelegateOS

+

Select a page from the navigation.

+
+ ); +} + +function App() { + return ( +
+
+ +
+
+ + } /> + } /> + +
+
+ ); +} + +export default App; diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..b8471ba --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,18 @@ +:root { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + color: #1f2937; + background-color: #f9fafb; +} + +body { + margin: 0; +} + +#root { + min-height: 100vh; +} + +a { + color: inherit; + text-decoration: none; +} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx new file mode 100644 index 0000000..6cede93 --- /dev/null +++ b/frontend/src/main.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import App from './App'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')).render( + + + + + +); diff --git a/frontend/src/pages/Committees.jsx b/frontend/src/pages/Committees.jsx new file mode 100644 index 0000000..13d2caf --- /dev/null +++ b/frontend/src/pages/Committees.jsx @@ -0,0 +1,60 @@ +import { useEffect, useState } from 'react'; + +const API_BASE = import.meta.env.VITE_API_BASE || ''; + +function Committees() { + const [committees, setCommittees] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + + useEffect(() => { + const fetchCommittees = async () => { + try { + setLoading(true); + setError(''); + const response = await fetch(`${API_BASE}/committees`); + if (!response.ok) { + throw new Error('Failed to fetch committees'); + } + const data = await response.json(); + setCommittees(data); + } catch (err) { + setError(err.message || 'An error occurred'); + } finally { + setLoading(false); + } + }; + + fetchCommittees(); + }, []); + + return ( +
+

Committees

+ {loading &&

Loading…

} + {error &&

{error}

} + {!loading && !error && ( + + + + + + + + + + {committees.map((committee) => ( + + + + + + ))} + +
IDNameDescription
{committee.id}{committee.name}{committee.description}
+ )} +
+ ); +} + +export default Committees; diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000..4176912 --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { + proxy: { + '/committees': 'http://localhost:3000' + } + } +});