Skip to content

Commit 526aa22

Browse files
author
Marvin Zhang
committed
feat: added @devlog/web
1 parent 44a7339 commit 526aa22

24 files changed

+4371
-58
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
"install-all": "pnpm install",
1818
"build:mcp": "pnpm --filter @devlog/mcp-server build",
1919
"build:types": "pnpm --filter @devlog/types build",
20-
"build:core": "pnpm --filter @devlog/core build"
20+
"build:core": "pnpm --filter @devlog/core build",
21+
"build:web": "pnpm --filter @devlog/web build",
22+
"dev:web": "pnpm --filter @devlog/web dev",
23+
"start:web": "pnpm --filter @devlog/web start"
2124
},
2225
"keywords": [
2326
"monorepo",

packages/web/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# @devlog/web
2+
3+
Web interface for devlog management - A modern dashboard for tracking development progress.
4+
5+
## Features
6+
7+
- 📊 **Dashboard** - Overview of development progress with statistics
8+
- 📝 **Devlog Management** - Create, edit, and delete development logs
9+
- 🔄 **Real-time Updates** - WebSocket connection for live updates
10+
- 🎨 **Modern UI** - Built with React and Tailwind CSS
11+
- 📱 **Responsive Design** - Works on desktop and mobile devices
12+
13+
## Getting Started
14+
15+
### Development
16+
17+
```bash
18+
# Install dependencies
19+
pnpm install
20+
21+
# Start development server (both client and server)
22+
pnpm dev
23+
24+
# Start only the client (with proxy to API)
25+
pnpm dev:client
26+
27+
# Start only the server
28+
pnpm dev:server
29+
```
30+
31+
The client will be available at http://localhost:3000 and will proxy API requests to the server at http://localhost:3001.
32+
33+
### Production
34+
35+
```bash
36+
# Build the application
37+
pnpm build
38+
39+
# Start the production server
40+
pnpm start
41+
```
42+
43+
## Architecture
44+
45+
The web package consists of two main parts:
46+
47+
### Client (React App)
48+
- Built with React 18 and TypeScript
49+
- Styled with Tailwind CSS
50+
- Bundled with Vite for fast development
51+
52+
### Server (Express API)
53+
- RESTful API built with Express.js
54+
- WebSocket support for real-time updates
55+
- Integrates with `@devlog/core` for data management
56+
57+
## API Endpoints
58+
59+
- `GET /api/devlogs` - List all devlogs
60+
- `POST /api/devlogs` - Create a new devlog
61+
- `GET /api/devlogs/:id` - Get devlog by ID
62+
- `PUT /api/devlogs/:id` - Update devlog
63+
- `DELETE /api/devlogs/:id` - Delete devlog
64+
- `GET /api/devlogs/stats/overview` - Get overview statistics
65+
- `POST /api/devlogs/:id/notes` - Add note to devlog
66+
67+
## WebSocket Events
68+
69+
- `subscribe` - Subscribe to updates for a channel
70+
- `unsubscribe` - Unsubscribe from a channel
71+
- `update` - Broadcast when devlogs are updated
72+
73+
## Environment Variables
74+
75+
- `PORT` - Server port (default: 3001)
76+
- `NODE_ENV` - Environment (development/production)
77+
78+
## Development Guidelines
79+
80+
This package follows the project's dogfooding approach - use the devlog system to track development of the web interface itself!

packages/web/package.json

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"name": "@devlog/web",
3+
"version": "1.0.0",
4+
"description": "Web interface for devlog management",
5+
"main": "build/server/index.js",
6+
"type": "module",
7+
"scripts": {
8+
"dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
9+
"dev:server": "tsx watch src/server/index.ts",
10+
"dev:client": "vite",
11+
"build": "npm run build:server && npm run build:client",
12+
"build:server": "tsc -p tsconfig.server.json",
13+
"build:client": "vite build",
14+
"preview": "vite preview",
15+
"start": "node build/server/index.js",
16+
"clean": "rimraf build dist"
17+
},
18+
"dependencies": {
19+
"@devlog/core": "workspace:*",
20+
"@devlog/types": "workspace:*",
21+
"express": "^4.18.2",
22+
"cors": "^2.8.5",
23+
"ws": "^8.14.2",
24+
"helmet": "^7.1.0",
25+
"compression": "^1.7.4"
26+
},
27+
"devDependencies": {
28+
"@types/express": "^4.17.21",
29+
"@types/cors": "^2.8.17",
30+
"@types/ws": "^8.5.10",
31+
"@types/compression": "^1.7.5",
32+
"@vitejs/plugin-react": "^4.2.1",
33+
"concurrently": "^8.2.2",
34+
"tsx": "^4.7.0",
35+
"typescript": "^5.3.3",
36+
"vite": "^5.0.12",
37+
"react": "^18.2.0",
38+
"react-dom": "^18.2.0",
39+
"@types/react": "^18.2.48",
40+
"@types/react-dom": "^18.2.18",
41+
"tailwindcss": "^3.4.1",
42+
"autoprefixer": "^10.4.17",
43+
"postcss": "^8.4.33",
44+
"lucide-react": "^0.323.0",
45+
"recharts": "^2.10.3",
46+
"date-fns": "^3.2.0",
47+
"rimraf": "^5.0.5"
48+
},
49+
"keywords": [
50+
"devlog",
51+
"development",
52+
"tracking",
53+
"web",
54+
"dashboard"
55+
],
56+
"author": "",
57+
"license": "MIT"
58+
}

packages/web/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

packages/web/src/client/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Devlog Dashboard</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { DevlogEntry, DevlogStats } from '@devlog/types';
3+
import { Dashboard } from './components/Dashboard';
4+
import { DevlogList } from './components/DevlogList';
5+
import { DevlogForm } from './components/DevlogForm';
6+
import { DevlogDetails } from './components/DevlogDetails';
7+
import { Sidebar } from './components/Sidebar';
8+
import { Header } from './components/Header';
9+
import { useDevlogs } from './hooks/useDevlogs';
10+
import { useWebSocket } from './hooks/useWebSocket';
11+
12+
type View = 'dashboard' | 'list' | 'create' | 'details';
13+
14+
function App() {
15+
const [currentView, setCurrentView] = useState<View>('dashboard');
16+
const [selectedDevlog, setSelectedDevlog] = useState<DevlogEntry | null>(null);
17+
const [stats, setStats] = useState<DevlogStats | null>(null);
18+
19+
const { devlogs, loading, error, refetch, createDevlog, updateDevlog, deleteDevlog } = useDevlogs();
20+
const { connected } = useWebSocket();
21+
22+
// Fetch stats
23+
useEffect(() => {
24+
const fetchStats = async () => {
25+
try {
26+
const response = await fetch('/api/devlogs/stats/overview');
27+
if (response.ok) {
28+
const statsData = await response.json();
29+
setStats(statsData);
30+
}
31+
} catch (error) {
32+
console.error('Failed to fetch stats:', error);
33+
}
34+
};
35+
36+
fetchStats();
37+
}, [devlogs]);
38+
39+
const handleViewChange = (view: View, devlog?: DevlogEntry) => {
40+
setCurrentView(view);
41+
setSelectedDevlog(devlog || null);
42+
};
43+
44+
const handleDevlogCreate = async (data: any) => {
45+
try {
46+
await createDevlog(data);
47+
setCurrentView('list');
48+
} catch (error) {
49+
console.error('Failed to create devlog:', error);
50+
}
51+
};
52+
53+
const handleDevlogUpdate = async (data: any) => {
54+
try {
55+
await updateDevlog(data);
56+
if (selectedDevlog) {
57+
// Refresh the selected devlog
58+
const updated = devlogs.find(d => d.id === selectedDevlog.id);
59+
setSelectedDevlog(updated || null);
60+
}
61+
} catch (error) {
62+
console.error('Failed to update devlog:', error);
63+
}
64+
};
65+
66+
const handleDevlogDelete = async (id: string) => {
67+
try {
68+
await deleteDevlog(id);
69+
if (selectedDevlog?.id === id) {
70+
setCurrentView('list');
71+
setSelectedDevlog(null);
72+
}
73+
} catch (error) {
74+
console.error('Failed to delete devlog:', error);
75+
}
76+
};
77+
78+
const renderCurrentView = () => {
79+
switch (currentView) {
80+
case 'dashboard':
81+
return (
82+
<Dashboard
83+
stats={stats}
84+
recentDevlogs={devlogs.slice(0, 5)}
85+
onViewDevlog={(devlog) => handleViewChange('details', devlog)}
86+
/>
87+
);
88+
case 'list':
89+
return (
90+
<DevlogList
91+
devlogs={devlogs}
92+
loading={loading}
93+
onViewDevlog={(devlog) => handleViewChange('details', devlog)}
94+
onDeleteDevlog={handleDevlogDelete}
95+
/>
96+
);
97+
case 'create':
98+
return (
99+
<DevlogForm
100+
onSubmit={handleDevlogCreate}
101+
onCancel={() => setCurrentView('list')}
102+
/>
103+
);
104+
case 'details':
105+
return selectedDevlog ? (
106+
<DevlogDetails
107+
devlog={selectedDevlog}
108+
onUpdate={handleDevlogUpdate}
109+
onDelete={() => handleDevlogDelete(selectedDevlog.id)}
110+
onBack={() => setCurrentView('list')}
111+
/>
112+
) : (
113+
<div>Devlog not found</div>
114+
);
115+
default:
116+
return <div>Unknown view</div>;
117+
}
118+
};
119+
120+
return (
121+
<div className="flex h-screen bg-gray-50">
122+
<Sidebar
123+
currentView={currentView}
124+
onViewChange={handleViewChange}
125+
stats={stats}
126+
/>
127+
<div className="flex-1 flex flex-col">
128+
<Header
129+
connected={connected}
130+
onRefresh={refetch}
131+
/>
132+
<main className="flex-1 overflow-auto p-6">
133+
{error && (
134+
<div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-md">
135+
<p className="text-red-800">{error}</p>
136+
</div>
137+
)}
138+
{renderCurrentView()}
139+
</main>
140+
</div>
141+
</div>
142+
);
143+
}
144+
145+
export default App;

0 commit comments

Comments
 (0)