baraqex 1.0.71 → 1.0.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -1
- package/dist/index.js.map +3 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +5 -1
- package/dist/server/index.js.map +3 -3
- package/package.json +2 -1
- package/templates/fullstack-template/package-lock.json +338 -5
- package/templates/fullstack-template/package.json +3 -2
- package/templates/fullstack-template/server.js +15 -1
- package/templates/fullstack-template/src/server.ts +5 -5
- package/templates/ssr-template/src/App.tsx +29 -20
- package/templates/fullstack-template/src/App.tsx +0 -271
- package/templates/fullstack-template/src/api/auth/login.ts +0 -75
- package/templates/fullstack-template/src/api/auth/me.ts +0 -66
- package/templates/fullstack-template/src/api/auth/register.ts +0 -59
- package/templates/fullstack-template/src/api/hello.js +0 -19
- package/templates/fullstack-template/src/api/todos/[id].ts +0 -108
- package/templates/fullstack-template/src/api/todos/index.ts +0 -79
- package/templates/fullstack-template/src/api/users.js +0 -39
- package/templates/fullstack-template/src/components/Dashboard.tsx +0 -129
- package/templates/fullstack-template/src/components/Header.tsx +0 -76
- package/templates/fullstack-template/src/contexts/ApiContext.tsx +0 -69
- package/templates/fullstack-template/src/contexts/AuthContext.tsx +0 -155
- package/templates/fullstack-template/src/pages/index.tsx +0 -200
- package/templates/fullstack-template/src/styles/App.css +0 -446
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// In-memory user storage for demo
|
|
2
|
-
let users = [
|
|
3
|
-
{ id: 1, name: 'John Doe', email: 'john@example.com' },
|
|
4
|
-
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
|
|
5
|
-
];
|
|
6
|
-
|
|
7
|
-
// GET /api/users
|
|
8
|
-
export async function get(req, res) {
|
|
9
|
-
res.json({
|
|
10
|
-
success: true,
|
|
11
|
-
users,
|
|
12
|
-
count: users.length
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// POST /api/users
|
|
17
|
-
export async function post(req, res) {
|
|
18
|
-
const { name, email } = req.body;
|
|
19
|
-
|
|
20
|
-
if (!name || !email) {
|
|
21
|
-
return res.status(400).json({
|
|
22
|
-
success: false,
|
|
23
|
-
error: 'Name and email are required'
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const newUser = {
|
|
28
|
-
id: users.length + 1,
|
|
29
|
-
name,
|
|
30
|
-
email
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
users.push(newUser);
|
|
34
|
-
|
|
35
|
-
res.status(201).json({
|
|
36
|
-
success: true,
|
|
37
|
-
user: newUser
|
|
38
|
-
});
|
|
39
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { jsx, useState, useEffect } from 'frontend-hamroun';
|
|
2
|
-
import { useAuth } from '../contexts/AuthContext';
|
|
3
|
-
import { useApi } from '../contexts/ApiContext';
|
|
4
|
-
|
|
5
|
-
export function Dashboard() {
|
|
6
|
-
const { user } = useAuth();
|
|
7
|
-
const api = useApi();
|
|
8
|
-
const [stats, setStats] = useState({
|
|
9
|
-
totalTodos: 0,
|
|
10
|
-
completedTodos: 0,
|
|
11
|
-
pendingTodos: 0
|
|
12
|
-
});
|
|
13
|
-
const [loading, setLoading] = useState(true);
|
|
14
|
-
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
loadDashboardData();
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
|
-
const loadDashboardData = async () => {
|
|
20
|
-
try {
|
|
21
|
-
const response = await api.get('/api/todos');
|
|
22
|
-
const todos = response.data.todos;
|
|
23
|
-
|
|
24
|
-
setStats({
|
|
25
|
-
totalTodos: todos.length,
|
|
26
|
-
completedTodos: todos.filter((todo: any) => todo.completed).length,
|
|
27
|
-
pendingTodos: todos.filter((todo: any) => !todo.completed).length
|
|
28
|
-
});
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error('Failed to load dashboard data:', error);
|
|
31
|
-
} finally {
|
|
32
|
-
setLoading(false);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
if (loading) {
|
|
37
|
-
return (
|
|
38
|
-
<div className="dashboard">
|
|
39
|
-
<div className="dashboard-loading">
|
|
40
|
-
<div className="spinner"></div>
|
|
41
|
-
<p>Loading dashboard...</p>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div className="dashboard">
|
|
49
|
-
<div className="dashboard-header">
|
|
50
|
-
<h2>Welcome back, {user?.name || user?.username}! 👋</h2>
|
|
51
|
-
<p className="dashboard-subtitle">Here's your productivity overview</p>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div className="stats-grid">
|
|
55
|
-
<div className="stat-card">
|
|
56
|
-
<div className="stat-icon">📝</div>
|
|
57
|
-
<div className="stat-content">
|
|
58
|
-
<h3 className="stat-number">{stats.totalTodos}</h3>
|
|
59
|
-
<p className="stat-label">Total Todos</p>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<div className="stat-card">
|
|
64
|
-
<div className="stat-icon">✅</div>
|
|
65
|
-
<div className="stat-content">
|
|
66
|
-
<h3 className="stat-number">{stats.completedTodos}</h3>
|
|
67
|
-
<p className="stat-label">Completed</p>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<div className="stat-card">
|
|
72
|
-
<div className="stat-icon">⏳</div>
|
|
73
|
-
<div className="stat-content">
|
|
74
|
-
<h3 className="stat-number">{stats.pendingTodos}</h3>
|
|
75
|
-
<p className="stat-label">Pending</p>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
|
|
79
|
-
<div className="stat-card">
|
|
80
|
-
<div className="stat-icon">📈</div>
|
|
81
|
-
<div className="stat-content">
|
|
82
|
-
<h3 className="stat-number">
|
|
83
|
-
{stats.totalTodos > 0 ? Math.round((stats.completedTodos / stats.totalTodos) * 100) : 0}%
|
|
84
|
-
</h3>
|
|
85
|
-
<p className="stat-label">Completion Rate</p>
|
|
86
|
-
</div>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
<div className="dashboard-actions">
|
|
91
|
-
<div className="action-card">
|
|
92
|
-
<h3>🚀 Quick Actions</h3>
|
|
93
|
-
<div className="action-buttons">
|
|
94
|
-
<button className="action-btn primary">
|
|
95
|
-
➕ Add New Todo
|
|
96
|
-
</button>
|
|
97
|
-
<button className="action-btn secondary">
|
|
98
|
-
📊 View Reports
|
|
99
|
-
</button>
|
|
100
|
-
<button className="action-btn secondary">
|
|
101
|
-
⚙️ Settings
|
|
102
|
-
</button>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
<div className="recent-activity">
|
|
107
|
-
<h3>📅 Recent Activity</h3>
|
|
108
|
-
<div className="activity-list">
|
|
109
|
-
<div className="activity-item">
|
|
110
|
-
<span className="activity-icon">✅</span>
|
|
111
|
-
<span className="activity-text">Completed todo: "Learn Baraqex"</span>
|
|
112
|
-
<span className="activity-time">2 hours ago</span>
|
|
113
|
-
</div>
|
|
114
|
-
<div className="activity-item">
|
|
115
|
-
<span className="activity-icon">➕</span>
|
|
116
|
-
<span className="activity-text">Added new todo</span>
|
|
117
|
-
<span className="activity-time">1 day ago</span>
|
|
118
|
-
</div>
|
|
119
|
-
<div className="activity-item">
|
|
120
|
-
<span className="activity-icon">👤</span>
|
|
121
|
-
<span className="activity-text">Updated profile</span>
|
|
122
|
-
<span className="activity-time">3 days ago</span>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { jsx } from 'frontend-hamroun';
|
|
2
|
-
import { useAuth } from '../contexts/AuthContext';
|
|
3
|
-
|
|
4
|
-
interface User {
|
|
5
|
-
id: number;
|
|
6
|
-
username: string;
|
|
7
|
-
name: string;
|
|
8
|
-
roles: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface HeaderProps {
|
|
12
|
-
user: User | null;
|
|
13
|
-
onNavigate: (view: string) => void;
|
|
14
|
-
currentView: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function Header({ user, onNavigate, currentView }: HeaderProps) {
|
|
18
|
-
const { logout } = useAuth();
|
|
19
|
-
|
|
20
|
-
const handleLogout = () => {
|
|
21
|
-
logout();
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<header className="app-header">
|
|
26
|
-
<div className="header-content">
|
|
27
|
-
<div className="header-left">
|
|
28
|
-
<h1 className="app-title">
|
|
29
|
-
<span className="logo">⚡</span>
|
|
30
|
-
Fullstack App
|
|
31
|
-
</h1>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
{user && (
|
|
35
|
-
<nav className="header-nav">
|
|
36
|
-
<button
|
|
37
|
-
className={`nav-button ${currentView === 'dashboard' ? 'active' : ''}`}
|
|
38
|
-
onClick={() => onNavigate('dashboard')}
|
|
39
|
-
>
|
|
40
|
-
📊 Dashboard
|
|
41
|
-
</button>
|
|
42
|
-
<button
|
|
43
|
-
className={`nav-button ${currentView === 'todos' ? 'active' : ''}`}
|
|
44
|
-
onClick={() => onNavigate('todos')}
|
|
45
|
-
>
|
|
46
|
-
✅ Todos
|
|
47
|
-
</button>
|
|
48
|
-
<button
|
|
49
|
-
className={`nav-button ${currentView === 'profile' ? 'active' : ''}`}
|
|
50
|
-
onClick={() => onNavigate('profile')}
|
|
51
|
-
>
|
|
52
|
-
👤 Profile
|
|
53
|
-
</button>
|
|
54
|
-
</nav>
|
|
55
|
-
)}
|
|
56
|
-
|
|
57
|
-
<div className="header-right">
|
|
58
|
-
{user ? (
|
|
59
|
-
<div className="user-menu">
|
|
60
|
-
<span className="user-greeting">
|
|
61
|
-
Hello, {user.name || user.username}
|
|
62
|
-
</span>
|
|
63
|
-
<button className="logout-button" onClick={handleLogout}>
|
|
64
|
-
🚪 Logout
|
|
65
|
-
</button>
|
|
66
|
-
</div>
|
|
67
|
-
) : (
|
|
68
|
-
<div className="auth-status">
|
|
69
|
-
Please log in to continue
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</header>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { jsx, createContext, useContext } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
interface ApiContextType {
|
|
4
|
-
request: (url: string, options?: RequestInit) => Promise<any>;
|
|
5
|
-
get: (url: string) => Promise<any>;
|
|
6
|
-
post: (url: string, data?: any) => Promise<any>;
|
|
7
|
-
put: (url: string, data?: any) => Promise<any>;
|
|
8
|
-
delete: (url: string) => Promise<any>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const ApiContext = createContext<ApiContextType | undefined>(undefined);
|
|
12
|
-
|
|
13
|
-
export function ApiProvider({ children }: { children: any }) {
|
|
14
|
-
const request = async (url: string, options: RequestInit = {}) => {
|
|
15
|
-
const token = localStorage.getItem('auth_token');
|
|
16
|
-
|
|
17
|
-
const config: RequestInit = {
|
|
18
|
-
...options,
|
|
19
|
-
headers: {
|
|
20
|
-
'Content-Type': 'application/json',
|
|
21
|
-
...options.headers,
|
|
22
|
-
...(token && { 'Authorization': `Bearer ${token}` })
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const response = await fetch(url, config);
|
|
27
|
-
const data = await response.json();
|
|
28
|
-
|
|
29
|
-
if (!response.ok) {
|
|
30
|
-
throw new Error(data.message || `HTTP error! status: ${response.status}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return data;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const get = (url: string) => request(url, { method: 'GET' });
|
|
37
|
-
|
|
38
|
-
const post = (url: string, data?: any) => request(url, {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
body: data ? JSON.stringify(data) : undefined
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const put = (url: string, data?: any) => request(url, {
|
|
44
|
-
method: 'PUT',
|
|
45
|
-
body: data ? JSON.stringify(data) : undefined
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const del = (url: string) => request(url, { method: 'DELETE' });
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<ApiContext.Provider value={{
|
|
52
|
-
request,
|
|
53
|
-
get,
|
|
54
|
-
post,
|
|
55
|
-
put,
|
|
56
|
-
delete: del
|
|
57
|
-
}}>
|
|
58
|
-
{children}
|
|
59
|
-
</ApiContext.Provider>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function useApi() {
|
|
64
|
-
const context = useContext(ApiContext);
|
|
65
|
-
if (context === undefined) {
|
|
66
|
-
throw new Error('useApi must be used within an ApiProvider');
|
|
67
|
-
}
|
|
68
|
-
return context;
|
|
69
|
-
}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { jsx, createContext, useContext, useState, useEffect } from 'frontend-hamroun';
|
|
2
|
-
|
|
3
|
-
interface User {
|
|
4
|
-
id: number;
|
|
5
|
-
username: string;
|
|
6
|
-
email: string;
|
|
7
|
-
name: string;
|
|
8
|
-
roles: string[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface AuthContextType {
|
|
12
|
-
user: User | null;
|
|
13
|
-
login: (credentials: LoginCredentials) => Promise<void>;
|
|
14
|
-
register: (userData: RegisterData) => Promise<void>;
|
|
15
|
-
logout: () => void;
|
|
16
|
-
loading: boolean;
|
|
17
|
-
error: string | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface LoginCredentials {
|
|
21
|
-
username: string;
|
|
22
|
-
password: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface RegisterData {
|
|
26
|
-
username: string;
|
|
27
|
-
password: string;
|
|
28
|
-
email: string;
|
|
29
|
-
name: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
33
|
-
|
|
34
|
-
export function AuthProvider({ children }: { children: any }) {
|
|
35
|
-
const [user, setUser] = useState<User | null>(null);
|
|
36
|
-
const [loading, setLoading] = useState(true);
|
|
37
|
-
const [error, setError] = useState<string | null>(null);
|
|
38
|
-
|
|
39
|
-
// Check for existing token on mount
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
const token = localStorage.getItem('auth_token');
|
|
42
|
-
if (token) {
|
|
43
|
-
validateToken(token);
|
|
44
|
-
} else {
|
|
45
|
-
setLoading(false);
|
|
46
|
-
}
|
|
47
|
-
}, []);
|
|
48
|
-
|
|
49
|
-
const validateToken = async (token: string) => {
|
|
50
|
-
try {
|
|
51
|
-
const response = await fetch('/api/auth/me', {
|
|
52
|
-
headers: {
|
|
53
|
-
'Authorization': `Bearer ${token}`
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
if (response.ok) {
|
|
58
|
-
const data = await response.json();
|
|
59
|
-
setUser(data.data.user);
|
|
60
|
-
} else {
|
|
61
|
-
localStorage.removeItem('auth_token');
|
|
62
|
-
}
|
|
63
|
-
} catch (error) {
|
|
64
|
-
console.error('Token validation failed:', error);
|
|
65
|
-
localStorage.removeItem('auth_token');
|
|
66
|
-
} finally {
|
|
67
|
-
setLoading(false);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const login = async (credentials: LoginCredentials) => {
|
|
72
|
-
try {
|
|
73
|
-
setLoading(true);
|
|
74
|
-
setError(null);
|
|
75
|
-
|
|
76
|
-
const response = await fetch('/api/auth/login', {
|
|
77
|
-
method: 'POST',
|
|
78
|
-
headers: {
|
|
79
|
-
'Content-Type': 'application/json'
|
|
80
|
-
},
|
|
81
|
-
body: JSON.stringify(credentials)
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const data = await response.json();
|
|
85
|
-
|
|
86
|
-
if (response.ok) {
|
|
87
|
-
localStorage.setItem('auth_token', data.data.token);
|
|
88
|
-
setUser(data.data.user);
|
|
89
|
-
} else {
|
|
90
|
-
throw new Error(data.message || 'Login failed');
|
|
91
|
-
}
|
|
92
|
-
} catch (error: any) {
|
|
93
|
-
setError(error.message);
|
|
94
|
-
throw error;
|
|
95
|
-
} finally {
|
|
96
|
-
setLoading(false);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const register = async (userData: RegisterData) => {
|
|
101
|
-
try {
|
|
102
|
-
setLoading(true);
|
|
103
|
-
setError(null);
|
|
104
|
-
|
|
105
|
-
const response = await fetch('/api/auth/register', {
|
|
106
|
-
method: 'POST',
|
|
107
|
-
headers: {
|
|
108
|
-
'Content-Type': 'application/json'
|
|
109
|
-
},
|
|
110
|
-
body: JSON.stringify(userData)
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
const data = await response.json();
|
|
114
|
-
|
|
115
|
-
if (response.ok) {
|
|
116
|
-
localStorage.setItem('auth_token', data.data.token);
|
|
117
|
-
setUser(data.data.user);
|
|
118
|
-
} else {
|
|
119
|
-
throw new Error(data.message || 'Registration failed');
|
|
120
|
-
}
|
|
121
|
-
} catch (error: any) {
|
|
122
|
-
setError(error.message);
|
|
123
|
-
throw error;
|
|
124
|
-
} finally {
|
|
125
|
-
setLoading(false);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const logout = () => {
|
|
130
|
-
localStorage.removeItem('auth_token');
|
|
131
|
-
setUser(null);
|
|
132
|
-
setError(null);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<AuthContext.Provider value={{
|
|
137
|
-
user,
|
|
138
|
-
login,
|
|
139
|
-
register,
|
|
140
|
-
logout,
|
|
141
|
-
loading,
|
|
142
|
-
error
|
|
143
|
-
}}>
|
|
144
|
-
{children}
|
|
145
|
-
</AuthContext.Provider>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function useAuth() {
|
|
150
|
-
const context = useContext(AuthContext);
|
|
151
|
-
if (context === undefined) {
|
|
152
|
-
throw new Error('useAuth must be used within an AuthProvider');
|
|
153
|
-
}
|
|
154
|
-
return context;
|
|
155
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'baraqex';
|
|
2
|
-
|
|
3
|
-
interface User {
|
|
4
|
-
id: number;
|
|
5
|
-
name: string;
|
|
6
|
-
email: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export default function HomePage() {
|
|
10
|
-
const [users, setUsers] = useState<User[]>([]);
|
|
11
|
-
const [newUser, setNewUser] = useState({ name: '', email: '' });
|
|
12
|
-
const [loading, setLoading] = useState(false);
|
|
13
|
-
|
|
14
|
-
// Fetch users on component mount
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
fetchUsers();
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
|
-
const fetchUsers = async () => {
|
|
20
|
-
try {
|
|
21
|
-
const response = await fetch('/api/users');
|
|
22
|
-
const data = await response.json();
|
|
23
|
-
if (data.success) {
|
|
24
|
-
setUsers(data.users);
|
|
25
|
-
}
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.error('Error fetching users:', error);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
32
|
-
e.preventDefault();
|
|
33
|
-
setLoading(true);
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const response = await fetch('/api/users', {
|
|
37
|
-
method: 'POST',
|
|
38
|
-
headers: {
|
|
39
|
-
'Content-Type': 'application/json',
|
|
40
|
-
},
|
|
41
|
-
body: JSON.stringify(newUser),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const data = await response.json();
|
|
45
|
-
|
|
46
|
-
if (data.success) {
|
|
47
|
-
setUsers([...users, data.user]);
|
|
48
|
-
setNewUser({ name: '', email: '' });
|
|
49
|
-
} else {
|
|
50
|
-
alert('Error: ' + data.error);
|
|
51
|
-
}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.error('Error creating user:', error);
|
|
54
|
-
alert('Error creating user');
|
|
55
|
-
} finally {
|
|
56
|
-
setLoading(false);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<div style={{
|
|
62
|
-
fontFamily: 'system-ui, sans-serif',
|
|
63
|
-
maxWidth: '800px',
|
|
64
|
-
margin: '0 auto',
|
|
65
|
-
padding: '20px'
|
|
66
|
-
}}>
|
|
67
|
-
<h1 style={{ color: '#333', marginBottom: '30px' }}>
|
|
68
|
-
🚀 Baraqex Fullstack Demo
|
|
69
|
-
</h1>
|
|
70
|
-
|
|
71
|
-
<p style={{ color: '#666', marginBottom: '40px' }}>
|
|
72
|
-
This page demonstrates server-side rendering with API integration.
|
|
73
|
-
The page is rendered on the server and then hydrated on the client.
|
|
74
|
-
</p>
|
|
75
|
-
|
|
76
|
-
{/* Add User Form */}
|
|
77
|
-
<div style={{
|
|
78
|
-
background: '#f5f5f5',
|
|
79
|
-
padding: '20px',
|
|
80
|
-
borderRadius: '8px',
|
|
81
|
-
marginBottom: '30px'
|
|
82
|
-
}}>
|
|
83
|
-
<h2 style={{ marginTop: '0', color: '#333' }}>Add New User</h2>
|
|
84
|
-
<form onSubmit={handleSubmit}>
|
|
85
|
-
<div style={{ marginBottom: '15px' }}>
|
|
86
|
-
<input
|
|
87
|
-
type="text"
|
|
88
|
-
placeholder="Name"
|
|
89
|
-
value={newUser.name}
|
|
90
|
-
onChange={(e) => setNewUser({ ...newUser, name: e.target.value })}
|
|
91
|
-
style={{
|
|
92
|
-
width: '100%',
|
|
93
|
-
padding: '10px',
|
|
94
|
-
border: '1px solid #ddd',
|
|
95
|
-
borderRadius: '4px',
|
|
96
|
-
fontSize: '14px'
|
|
97
|
-
}}
|
|
98
|
-
required
|
|
99
|
-
/>
|
|
100
|
-
</div>
|
|
101
|
-
<div style={{ marginBottom: '15px' }}>
|
|
102
|
-
<input
|
|
103
|
-
type="email"
|
|
104
|
-
placeholder="Email"
|
|
105
|
-
value={newUser.email}
|
|
106
|
-
onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
|
|
107
|
-
style={{
|
|
108
|
-
width: '100%',
|
|
109
|
-
padding: '10px',
|
|
110
|
-
border: '1px solid #ddd',
|
|
111
|
-
borderRadius: '4px',
|
|
112
|
-
fontSize: '14px'
|
|
113
|
-
}}
|
|
114
|
-
required
|
|
115
|
-
/>
|
|
116
|
-
</div>
|
|
117
|
-
<button
|
|
118
|
-
type="submit"
|
|
119
|
-
disabled={loading}
|
|
120
|
-
style={{
|
|
121
|
-
background: loading ? '#ccc' : '#007bff',
|
|
122
|
-
color: 'white',
|
|
123
|
-
border: 'none',
|
|
124
|
-
padding: '10px 20px',
|
|
125
|
-
borderRadius: '4px',
|
|
126
|
-
cursor: loading ? 'not-allowed' : 'pointer',
|
|
127
|
-
fontSize: '14px'
|
|
128
|
-
}}
|
|
129
|
-
>
|
|
130
|
-
{loading ? 'Adding...' : 'Add User'}
|
|
131
|
-
</button>
|
|
132
|
-
</form>
|
|
133
|
-
</div>
|
|
134
|
-
|
|
135
|
-
{/* Users List */}
|
|
136
|
-
<div>
|
|
137
|
-
<h2 style={{ color: '#333' }}>Users ({users.length})</h2>
|
|
138
|
-
{users.length === 0 ? (
|
|
139
|
-
<p style={{ color: '#666', fontStyle: 'italic' }}>
|
|
140
|
-
No users found. Add some users using the form above!
|
|
141
|
-
</p>
|
|
142
|
-
) : (
|
|
143
|
-
<div style={{ display: 'grid', gap: '10px' }}>
|
|
144
|
-
{users.map((user) => (
|
|
145
|
-
<div
|
|
146
|
-
key={user.id}
|
|
147
|
-
style={{
|
|
148
|
-
background: 'white',
|
|
149
|
-
border: '1px solid #ddd',
|
|
150
|
-
borderRadius: '6px',
|
|
151
|
-
padding: '15px',
|
|
152
|
-
display: 'flex',
|
|
153
|
-
justifyContent: 'space-between',
|
|
154
|
-
alignItems: 'center'
|
|
155
|
-
}}
|
|
156
|
-
>
|
|
157
|
-
<div>
|
|
158
|
-
<strong style={{ color: '#333' }}>{user.name}</strong>
|
|
159
|
-
<br />
|
|
160
|
-
<span style={{ color: '#666', fontSize: '14px' }}>{user.email}</span>
|
|
161
|
-
</div>
|
|
162
|
-
<div style={{
|
|
163
|
-
background: '#28a745',
|
|
164
|
-
color: 'white',
|
|
165
|
-
padding: '4px 8px',
|
|
166
|
-
borderRadius: '4px',
|
|
167
|
-
fontSize: '12px'
|
|
168
|
-
}}>
|
|
169
|
-
ID: {user.id}
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
172
|
-
))}
|
|
173
|
-
</div>
|
|
174
|
-
)}
|
|
175
|
-
</div>
|
|
176
|
-
|
|
177
|
-
{/* API Info */}
|
|
178
|
-
<div style={{
|
|
179
|
-
marginTop: '40px',
|
|
180
|
-
padding: '20px',
|
|
181
|
-
background: '#e7f3ff',
|
|
182
|
-
borderRadius: '8px'
|
|
183
|
-
}}>
|
|
184
|
-
<h3 style={{ marginTop: '0', color: '#0066cc' }}>🔗 API Endpoints</h3>
|
|
185
|
-
<ul style={{ color: '#333' }}>
|
|
186
|
-
<li><code>GET /api/users</code> - Fetch all users</li>
|
|
187
|
-
<li><code>POST /api/users</code> - Create a new user</li>
|
|
188
|
-
<li><code>GET /api/hello</code> - Simple hello endpoint</li>
|
|
189
|
-
</ul>
|
|
190
|
-
<p style={{ color: '#666', fontSize: '14px', marginBottom: '0' }}>
|
|
191
|
-
Try visiting <a href="/api/users" target="_blank">/api/users</a> or <a href="/api/hello" target="_blank">/api/hello</a> to see the API responses.
|
|
192
|
-
</p>
|
|
193
|
-
</div>
|
|
194
|
-
</div>
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Page metadata for SEO
|
|
199
|
-
HomePage.title = 'Baraqex Fullstack Demo';
|
|
200
|
-
HomePage.description = 'A demonstration of Baraqex fullstack capabilities with SSR and API integration';
|