vg-coder-cli 1.0.0 → 1.0.1
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/package.json +3 -3
- package/vg-coder-cli-1.0.0.tgz +0 -0
- package/vg-coder-cli-1.0.1.tgz +0 -0
- package/test-project/package.json +0 -21
- package/test-project/src/controllers/userController.js +0 -129
- package/test-project/src/index.js +0 -46
- package/test-project/src/middleware/auth.js +0 -142
- package/test-project/styles/main.css +0 -287
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vg-coder-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "🚀 CLI tool to analyze projects, concatenate source files, count tokens, and export HTML with syntax highlighting and copy functionality",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node src/index.js",
|
|
11
11
|
"test": "jest",
|
|
12
|
-
"test:analyze": "node src/index.js analyze hdb-signature-service --max-tokens 8192 --theme monokai",
|
|
13
12
|
"test:watch": "jest --watch",
|
|
14
|
-
"dev": "nodemon src/index.js"
|
|
13
|
+
"dev": "nodemon src/index.js",
|
|
14
|
+
"push" : "npm pack && npm publish"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"cli",
|
package/vg-coder-cli-1.0.0.tgz
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "test-project",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "A test project for VG Coder CLI",
|
|
5
|
-
"main": "src/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "node src/index.js",
|
|
8
|
-
"test": "jest"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"express": "^4.18.0",
|
|
12
|
-
"lodash": "^4.17.21"
|
|
13
|
-
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"jest": "^29.0.0",
|
|
16
|
-
"nodemon": "^3.0.0"
|
|
17
|
-
},
|
|
18
|
-
"keywords": ["test", "demo", "vg-coder"],
|
|
19
|
-
"author": "VG Coder",
|
|
20
|
-
"license": "MIT"
|
|
21
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const _ = require('lodash');
|
|
3
|
-
const User = require('../models/User');
|
|
4
|
-
const { validateUser, validateEmail } = require('../utils/validation');
|
|
5
|
-
|
|
6
|
-
const router = express.Router();
|
|
7
|
-
|
|
8
|
-
// Get all users
|
|
9
|
-
router.get('/', async (req, res) => {
|
|
10
|
-
try {
|
|
11
|
-
const { page = 1, limit = 10, search } = req.query;
|
|
12
|
-
const offset = (page - 1) * limit;
|
|
13
|
-
|
|
14
|
-
let users = await User.findAll({
|
|
15
|
-
limit: parseInt(limit),
|
|
16
|
-
offset: parseInt(offset),
|
|
17
|
-
where: search ? {
|
|
18
|
-
name: { [Op.iLike]: `%${search}%` }
|
|
19
|
-
} : {}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
users = users.map(user => _.omit(user.toJSON(), ['password']));
|
|
23
|
-
|
|
24
|
-
res.json({
|
|
25
|
-
users,
|
|
26
|
-
pagination: {
|
|
27
|
-
page: parseInt(page),
|
|
28
|
-
limit: parseInt(limit),
|
|
29
|
-
total: await User.count()
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
} catch (error) {
|
|
33
|
-
res.status(500).json({ error: error.message });
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Get user by ID
|
|
38
|
-
router.get('/:id', async (req, res) => {
|
|
39
|
-
try {
|
|
40
|
-
const { id } = req.params;
|
|
41
|
-
const user = await User.findByPk(id);
|
|
42
|
-
|
|
43
|
-
if (!user) {
|
|
44
|
-
return res.status(404).json({ error: 'User not found' });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
res.json(_.omit(user.toJSON(), ['password']));
|
|
48
|
-
} catch (error) {
|
|
49
|
-
res.status(500).json({ error: error.message });
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Create new user
|
|
54
|
-
router.post('/', async (req, res) => {
|
|
55
|
-
try {
|
|
56
|
-
const userData = req.body;
|
|
57
|
-
|
|
58
|
-
// Validate input
|
|
59
|
-
const validation = validateUser(userData);
|
|
60
|
-
if (!validation.isValid) {
|
|
61
|
-
return res.status(400).json({
|
|
62
|
-
error: 'Validation failed',
|
|
63
|
-
details: validation.errors
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Check if email already exists
|
|
68
|
-
const existingUser = await User.findOne({
|
|
69
|
-
where: { email: userData.email }
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (existingUser) {
|
|
73
|
-
return res.status(409).json({
|
|
74
|
-
error: 'Email already exists'
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const user = await User.create(userData);
|
|
79
|
-
res.status(201).json(_.omit(user.toJSON(), ['password']));
|
|
80
|
-
} catch (error) {
|
|
81
|
-
res.status(500).json({ error: error.message });
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Update user
|
|
86
|
-
router.put('/:id', async (req, res) => {
|
|
87
|
-
try {
|
|
88
|
-
const { id } = req.params;
|
|
89
|
-
const updateData = req.body;
|
|
90
|
-
|
|
91
|
-
const user = await User.findByPk(id);
|
|
92
|
-
if (!user) {
|
|
93
|
-
return res.status(404).json({ error: 'User not found' });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Validate update data
|
|
97
|
-
const validation = validateUser(updateData, true);
|
|
98
|
-
if (!validation.isValid) {
|
|
99
|
-
return res.status(400).json({
|
|
100
|
-
error: 'Validation failed',
|
|
101
|
-
details: validation.errors
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
await user.update(updateData);
|
|
106
|
-
res.json(_.omit(user.toJSON(), ['password']));
|
|
107
|
-
} catch (error) {
|
|
108
|
-
res.status(500).json({ error: error.message });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Delete user
|
|
113
|
-
router.delete('/:id', async (req, res) => {
|
|
114
|
-
try {
|
|
115
|
-
const { id } = req.params;
|
|
116
|
-
const user = await User.findByPk(id);
|
|
117
|
-
|
|
118
|
-
if (!user) {
|
|
119
|
-
return res.status(404).json({ error: 'User not found' });
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
await user.destroy();
|
|
123
|
-
res.status(204).send();
|
|
124
|
-
} catch (error) {
|
|
125
|
-
res.status(500).json({ error: error.message });
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
module.exports = router;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const _ = require('lodash');
|
|
3
|
-
const userController = require('./controllers/userController');
|
|
4
|
-
const authMiddleware = require('./middleware/auth');
|
|
5
|
-
|
|
6
|
-
const app = express();
|
|
7
|
-
const PORT = process.env.PORT || 3000;
|
|
8
|
-
|
|
9
|
-
// Middleware
|
|
10
|
-
app.use(express.json());
|
|
11
|
-
app.use(express.urlencoded({ extended: true }));
|
|
12
|
-
|
|
13
|
-
// Routes
|
|
14
|
-
app.get('/', (req, res) => {
|
|
15
|
-
res.json({
|
|
16
|
-
message: 'Welcome to Test API',
|
|
17
|
-
version: '1.0.0',
|
|
18
|
-
timestamp: new Date().toISOString()
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
app.use('/api/users', authMiddleware, userController);
|
|
23
|
-
|
|
24
|
-
// Error handling middleware
|
|
25
|
-
app.use((err, req, res, next) => {
|
|
26
|
-
console.error(err.stack);
|
|
27
|
-
res.status(500).json({
|
|
28
|
-
error: 'Something went wrong!',
|
|
29
|
-
message: err.message
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// 404 handler
|
|
34
|
-
app.use('*', (req, res) => {
|
|
35
|
-
res.status(404).json({
|
|
36
|
-
error: 'Route not found',
|
|
37
|
-
path: req.originalUrl
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
app.listen(PORT, () => {
|
|
42
|
-
console.log(`Server is running on port ${PORT}`);
|
|
43
|
-
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
module.exports = app;
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
const jwt = require('jsonwebtoken');
|
|
2
|
-
const User = require('../models/User');
|
|
3
|
-
|
|
4
|
-
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Authentication middleware
|
|
8
|
-
* Verifies JWT token and attaches user to request
|
|
9
|
-
*/
|
|
10
|
-
const authMiddleware = async (req, res, next) => {
|
|
11
|
-
try {
|
|
12
|
-
const authHeader = req.headers.authorization;
|
|
13
|
-
|
|
14
|
-
if (!authHeader) {
|
|
15
|
-
return res.status(401).json({
|
|
16
|
-
error: 'Access denied. No token provided.'
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const token = authHeader.startsWith('Bearer ')
|
|
21
|
-
? authHeader.slice(7)
|
|
22
|
-
: authHeader;
|
|
23
|
-
|
|
24
|
-
if (!token) {
|
|
25
|
-
return res.status(401).json({
|
|
26
|
-
error: 'Access denied. Invalid token format.'
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Verify token
|
|
31
|
-
const decoded = jwt.verify(token, JWT_SECRET);
|
|
32
|
-
|
|
33
|
-
// Get user from database
|
|
34
|
-
const user = await User.findByPk(decoded.userId);
|
|
35
|
-
if (!user) {
|
|
36
|
-
return res.status(401).json({
|
|
37
|
-
error: 'Access denied. User not found.'
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Check if user is active
|
|
42
|
-
if (!user.isActive) {
|
|
43
|
-
return res.status(401).json({
|
|
44
|
-
error: 'Access denied. User account is disabled.'
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Attach user to request
|
|
49
|
-
req.user = user;
|
|
50
|
-
next();
|
|
51
|
-
|
|
52
|
-
} catch (error) {
|
|
53
|
-
if (error.name === 'JsonWebTokenError') {
|
|
54
|
-
return res.status(401).json({
|
|
55
|
-
error: 'Access denied. Invalid token.'
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (error.name === 'TokenExpiredError') {
|
|
60
|
-
return res.status(401).json({
|
|
61
|
-
error: 'Access denied. Token expired.'
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
console.error('Auth middleware error:', error);
|
|
66
|
-
res.status(500).json({
|
|
67
|
-
error: 'Internal server error during authentication.'
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Role-based authorization middleware
|
|
74
|
-
*/
|
|
75
|
-
const requireRole = (roles) => {
|
|
76
|
-
return (req, res, next) => {
|
|
77
|
-
if (!req.user) {
|
|
78
|
-
return res.status(401).json({
|
|
79
|
-
error: 'Access denied. User not authenticated.'
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const userRoles = Array.isArray(req.user.roles)
|
|
84
|
-
? req.user.roles
|
|
85
|
-
: [req.user.role];
|
|
86
|
-
|
|
87
|
-
const hasRequiredRole = roles.some(role =>
|
|
88
|
-
userRoles.includes(role)
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
if (!hasRequiredRole) {
|
|
92
|
-
return res.status(403).json({
|
|
93
|
-
error: 'Access denied. Insufficient permissions.',
|
|
94
|
-
required: roles,
|
|
95
|
-
current: userRoles
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
next();
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Optional authentication middleware
|
|
105
|
-
* Attaches user if token is valid, but doesn't require it
|
|
106
|
-
*/
|
|
107
|
-
const optionalAuth = async (req, res, next) => {
|
|
108
|
-
try {
|
|
109
|
-
const authHeader = req.headers.authorization;
|
|
110
|
-
|
|
111
|
-
if (!authHeader) {
|
|
112
|
-
return next();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const token = authHeader.startsWith('Bearer ')
|
|
116
|
-
? authHeader.slice(7)
|
|
117
|
-
: authHeader;
|
|
118
|
-
|
|
119
|
-
if (!token) {
|
|
120
|
-
return next();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const decoded = jwt.verify(token, JWT_SECRET);
|
|
124
|
-
const user = await User.findByPk(decoded.userId);
|
|
125
|
-
|
|
126
|
-
if (user && user.isActive) {
|
|
127
|
-
req.user = user;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
next();
|
|
131
|
-
|
|
132
|
-
} catch (error) {
|
|
133
|
-
// Ignore auth errors in optional auth
|
|
134
|
-
next();
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
module.exports = {
|
|
139
|
-
authMiddleware,
|
|
140
|
-
requireRole,
|
|
141
|
-
optionalAuth
|
|
142
|
-
};
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
/* Main stylesheet for test project */
|
|
2
|
-
|
|
3
|
-
:root {
|
|
4
|
-
--primary-color: #007bff;
|
|
5
|
-
--secondary-color: #6c757d;
|
|
6
|
-
--success-color: #28a745;
|
|
7
|
-
--danger-color: #dc3545;
|
|
8
|
-
--warning-color: #ffc107;
|
|
9
|
-
--info-color: #17a2b8;
|
|
10
|
-
--light-color: #f8f9fa;
|
|
11
|
-
--dark-color: #343a40;
|
|
12
|
-
|
|
13
|
-
--font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
14
|
-
--font-family-mono: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
15
|
-
|
|
16
|
-
--border-radius: 0.375rem;
|
|
17
|
-
--box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
18
|
-
--transition: all 0.15s ease-in-out;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
* {
|
|
22
|
-
box-sizing: border-box;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
body {
|
|
26
|
-
font-family: var(--font-family-sans);
|
|
27
|
-
line-height: 1.6;
|
|
28
|
-
color: var(--dark-color);
|
|
29
|
-
background-color: #fff;
|
|
30
|
-
margin: 0;
|
|
31
|
-
padding: 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.container {
|
|
35
|
-
max-width: 1200px;
|
|
36
|
-
margin: 0 auto;
|
|
37
|
-
padding: 0 1rem;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.header {
|
|
41
|
-
background-color: var(--primary-color);
|
|
42
|
-
color: white;
|
|
43
|
-
padding: 1rem 0;
|
|
44
|
-
box-shadow: var(--box-shadow);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.header h1 {
|
|
48
|
-
margin: 0;
|
|
49
|
-
font-size: 1.75rem;
|
|
50
|
-
font-weight: 600;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.nav {
|
|
54
|
-
background-color: var(--light-color);
|
|
55
|
-
border-bottom: 1px solid #dee2e6;
|
|
56
|
-
padding: 0.5rem 0;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.nav ul {
|
|
60
|
-
list-style: none;
|
|
61
|
-
margin: 0;
|
|
62
|
-
padding: 0;
|
|
63
|
-
display: flex;
|
|
64
|
-
gap: 2rem;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.nav a {
|
|
68
|
-
color: var(--dark-color);
|
|
69
|
-
text-decoration: none;
|
|
70
|
-
padding: 0.5rem 1rem;
|
|
71
|
-
border-radius: var(--border-radius);
|
|
72
|
-
transition: var(--transition);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
.nav a:hover,
|
|
76
|
-
.nav a.active {
|
|
77
|
-
background-color: var(--primary-color);
|
|
78
|
-
color: white;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.main {
|
|
82
|
-
padding: 2rem 0;
|
|
83
|
-
min-height: calc(100vh - 200px);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.card {
|
|
87
|
-
background: white;
|
|
88
|
-
border: 1px solid #dee2e6;
|
|
89
|
-
border-radius: var(--border-radius);
|
|
90
|
-
box-shadow: var(--box-shadow);
|
|
91
|
-
margin-bottom: 1.5rem;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.card-header {
|
|
95
|
-
padding: 1rem 1.25rem;
|
|
96
|
-
background-color: var(--light-color);
|
|
97
|
-
border-bottom: 1px solid #dee2e6;
|
|
98
|
-
font-weight: 600;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.card-body {
|
|
102
|
-
padding: 1.25rem;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.btn {
|
|
106
|
-
display: inline-block;
|
|
107
|
-
padding: 0.5rem 1rem;
|
|
108
|
-
font-size: 1rem;
|
|
109
|
-
font-weight: 400;
|
|
110
|
-
line-height: 1.5;
|
|
111
|
-
text-align: center;
|
|
112
|
-
text-decoration: none;
|
|
113
|
-
vertical-align: middle;
|
|
114
|
-
cursor: pointer;
|
|
115
|
-
border: 1px solid transparent;
|
|
116
|
-
border-radius: var(--border-radius);
|
|
117
|
-
transition: var(--transition);
|
|
118
|
-
user-select: none;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.btn-primary {
|
|
122
|
-
color: white;
|
|
123
|
-
background-color: var(--primary-color);
|
|
124
|
-
border-color: var(--primary-color);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.btn-primary:hover {
|
|
128
|
-
background-color: #0056b3;
|
|
129
|
-
border-color: #004085;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.btn-secondary {
|
|
133
|
-
color: white;
|
|
134
|
-
background-color: var(--secondary-color);
|
|
135
|
-
border-color: var(--secondary-color);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.btn-success {
|
|
139
|
-
color: white;
|
|
140
|
-
background-color: var(--success-color);
|
|
141
|
-
border-color: var(--success-color);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.btn-danger {
|
|
145
|
-
color: white;
|
|
146
|
-
background-color: var(--danger-color);
|
|
147
|
-
border-color: var(--danger-color);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.form-group {
|
|
151
|
-
margin-bottom: 1rem;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.form-label {
|
|
155
|
-
display: block;
|
|
156
|
-
margin-bottom: 0.5rem;
|
|
157
|
-
font-weight: 500;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.form-control {
|
|
161
|
-
display: block;
|
|
162
|
-
width: 100%;
|
|
163
|
-
padding: 0.5rem 0.75rem;
|
|
164
|
-
font-size: 1rem;
|
|
165
|
-
line-height: 1.5;
|
|
166
|
-
color: var(--dark-color);
|
|
167
|
-
background-color: white;
|
|
168
|
-
border: 1px solid #ced4da;
|
|
169
|
-
border-radius: var(--border-radius);
|
|
170
|
-
transition: var(--transition);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
.form-control:focus {
|
|
174
|
-
outline: 0;
|
|
175
|
-
border-color: #80bdff;
|
|
176
|
-
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.table {
|
|
180
|
-
width: 100%;
|
|
181
|
-
margin-bottom: 1rem;
|
|
182
|
-
border-collapse: collapse;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.table th,
|
|
186
|
-
.table td {
|
|
187
|
-
padding: 0.75rem;
|
|
188
|
-
vertical-align: top;
|
|
189
|
-
border-top: 1px solid #dee2e6;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.table thead th {
|
|
193
|
-
vertical-align: bottom;
|
|
194
|
-
border-bottom: 2px solid #dee2e6;
|
|
195
|
-
background-color: var(--light-color);
|
|
196
|
-
font-weight: 600;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.table tbody tr:hover {
|
|
200
|
-
background-color: rgba(0, 0, 0, 0.075);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.alert {
|
|
204
|
-
position: relative;
|
|
205
|
-
padding: 0.75rem 1.25rem;
|
|
206
|
-
margin-bottom: 1rem;
|
|
207
|
-
border: 1px solid transparent;
|
|
208
|
-
border-radius: var(--border-radius);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.alert-success {
|
|
212
|
-
color: #155724;
|
|
213
|
-
background-color: #d4edda;
|
|
214
|
-
border-color: #c3e6cb;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.alert-danger {
|
|
218
|
-
color: #721c24;
|
|
219
|
-
background-color: #f8d7da;
|
|
220
|
-
border-color: #f5c6cb;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.alert-warning {
|
|
224
|
-
color: #856404;
|
|
225
|
-
background-color: #fff3cd;
|
|
226
|
-
border-color: #ffeaa7;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.footer {
|
|
230
|
-
background-color: var(--dark-color);
|
|
231
|
-
color: white;
|
|
232
|
-
text-align: center;
|
|
233
|
-
padding: 1rem 0;
|
|
234
|
-
margin-top: auto;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/* Responsive design */
|
|
238
|
-
@media (max-width: 768px) {
|
|
239
|
-
.container {
|
|
240
|
-
padding: 0 0.5rem;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
.nav ul {
|
|
244
|
-
flex-direction: column;
|
|
245
|
-
gap: 0;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
.nav a {
|
|
249
|
-
display: block;
|
|
250
|
-
border-radius: 0;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.main {
|
|
254
|
-
padding: 1rem 0;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.card-body {
|
|
258
|
-
padding: 1rem;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/* Utility classes */
|
|
263
|
-
.text-center { text-align: center; }
|
|
264
|
-
.text-left { text-align: left; }
|
|
265
|
-
.text-right { text-align: right; }
|
|
266
|
-
|
|
267
|
-
.mt-1 { margin-top: 0.25rem; }
|
|
268
|
-
.mt-2 { margin-top: 0.5rem; }
|
|
269
|
-
.mt-3 { margin-top: 1rem; }
|
|
270
|
-
.mt-4 { margin-top: 1.5rem; }
|
|
271
|
-
.mt-5 { margin-top: 3rem; }
|
|
272
|
-
|
|
273
|
-
.mb-1 { margin-bottom: 0.25rem; }
|
|
274
|
-
.mb-2 { margin-bottom: 0.5rem; }
|
|
275
|
-
.mb-3 { margin-bottom: 1rem; }
|
|
276
|
-
.mb-4 { margin-bottom: 1.5rem; }
|
|
277
|
-
.mb-5 { margin-bottom: 3rem; }
|
|
278
|
-
|
|
279
|
-
.d-none { display: none; }
|
|
280
|
-
.d-block { display: block; }
|
|
281
|
-
.d-inline { display: inline; }
|
|
282
|
-
.d-inline-block { display: inline-block; }
|
|
283
|
-
.d-flex { display: flex; }
|
|
284
|
-
|
|
285
|
-
.justify-content-center { justify-content: center; }
|
|
286
|
-
.justify-content-between { justify-content: space-between; }
|
|
287
|
-
.align-items-center { align-items: center; }
|