clovie 0.1.6 → 0.1.7
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/bin/cli.js +50 -5
- package/package.json +1 -1
- package/templates/server/README.md +111 -0
- package/templates/server/clovie.config.js +107 -0
- package/templates/server/package.json +24 -0
- package/templates/server/partials/footer.html +16 -0
- package/templates/server/partials/header.html +26 -0
- package/templates/server/partials/nav.html +49 -0
- package/templates/server/scripts/app.js +213 -0
- package/templates/server/styles/main.scss +715 -0
- package/templates/server/views/dashboard.html +70 -0
- package/templates/server/views/index.html +66 -0
- package/templates/server/views/login.html +73 -0
- package/templates/server/views/profile.html +65 -0
- package/templates/static/README.md +67 -0
- package/templates/static/clovie.config.js +40 -0
- package/templates/static/package.json +20 -0
- package/templates/static/partials/footer.html +8 -0
- package/templates/static/partials/header.html +9 -0
- package/templates/static/scripts/main.js +73 -0
- package/templates/static/styles/main.scss +262 -0
- package/templates/static/views/about.html +46 -0
- package/templates/static/views/index.html +59 -0
package/bin/cli.js
CHANGED
|
@@ -12,11 +12,31 @@ import { createClovie } from "../lib/createClovie.js";
|
|
|
12
12
|
|
|
13
13
|
// Check for create command first (before any argument parsing)
|
|
14
14
|
if (process.argv.includes('create') && process.argv.length > 2) {
|
|
15
|
-
const
|
|
15
|
+
const createArgIndex = process.argv.indexOf('create');
|
|
16
|
+
const projectName = process.argv[createArgIndex + 1];
|
|
16
17
|
|
|
17
18
|
if (!projectName) {
|
|
18
19
|
console.error('Error: Please provide a project name');
|
|
19
|
-
console.error('Usage: clovie create <project-name>');
|
|
20
|
+
console.error('Usage: clovie create <project-name> [--template <template-type>]');
|
|
21
|
+
console.error('Available templates: default, static, server');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Parse arguments to get template option
|
|
26
|
+
const createArgs = process.argv.slice(createArgIndex + 2);
|
|
27
|
+
let templateType = 'default';
|
|
28
|
+
|
|
29
|
+
// Look for --template or -t flag
|
|
30
|
+
const templateIndex = createArgs.findIndex(arg => arg === '--template' || arg === '-t');
|
|
31
|
+
if (templateIndex !== -1 && createArgs[templateIndex + 1]) {
|
|
32
|
+
templateType = createArgs[templateIndex + 1];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Validate template type
|
|
36
|
+
const validTemplates = ['default', 'static', 'server'];
|
|
37
|
+
if (!validTemplates.includes(templateType)) {
|
|
38
|
+
console.error(`Error: Invalid template type '${templateType}'`);
|
|
39
|
+
console.error(`Available templates: ${validTemplates.join(', ')}`);
|
|
20
40
|
process.exit(1);
|
|
21
41
|
}
|
|
22
42
|
|
|
@@ -28,8 +48,8 @@ if (process.argv.includes('create') && process.argv.length > 2) {
|
|
|
28
48
|
process.exit(1);
|
|
29
49
|
}
|
|
30
50
|
|
|
31
|
-
// Copy template files
|
|
32
|
-
const templateDir = path.join(__dirname,
|
|
51
|
+
// Copy template files from the specified template
|
|
52
|
+
const templateDir = path.join(__dirname, `../templates/${templateType}`);
|
|
33
53
|
|
|
34
54
|
// Create project directory first
|
|
35
55
|
fs.mkdirSync(projectPath, { recursive: true });
|
|
@@ -54,12 +74,37 @@ if (process.argv.includes('create') && process.argv.length > 2) {
|
|
|
54
74
|
};
|
|
55
75
|
|
|
56
76
|
try {
|
|
77
|
+
// Check if template directory exists
|
|
78
|
+
if (!fs.existsSync(templateDir)) {
|
|
79
|
+
console.error(`Error: Template '${templateType}' not found at ${templateDir}`);
|
|
80
|
+
console.error(`Available templates: ${validTemplates.join(', ')}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
57
84
|
await copyDir(templateDir, projectPath);
|
|
58
|
-
console.log(`✅ Clovie project created successfully
|
|
85
|
+
console.log(`✅ Clovie ${templateType} project created successfully!`);
|
|
86
|
+
console.log(`📁 Project location: ${projectPath}`);
|
|
87
|
+
console.log(`🎨 Template: ${templateType}`);
|
|
59
88
|
console.log('\nNext steps:');
|
|
60
89
|
console.log(` cd ${projectName}`);
|
|
61
90
|
console.log(' npm install');
|
|
62
91
|
console.log(' npm run dev');
|
|
92
|
+
|
|
93
|
+
// Show template-specific next steps
|
|
94
|
+
if (templateType === 'server') {
|
|
95
|
+
console.log('\n🌐 Server template features:');
|
|
96
|
+
console.log(' • API endpoints ready at /api/*');
|
|
97
|
+
console.log(' • Admin dashboard at /dashboard.html');
|
|
98
|
+
console.log(' • User profiles at /user/:id');
|
|
99
|
+
console.log(' • Use "npm run start" for production server');
|
|
100
|
+
} else if (templateType === 'static') {
|
|
101
|
+
console.log('\n📄 Static template features:');
|
|
102
|
+
console.log(' • SEO optimized pages');
|
|
103
|
+
console.log(' • Modern responsive design');
|
|
104
|
+
console.log(' • Ready for JAMstack deployment');
|
|
105
|
+
console.log(' • Use "npm run build" to generate static files');
|
|
106
|
+
}
|
|
107
|
+
|
|
63
108
|
process.exit(0);
|
|
64
109
|
} catch (err) {
|
|
65
110
|
console.error('Error creating project:', err);
|
package/package.json
CHANGED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
A server-side web application built with [Clovie](https://github.com/adrianjonmiller/clovie).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🌐 Express.js server with Clovie integration
|
|
8
|
+
- 🔌 API routes with state management
|
|
9
|
+
- 📄 Server-side rendered views
|
|
10
|
+
- 🎨 Modern responsive UI
|
|
11
|
+
- 🔄 Live reload in development
|
|
12
|
+
- 📊 Built-in dashboard and user management
|
|
13
|
+
- ⚡ Real-time WebSocket support
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install dependencies
|
|
19
|
+
npm install
|
|
20
|
+
|
|
21
|
+
# Start development server (with live reload)
|
|
22
|
+
npm run dev
|
|
23
|
+
|
|
24
|
+
# Start production server
|
|
25
|
+
npm start
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Project Structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
├── views/ # Server-rendered templates
|
|
32
|
+
│ ├── index.html # Homepage
|
|
33
|
+
│ ├── dashboard.html # Admin dashboard
|
|
34
|
+
│ ├── login.html # Login page
|
|
35
|
+
│ └── profile.html # User profile page
|
|
36
|
+
├── scripts/ # Client-side JavaScript
|
|
37
|
+
│ └── app.js # Main application logic
|
|
38
|
+
├── styles/ # SCSS stylesheets
|
|
39
|
+
│ └── main.scss # Main styles with dashboard UI
|
|
40
|
+
├── partials/ # Reusable template components
|
|
41
|
+
│ ├── header.html # Site header with user menu
|
|
42
|
+
│ ├── nav.html # Sidebar navigation
|
|
43
|
+
│ └── footer.html # Site footer
|
|
44
|
+
└── clovie.config.js # Server configuration
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Available API Endpoints
|
|
48
|
+
|
|
49
|
+
This template includes these working API endpoints:
|
|
50
|
+
|
|
51
|
+
- `GET /api/status` - Server status and uptime
|
|
52
|
+
- `GET /api/users` - List all users with total count
|
|
53
|
+
- `POST /api/users` - Create a new user (requires name, email in body)
|
|
54
|
+
- `GET /api/users/:id` - Get specific user by ID
|
|
55
|
+
|
|
56
|
+
## Server Routes
|
|
57
|
+
|
|
58
|
+
- `/` - Homepage
|
|
59
|
+
- `/dashboard.html` - Admin dashboard
|
|
60
|
+
- `/login.html` - Login page
|
|
61
|
+
- `/user/:id` - Dynamic user profile page (server-rendered)
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
The `clovie.config.js` file shows how to:
|
|
66
|
+
|
|
67
|
+
- Set up API routes with `action` functions
|
|
68
|
+
- Configure middleware with Express functions
|
|
69
|
+
- Create dynamic routes with templates and data functions
|
|
70
|
+
- Use Clovie's state management system
|
|
71
|
+
|
|
72
|
+
## State Management
|
|
73
|
+
|
|
74
|
+
Clovie provides a state system accessible in API actions and route data functions:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
// In API actions and route data functions:
|
|
78
|
+
const users = state.get('users') || [];
|
|
79
|
+
state.set('users', newUsersArray);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Start with live reload
|
|
86
|
+
npm run dev
|
|
87
|
+
# Server runs at http://localhost:3000
|
|
88
|
+
# Live reload via WebSocket
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API Usage Examples
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
// Get server status
|
|
95
|
+
fetch('/api/status')
|
|
96
|
+
|
|
97
|
+
// Create a user
|
|
98
|
+
fetch('/api/users', {
|
|
99
|
+
method: 'POST',
|
|
100
|
+
headers: { 'Content-Type': 'application/json' },
|
|
101
|
+
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// Get user by ID
|
|
105
|
+
fetch('/api/users/123')
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Learn More
|
|
109
|
+
|
|
110
|
+
- [Clovie Documentation](https://github.com/adrianjonmiller/clovie)
|
|
111
|
+
- [Express.js Guide](https://expressjs.com/)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
// Server-side application configuration
|
|
5
|
+
type: 'server',
|
|
6
|
+
port: 3000,
|
|
7
|
+
|
|
8
|
+
data: {
|
|
9
|
+
title: '{{projectName}}',
|
|
10
|
+
description: 'Full-stack web application built with Clovie',
|
|
11
|
+
author: 'Your Name',
|
|
12
|
+
version: '1.0.0'
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
// Middleware configuration (functions that run before routes)
|
|
16
|
+
middleware: [
|
|
17
|
+
express.json(),
|
|
18
|
+
express.urlencoded({ extended: true })
|
|
19
|
+
],
|
|
20
|
+
|
|
21
|
+
// API routes configuration
|
|
22
|
+
api: [
|
|
23
|
+
{
|
|
24
|
+
name: 'API Status',
|
|
25
|
+
path: '/api/status',
|
|
26
|
+
method: 'GET',
|
|
27
|
+
action: async (state, event) => {
|
|
28
|
+
return {
|
|
29
|
+
status: 'ok',
|
|
30
|
+
timestamp: new Date().toISOString(),
|
|
31
|
+
uptime: process.uptime()
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Get Users',
|
|
37
|
+
path: '/api/users',
|
|
38
|
+
method: 'GET',
|
|
39
|
+
action: async (state, event) => {
|
|
40
|
+
// Get users from state or return mock data
|
|
41
|
+
const users = state.get ? state.get('users') || [] : [];
|
|
42
|
+
return {
|
|
43
|
+
users,
|
|
44
|
+
total: users.length
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Create User',
|
|
50
|
+
path: '/api/users',
|
|
51
|
+
method: 'POST',
|
|
52
|
+
action: async (state, event) => {
|
|
53
|
+
const { name, email } = event.body;
|
|
54
|
+
const newUser = {
|
|
55
|
+
id: Date.now(),
|
|
56
|
+
name,
|
|
57
|
+
email,
|
|
58
|
+
createdAt: new Date().toISOString()
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Add user to state if available
|
|
62
|
+
if (state.set) {
|
|
63
|
+
const users = state.get('users') || [];
|
|
64
|
+
users.push(newUser);
|
|
65
|
+
state.set('users', users);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { success: true, user: newUser };
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'Get User by ID',
|
|
73
|
+
path: '/api/users/:id',
|
|
74
|
+
method: 'GET',
|
|
75
|
+
action: async (state, event) => {
|
|
76
|
+
const userId = parseInt(event.params.id);
|
|
77
|
+
const users = state.get ? state.get('users') || [] : [];
|
|
78
|
+
const user = users.find(u => u.id === userId);
|
|
79
|
+
|
|
80
|
+
if (!user) {
|
|
81
|
+
return { error: 'User not found', status: 404 };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { user };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
|
|
89
|
+
// Configured routes (server-rendered pages with templates)
|
|
90
|
+
routes: [
|
|
91
|
+
{
|
|
92
|
+
name: 'User Profile',
|
|
93
|
+
path: '/user/:id',
|
|
94
|
+
template: './views/profile.html',
|
|
95
|
+
data: async (state, params) => {
|
|
96
|
+
const userId = parseInt(params.id);
|
|
97
|
+
const users = state.get ? state.get('users') || [] : [];
|
|
98
|
+
const user = users.find(u => u.id === userId);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
user: user || { name: 'Unknown User', email: 'unknown@example.com' },
|
|
102
|
+
title: `User Profile - ${user?.name || 'Unknown'}`
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Server-side web application built with Clovie",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "clovie watch",
|
|
9
|
+
"start": "clovie server",
|
|
10
|
+
"build": "clovie build"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@brickworks/clovie": "^0.1.5",
|
|
14
|
+
"express": "^4.18.2"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"server",
|
|
18
|
+
"web-app",
|
|
19
|
+
"clovie",
|
|
20
|
+
"full-stack",
|
|
21
|
+
"api"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<footer class="footer">
|
|
2
|
+
<div class="container">
|
|
3
|
+
<div class="footer__content">
|
|
4
|
+
<div class="footer__section">
|
|
5
|
+
<p>© 2024 {{title}}. Built with <a href="https://github.com/adrianjonmiller/clovie" target="_blank">Clovie</a></p>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="footer__section">
|
|
8
|
+
<div class="footer__links">
|
|
9
|
+
<a href="/api/status">API Status</a>
|
|
10
|
+
<a href="/docs">Documentation</a>
|
|
11
|
+
<a href="/support">Support</a>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</footer>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<header class="header">
|
|
2
|
+
<div class="container">
|
|
3
|
+
<div class="header__content">
|
|
4
|
+
<a href="/" class="header__brand">
|
|
5
|
+
<span class="brand-icon">⚡</span>
|
|
6
|
+
{{title}}
|
|
7
|
+
</a>
|
|
8
|
+
|
|
9
|
+
<div class="header__actions">
|
|
10
|
+
<button class="btn btn--small btn--outline" onclick="toggleTheme()">🌙</button>
|
|
11
|
+
<div class="user-menu" id="user-menu">
|
|
12
|
+
<button class="user-avatar" onclick="toggleUserMenu()">
|
|
13
|
+
<img src="https://ui-avatars.com/api/?name=User&background=2563eb&color=fff" alt="User Avatar">
|
|
14
|
+
</button>
|
|
15
|
+
<div class="user-dropdown" id="user-dropdown">
|
|
16
|
+
<a href="/dashboard.html">Dashboard</a>
|
|
17
|
+
<a href="/profile">Profile</a>
|
|
18
|
+
<a href="/settings">Settings</a>
|
|
19
|
+
<hr>
|
|
20
|
+
<a href="#" onclick="logout()">Sign Out</a>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</header>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<nav class="sidebar" id="sidebar">
|
|
2
|
+
<div class="sidebar__content">
|
|
3
|
+
<ul class="nav-menu">
|
|
4
|
+
<li class="nav-item">
|
|
5
|
+
<a href="/dashboard.html" class="nav-link" data-page="dashboard">
|
|
6
|
+
<span class="nav-icon">📊</span>
|
|
7
|
+
Dashboard
|
|
8
|
+
</a>
|
|
9
|
+
</li>
|
|
10
|
+
<li class="nav-item">
|
|
11
|
+
<a href="/users" class="nav-link" data-page="users">
|
|
12
|
+
<span class="nav-icon">👥</span>
|
|
13
|
+
Users
|
|
14
|
+
</a>
|
|
15
|
+
</li>
|
|
16
|
+
<li class="nav-item">
|
|
17
|
+
<a href="/posts" class="nav-link" data-page="posts">
|
|
18
|
+
<span class="nav-icon">📝</span>
|
|
19
|
+
Posts
|
|
20
|
+
</a>
|
|
21
|
+
</li>
|
|
22
|
+
<li class="nav-item">
|
|
23
|
+
<a href="/analytics" class="nav-link" data-page="analytics">
|
|
24
|
+
<span class="nav-icon">📈</span>
|
|
25
|
+
Analytics
|
|
26
|
+
</a>
|
|
27
|
+
</li>
|
|
28
|
+
<li class="nav-item">
|
|
29
|
+
<a href="/settings" class="nav-link" data-page="settings">
|
|
30
|
+
<span class="nav-icon">⚙️</span>
|
|
31
|
+
Settings
|
|
32
|
+
</a>
|
|
33
|
+
</li>
|
|
34
|
+
</ul>
|
|
35
|
+
|
|
36
|
+
<div class="sidebar__footer">
|
|
37
|
+
<div class="server-status">
|
|
38
|
+
<span class="status-dot status-dot--online"></span>
|
|
39
|
+
<span class="status-text">Server Online</span>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<button class="sidebar-toggle" onclick="toggleSidebar()">
|
|
45
|
+
<span></span>
|
|
46
|
+
<span></span>
|
|
47
|
+
<span></span>
|
|
48
|
+
</button>
|
|
49
|
+
</nav>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Server Application JavaScript
|
|
2
|
+
|
|
3
|
+
// API helper functions
|
|
4
|
+
const api = {
|
|
5
|
+
async get(url) {
|
|
6
|
+
const response = await fetch(url);
|
|
7
|
+
if (!response.ok) {
|
|
8
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
9
|
+
}
|
|
10
|
+
return response.json();
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
async post(url, data) {
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
},
|
|
19
|
+
body: JSON.stringify(data)
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
23
|
+
}
|
|
24
|
+
return response.json();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Dashboard functionality
|
|
29
|
+
async function loadDashboardData() {
|
|
30
|
+
try {
|
|
31
|
+
// Load API status
|
|
32
|
+
const status = await api.get('/api/status');
|
|
33
|
+
document.getElementById('server-uptime').textContent = `${Math.floor(status.uptime / 60)}m`;
|
|
34
|
+
|
|
35
|
+
// Load users
|
|
36
|
+
const usersData = await api.get('/api/users');
|
|
37
|
+
document.getElementById('user-count').textContent = usersData.total;
|
|
38
|
+
|
|
39
|
+
// Update recent activity
|
|
40
|
+
updateActivity(`System status: ${status.status}`);
|
|
41
|
+
updateActivity(`${usersData.total} users registered`);
|
|
42
|
+
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Error loading dashboard data:', error);
|
|
45
|
+
document.getElementById('user-count').textContent = 'Error';
|
|
46
|
+
document.getElementById('server-uptime').textContent = 'Error';
|
|
47
|
+
updateActivity('Failed to load data');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// User management functions
|
|
52
|
+
async function createUser() {
|
|
53
|
+
const name = prompt('Enter user name:');
|
|
54
|
+
const email = prompt('Enter user email:');
|
|
55
|
+
|
|
56
|
+
if (name && email) {
|
|
57
|
+
try {
|
|
58
|
+
const result = await api.post('/api/users', { name, email });
|
|
59
|
+
if (result.success) {
|
|
60
|
+
updateActivity(`Created user: ${name}`);
|
|
61
|
+
loadDashboardData(); // Refresh data
|
|
62
|
+
alert('User created successfully!');
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error creating user:', error);
|
|
66
|
+
alert('Failed to create user');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function refreshData() {
|
|
72
|
+
updateActivity('Refreshing data...');
|
|
73
|
+
await loadDashboardData();
|
|
74
|
+
updateActivity('Data refreshed');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function checkApiStatus() {
|
|
78
|
+
try {
|
|
79
|
+
const status = await api.get('/api/status');
|
|
80
|
+
alert(`API Status: ${status.status}\nUptime: ${Math.floor(status.uptime / 60)} minutes`);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
alert('API is not responding');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Utility functions
|
|
87
|
+
function updateActivity(message) {
|
|
88
|
+
const activityList = document.getElementById('recent-activity');
|
|
89
|
+
if (activityList) {
|
|
90
|
+
// Remove loading message if present
|
|
91
|
+
const loading = activityList.querySelector('.loading');
|
|
92
|
+
if (loading) loading.remove();
|
|
93
|
+
|
|
94
|
+
// Add new activity
|
|
95
|
+
const li = document.createElement('li');
|
|
96
|
+
li.className = 'activity-item';
|
|
97
|
+
li.innerHTML = `
|
|
98
|
+
<span class="activity-time">${new Date().toLocaleTimeString()}</span>
|
|
99
|
+
<span class="activity-message">${message}</span>
|
|
100
|
+
`;
|
|
101
|
+
activityList.insertBefore(li, activityList.firstChild);
|
|
102
|
+
|
|
103
|
+
// Keep only last 5 items
|
|
104
|
+
while (activityList.children.length > 5) {
|
|
105
|
+
activityList.removeChild(activityList.lastChild);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// User menu functionality
|
|
111
|
+
function toggleUserMenu() {
|
|
112
|
+
const dropdown = document.getElementById('user-dropdown');
|
|
113
|
+
dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Close dropdown when clicking outside
|
|
117
|
+
document.addEventListener('click', (e) => {
|
|
118
|
+
const userMenu = document.getElementById('user-menu');
|
|
119
|
+
const dropdown = document.getElementById('user-dropdown');
|
|
120
|
+
if (userMenu && !userMenu.contains(e.target)) {
|
|
121
|
+
dropdown.style.display = 'none';
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Sidebar functionality
|
|
126
|
+
function toggleSidebar() {
|
|
127
|
+
const sidebar = document.getElementById('sidebar');
|
|
128
|
+
sidebar.classList.toggle('sidebar--collapsed');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Theme toggle
|
|
132
|
+
function toggleTheme() {
|
|
133
|
+
document.body.classList.toggle('dark-theme');
|
|
134
|
+
const isDark = document.body.classList.contains('dark-theme');
|
|
135
|
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Load saved theme
|
|
139
|
+
if (localStorage.getItem('theme') === 'dark') {
|
|
140
|
+
document.body.classList.add('dark-theme');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Login form handling
|
|
144
|
+
async function handleLogin(e) {
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
const formData = new FormData(e.target);
|
|
147
|
+
const email = formData.get('email');
|
|
148
|
+
const password = formData.get('password');
|
|
149
|
+
|
|
150
|
+
// Show loading state
|
|
151
|
+
const button = e.target.querySelector('button[type="submit"]');
|
|
152
|
+
const buttonText = button.querySelector('.btn__text');
|
|
153
|
+
const buttonLoading = button.querySelector('.btn__loading');
|
|
154
|
+
|
|
155
|
+
buttonText.style.display = 'none';
|
|
156
|
+
buttonLoading.style.display = 'inline';
|
|
157
|
+
button.disabled = true;
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// In a real app, you'd call your login API
|
|
161
|
+
// const result = await api.post('/api/auth/login', { email, password });
|
|
162
|
+
|
|
163
|
+
// For demo, simulate login
|
|
164
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
165
|
+
|
|
166
|
+
// Redirect to dashboard
|
|
167
|
+
window.location.href = '/dashboard.html';
|
|
168
|
+
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// Show error
|
|
171
|
+
const errorDiv = document.getElementById('auth-error');
|
|
172
|
+
const errorMessage = document.getElementById('error-message');
|
|
173
|
+
errorMessage.textContent = 'Login failed. Please try again.';
|
|
174
|
+
errorDiv.style.display = 'block';
|
|
175
|
+
|
|
176
|
+
// Reset button
|
|
177
|
+
buttonText.style.display = 'inline';
|
|
178
|
+
buttonLoading.style.display = 'none';
|
|
179
|
+
button.disabled = false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Profile functions
|
|
184
|
+
function editProfile() {
|
|
185
|
+
alert('Edit profile functionality would go here');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function viewActivity() {
|
|
189
|
+
alert('User activity view would go here');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function downloadData() {
|
|
193
|
+
alert('Data download functionality would go here');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function loadUserActivity(userId) {
|
|
197
|
+
// Load user-specific activity
|
|
198
|
+
updateActivity(`Viewing profile for user ${userId}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Initialize based on current page
|
|
202
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
203
|
+
const currentPath = window.location.pathname;
|
|
204
|
+
|
|
205
|
+
// Update active navigation
|
|
206
|
+
document.querySelectorAll('.nav-link').forEach(link => {
|
|
207
|
+
if (link.getAttribute('href') === currentPath) {
|
|
208
|
+
link.classList.add('nav-link--active');
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
console.log('🚀 Server app initialized');
|
|
213
|
+
});
|