-- Database Schema (users.sql) CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, role user_role DEFAULT 'user', created_at TIMESTAMP DEFAULT NOW(), last_login TIMESTAMP ); CREATE TABLE user_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id INTEGER REFERENCES users(id), expires_at TIMESTAMP NOT NULL ); -- API Endpoint (users.js) app.get('/api/users', async (req, res) => { const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 10; const offset = (page - 1) * limit; const result = await db.query(` SELECT id, email, role, created_at, last_login FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2 `, [limit, offset]); res.json(result.rows); }); // Frontend (users.html) <div id="users"></div> <script> fetch('/api/users') .then(r => r.json()) .then(users => { document.getElementById('users').innerHTML = users .map(u => `<div>${u.email} - ${u.role}</div>`) .join(''); }); </script>
// userSlice.js import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; export const fetchUsers = createAsyncThunk( 'users/fetchUsers', async ({ page, limit }, { rejectWithValue }) => { try { const response = await fetch(`/api/users?page=${page}&limit=${limit}`); if (!response.ok) throw new Error('Failed to fetch'); return await response.json(); } catch (error) { return rejectWithValue(error.message); } } ); const userSlice = createSlice({ name: 'users', initialState: { items: [], loading: false, error: null, pagination: { page: 1, limit: 10, total: 0 } }, reducers: { setPage: (state, action) => { state.pagination.page = action.payload; }, clearError: (state) => { state.error = null; } }, extraReducers: (builder) => { builder .addCase(fetchUsers.pending, (state) => { state.loading = true; state.error = null; }) .addCase(fetchUsers.fulfilled, (state, action) => { state.loading = false; state.items = action.payload; }) .addCase(fetchUsers.rejected, (state, action) => { state.loading = false; state.error = action.payload; }); } }); // UserList.jsx import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { fetchUsers, setPage } from './userSlice'; const UserList = () => { const dispatch = useDispatch(); const { items, loading, error, pagination } = useSelector(state => state.users); useEffect(() => { dispatch(fetchUsers({ page: pagination.page, limit: pagination.limit })); }, [dispatch, pagination.page, pagination.limit]); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; return ( <div> {items.map(user => ( <UserCard key={user.id} user={user} /> ))} <Pagination current={pagination.page} onChange={(page) => dispatch(setPage(page))} /> </div> ); }; // UserCard.jsx const UserCard = React.memo(({ user }) => ( <div className="user-card"> <span>{user.email}</span> <span>{user.role}</span> </div> )); // Pagination.jsx const Pagination = ({ current, onChange }) => { return ( <div className="pagination"> {/* 50 more lines of pagination logic */} </div> ); };
-- Database Schema CREATE TABLE messages ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id), channel_id INTEGER REFERENCES channels(id), content TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); -- Trigger for real-time notifications CREATE OR REPLACE FUNCTION notify_new_message() RETURNS TRIGGER AS $$ BEGIN PERFORM pg_notify('new_message', json_build_object( 'id', NEW.id, 'user_id', NEW.user_id, 'channel_id', NEW.channel_id, 'content', NEW.content, 'created_at', NEW.created_at )::text ); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER message_notify AFTER INSERT ON messages FOR EACH ROW EXECUTE FUNCTION notify_new_message(); // Server (chat.js) const client = new pg.Client(); client.connect(); client.query('LISTEN new_message'); client.on('notification', (msg) => { const message = JSON.parse(msg.payload); io.to(`channel_${message.channel_id}`).emit('message', message); }); app.post('/api/messages', async (req, res) => { const { content, channel_id } = req.body; const user_id = req.user.id; await db.query( 'INSERT INTO messages (user_id, channel_id, content) VALUES ($1, $2, $3)', [user_id, channel_id, content] ); res.json({ success: true }); }); // Frontend (chat.html) <div id="messages"></div> <input id="messageInput" placeholder="Type a message..."> <script> const socket = io(); socket.on('message', (msg) => { const div = document.createElement('div'); div.textContent = `${msg.user_id}: ${msg.content}`; document.getElementById('messages').appendChild(div); }); document.getElementById('messageInput').addEventListener('keypress', (e) => { if (e.key === 'Enter') { fetch('/api/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: e.target.value, channel_id: 1 }) }); e.target.value = ''; } }); </script>
// chatSaga.js import { takeEvery, call, put, fork, take } from 'redux-saga/effects'; import { eventChannel } from 'redux-saga'; function createSocketChannel(socket) { return eventChannel(emit => { socket.on('message', (message) => emit({ type: 'SOCKET_MESSAGE', payload: message })); socket.on('disconnect', () => emit({ type: 'SOCKET_DISCONNECT' })); return () => socket.disconnect(); }); } function* watchSocketConnection() { const socket = io(); const socketChannel = yield call(createSocketChannel, socket); while (true) { try { const action = yield take(socketChannel); yield put(action); } catch (error) { yield put({ type: 'SOCKET_ERROR', payload: error.message }); } } } function* sendMessage(action) { try { yield call(fetch, '/api/messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(action.payload) }); } catch (error) { yield put({ type: 'SEND_MESSAGE_FAILED', payload: error.message }); } } function* chatSaga() { yield fork(watchSocketConnection); yield takeEvery('SEND_MESSAGE', sendMessage); } // chatSlice.js const chatSlice = createSlice({ name: 'chat', initialState: { messages: [], connected: false, typing: {}, error: null }, reducers: { socketConnected: (state) => { state.connected = true; }, socketDisconnected: (state) => { state.connected = false; }, messageReceived: (state, action) => { state.messages.push(action.payload); }, setTyping: (state, action) => { state.typing[action.payload.userId] = action.payload.isTyping; } } }); // MessageList.jsx const MessageList = () => { const messages = useSelector(state => state.chat.messages); const messagesEndRef = useRef(null); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); return ( <div className="messages-container"> {messages.map(message => ( <Message key={message.id} message={message} /> ))} <div ref={messagesEndRef} /> </div> ); }; // MessageInput.jsx const MessageInput = () => { const [message, setMessage] = useState(''); const dispatch = useDispatch(); const handleSubmit = (e) => { e.preventDefault(); if (message.trim()) { dispatch({ type: 'SEND_MESSAGE', payload: { content: message } }); setMessage(''); } }; return ( <form onSubmit={handleSubmit}> <input value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Type a message..." /> </form> ); };
-- Database Views for Analytics CREATE VIEW daily_user_stats AS SELECT DATE(created_at) as date, COUNT(*) as new_users, COUNT(DISTINCT CASE WHEN last_login >= DATE(created_at) THEN id END) as active_users FROM users GROUP BY DATE(created_at) ORDER BY date DESC; CREATE VIEW message_stats AS SELECT DATE(created_at) as date, COUNT(*) as total_messages, COUNT(DISTINCT user_id) as active_users, AVG(LENGTH(content)) as avg_message_length FROM messages GROUP BY DATE(created_at) ORDER BY date DESC; -- API Endpoint app.get('/api/analytics', async (req, res) => { const [userStats, messageStats] = await Promise.all([ db.query('SELECT * FROM daily_user_stats LIMIT 30'), db.query('SELECT * FROM message_stats LIMIT 30') ]); res.json({ users: userStats.rows, messages: messageStats.rows }); }); // Frontend (dashboard.html) <canvas id="userChart" width="400" height="200"></canvas> <canvas id="messageChart" width="400" height="200"></canvas> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script> fetch('/api/analytics') .then(r => r.json()) .then(data => { new Chart(document.getElementById('userChart'), { type: 'line', data: { labels: data.users.map(d => d.date), datasets: [{ label: 'New Users', data: data.users.map(d => d.new_users), borderColor: '#0f0' }] } }); new Chart(document.getElementById('messageChart'), { type: 'bar', data: { labels: data.messages.map(d => d.date), datasets: [{ label: 'Messages', data: data.messages.map(d => d.total_messages), backgroundColor: '#ff6b6b' }] } }); }); </script>
// analyticsSlice.js const analyticsSlice = createSlice({ name: 'analytics', initialState: { userStats: { data: [], loading: false, error: null }, messageStats: { data: [], loading: false, error: null }, dateRange: { start: null, end: null }, filters: { userType: 'all', messageType: 'all' } }, reducers: { setDateRange: (state, action) => { state.dateRange = action.payload; }, setFilters: (state, action) => { state.filters = { ...state.filters, ...action.payload }; }, // ... 20 more reducers } }); // ChartContainer.jsx const ChartContainer = ({ type, data, options }) => { const chartRef = useRef(null); const chartInstance = useRef(null); useEffect(() => { if (chartInstance.current) { chartInstance.current.destroy(); } const ctx = chartRef.current.getContext('2d'); chartInstance.current = new Chart(ctx, { type, data, options }); return () => { if (chartInstance.current) { chartInstance.current.destroy(); } }; }, [type, data, options]); return <canvas ref={chartRef} />; }; // AnalyticsDashboard.jsx const AnalyticsDashboard = () => { const dispatch = useDispatch(); const { userStats, messageStats, dateRange, filters } = useSelector(state => state.analytics); useEffect(() => { dispatch(fetchUserStats({ dateRange, filters })); dispatch(fetchMessageStats({ dateRange, filters })); }, [dispatch, dateRange, filters]); const userChartData = useMemo(() => ({ labels: userStats.data.map(d => d.date), datasets: [{ label: 'New Users', data: userStats.data.map(d => d.new_users), borderColor: '#0f0' }] }), [userStats.data]); const messageChartData = useMemo(() => ({ labels: messageStats.data.map(d => d.date), datasets: [{ label: 'Messages', data: messageStats.data.map(d => d.total_messages), backgroundColor: '#ff6b6b' }] }), [messageStats.data]); if (userStats.loading || messageStats.loading) { return <LoadingSpinner />; } return ( <div className="analytics-dashboard"> <DateRangePicker value={dateRange} onChange={(range) => dispatch(setDateRange(range))} /> <FilterPanel filters={filters} onFilterChange={(newFilters) => dispatch(setFilters(newFilters))} /> <div className="charts-grid"> <ChartContainer type="line" data={userChartData} options={userChartOptions} /> <ChartContainer type="bar" data={messageChartData} options={messageChartOptions} /> </div> </div> ); };