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 && (
+
+
+
+ | ID |
+ Name |
+ Description |
+
+
+
+ {committees.map((committee) => (
+
+ | {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'
+ }
+ }
+});