create-fullstack-boilerplate 1.0.0
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/README.md +390 -0
- package/index.js +78 -0
- package/lib/addDB.js +77 -0
- package/lib/addRoute.js +264 -0
- package/lib/copyProject.js +25 -0
- package/lib/dataTypes.js +79 -0
- package/lib/installDeps.js +11 -0
- package/lib/prompts.js +289 -0
- package/lib/setupExtraDB.js +172 -0
- package/lib/setupMainDB.js +9 -0
- package/lib/testDBConnection.js +31 -0
- package/lib/utils.js +39 -0
- package/package.json +45 -0
- package/template/Backend/.env +7 -0
- package/template/Backend/DB/DBInit.js +28 -0
- package/template/Backend/DB/dbConfigs.js +4 -0
- package/template/Backend/Models/index.js +54 -0
- package/template/Backend/README.md +535 -0
- package/template/Backend/middleware/authMiddleware.js +19 -0
- package/template/Backend/package-lock.json +2997 -0
- package/template/Backend/package.json +32 -0
- package/template/Backend/routes/authRoutes.js +15 -0
- package/template/Backend/routes/dashboardRoutes.js +13 -0
- package/template/Backend/routes/index.js +15 -0
- package/template/Backend/routes/settingsRoutes.js +9 -0
- package/template/Backend/server.js +70 -0
- package/template/Backend/services/authService.js +68 -0
- package/template/Backend/services/cryptoService.js +14 -0
- package/template/Backend/services/dashboardService.js +39 -0
- package/template/Backend/services/settingsService.js +43 -0
- package/template/Frontend/.env +3 -0
- package/template/Frontend/README.md +576 -0
- package/template/Frontend/eslint.config.js +29 -0
- package/template/Frontend/index.html +13 -0
- package/template/Frontend/package-lock.json +3690 -0
- package/template/Frontend/package.json +39 -0
- package/template/Frontend/public/PMDLogo.png +0 -0
- package/template/Frontend/public/pp.jpg +0 -0
- package/template/Frontend/public/tabicon.png +0 -0
- package/template/Frontend/src/App.jsx +71 -0
- package/template/Frontend/src/assets/fonts/ArticulatCFDemiBold/font.woff +0 -0
- package/template/Frontend/src/assets/fonts/ArticulatCFDemiBold/font.woff2 +0 -0
- package/template/Frontend/src/assets/fonts/ArticulatCFNormal/font.woff +0 -0
- package/template/Frontend/src/assets/fonts/ArticulatCFNormal/font.woff2 +0 -0
- package/template/Frontend/src/assets/fonts/ArticulatCFRegular/font.woff +0 -0
- package/template/Frontend/src/assets/fonts/ArticulatCFRegular/font.woff2 +0 -0
- package/template/Frontend/src/assets/fonts/MixtaProRegularItalic/font.woff +0 -0
- package/template/Frontend/src/assets/fonts/MixtaProRegularItalic/font.woff2 +0 -0
- package/template/Frontend/src/assets/fonts/fonts_sohne/OTF/S/303/266hneMono-Buch.otf +0 -0
- package/template/Frontend/src/assets/fonts/fonts_sohne/OTF/S/303/266hneMono-Leicht.otf +0 -0
- package/template/Frontend/src/assets/fonts/fonts_sohne/WOFF2/soehne-mono-buch.woff2 +0 -0
- package/template/Frontend/src/assets/fonts/fonts_sohne/WOFF2/soehne-mono-leicht.woff2 +0 -0
- package/template/Frontend/src/components/Layout.jsx +61 -0
- package/template/Frontend/src/components/Loader.jsx +19 -0
- package/template/Frontend/src/components/ProtectedRoute.jsx +19 -0
- package/template/Frontend/src/components/Sidebar.jsx +286 -0
- package/template/Frontend/src/components/ThemeToggle.jsx +30 -0
- package/template/Frontend/src/config/axiosClient.js +46 -0
- package/template/Frontend/src/config/encryption.js +11 -0
- package/template/Frontend/src/config/routes.js +65 -0
- package/template/Frontend/src/contexts/AuthContext.jsx +144 -0
- package/template/Frontend/src/contexts/ThemeContext.jsx +69 -0
- package/template/Frontend/src/index.css +88 -0
- package/template/Frontend/src/main.jsx +11 -0
- package/template/Frontend/src/pages/Dashboard.jsx +137 -0
- package/template/Frontend/src/pages/Login.jsx +195 -0
- package/template/Frontend/src/pages/NotFound.jsx +70 -0
- package/template/Frontend/src/pages/Settings.jsx +69 -0
- package/template/Frontend/tailwind.config.js +90 -0
- package/template/Frontend/vite.config.js +37 -0
- package/template/Readme.md +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { useNavigate, Link } from 'react-router-dom';
|
|
3
|
+
import { Eye, EyeOff, Mail, Lock, AlertCircle, User } from 'lucide-react';
|
|
4
|
+
import { useAuth } from '../contexts/AuthContext';
|
|
5
|
+
const Login = () => {
|
|
6
|
+
const navigate = useNavigate();
|
|
7
|
+
const { handleLogin } = useAuth();
|
|
8
|
+
|
|
9
|
+
const [formData, setFormData] = useState({
|
|
10
|
+
email: '',
|
|
11
|
+
password: ''
|
|
12
|
+
});
|
|
13
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
14
|
+
const [error, setError] = useState('');
|
|
15
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
16
|
+
const [rememberMe, setRememberMe] = useState(false);
|
|
17
|
+
|
|
18
|
+
const handleChange = (e) => {
|
|
19
|
+
const { name, value } = e.target;
|
|
20
|
+
setFormData(prev => ({ ...prev, [name]: value }));
|
|
21
|
+
setError('');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const handleSubmit = async (e) => {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
setIsLoading(true);
|
|
27
|
+
setError('');
|
|
28
|
+
|
|
29
|
+
if (!formData.email || !formData.password) {
|
|
30
|
+
setError('Please fill in all fields');
|
|
31
|
+
setIsLoading(false);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const result = await handleLogin(formData.email, formData.password);
|
|
37
|
+
|
|
38
|
+
if (result.success) {
|
|
39
|
+
navigate('/dashboard');
|
|
40
|
+
} else {
|
|
41
|
+
setError(result.error);
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Login error:', error);
|
|
45
|
+
setError('An unexpected error occurred. Please try again.');
|
|
46
|
+
} finally {
|
|
47
|
+
setIsLoading(false);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleForgotPassword = () => {
|
|
52
|
+
console.log('Forgot password clicked');
|
|
53
|
+
// Navigate to forgot password page when implemented
|
|
54
|
+
// navigate('/forgot-password');
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className="h-screen flex items-center justify-center relative overflow-hidden bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-100">
|
|
59
|
+
{/* Decorative diagonal lines */}
|
|
60
|
+
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
|
61
|
+
<div className="absolute -top-20 -right-20 w-40 h-full bg-gradient-to-b from-blue-400/20 via-indigo-400/10 to-transparent transform rotate-12"></div>
|
|
62
|
+
<div className="absolute -bottom-20 -left-20 w-40 h-full bg-gradient-to-t from-indigo-400/20 via-blue-400/10 to-transparent transform -rotate-12"></div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Main content */}
|
|
66
|
+
<div className="relative z-10 w-full max-w-md mx-4">
|
|
67
|
+
{/* Main Card */}
|
|
68
|
+
<div className="bg-white/90 backdrop-blur-sm shadow-2xl rounded-2xl border border-white/20 overflow-hidden">
|
|
69
|
+
{/* Header */}
|
|
70
|
+
<div className="px-6 pt-6 pb-4 text-center">
|
|
71
|
+
<div className="relative mb-4">
|
|
72
|
+
<img src={'/PMDLogo.png'} alt="PMD Logo" className='w-max h-14 mx-auto' />
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<h1 className="text-2xl font-articulatcf-demibold text-gray-800 mb-1">Welcome Back</h1>
|
|
76
|
+
<p className="text-gray-600 font-articulat-cf text-sm">Sign in to your account</p>
|
|
77
|
+
|
|
78
|
+
{/* Accent line */}
|
|
79
|
+
<div className="w-12 h-0.5 bg-gradient-to-r from-blue-500 to-indigo-500 rounded-full mx-auto mt-3"></div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Error Alert */}
|
|
83
|
+
{error && (
|
|
84
|
+
<div className="mx-6 mb-4">
|
|
85
|
+
<div className="bg-red-50 border border-red-200 rounded-lg p-3 flex items-start space-x-2">
|
|
86
|
+
<AlertCircle className="w-4 h-4 text-red-500 flex-shrink-0 mt-0.5" />
|
|
87
|
+
<span className="text-xs text-red-700 font-articulat-cf">{error}</span>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
<div className='flex flex-col w-full my-6 px-6 justify-start items-start'>
|
|
93
|
+
<p><strong><i>Temp Email:</i></strong> admin@npmjs.com</p>
|
|
94
|
+
<p><strong><i>Temp Pass:</i></strong> admin@786</p>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{/* Form */}
|
|
98
|
+
<form onSubmit={handleSubmit} className="px-6 pb-4 space-y-8">
|
|
99
|
+
{/* Email Field */}
|
|
100
|
+
<div className="space-y-3">
|
|
101
|
+
<label className="flex items-start text-md font-articulat-cf font-semibold text-gray-700">
|
|
102
|
+
<Mail className="w-5 h-5 mr-1.5 text-blue-500" />
|
|
103
|
+
Email Address
|
|
104
|
+
</label>
|
|
105
|
+
<input
|
|
106
|
+
type="email"
|
|
107
|
+
name="email"
|
|
108
|
+
value={formData.email}
|
|
109
|
+
onChange={handleChange}
|
|
110
|
+
placeholder="Enter your email"
|
|
111
|
+
className="w-full px-3 py-2.5 bg-gray-50 border border-gray-200 rounded-lg font-articulat-cf text-gray-800 placeholder:text-gray-400 text-sm"
|
|
112
|
+
required
|
|
113
|
+
disabled={isLoading}
|
|
114
|
+
autoComplete="email"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
{/* Password Field */}
|
|
119
|
+
<div className="space-y-3">
|
|
120
|
+
<label className="flex items-start text-md font-articulat-cf font-medium text-gray-700">
|
|
121
|
+
<Lock className="w-5 h-5 mr-1.5 text-blue-500" />
|
|
122
|
+
Password
|
|
123
|
+
</label>
|
|
124
|
+
<div className="relative">
|
|
125
|
+
<input
|
|
126
|
+
type={showPassword ? "text" : "password"}
|
|
127
|
+
name="password"
|
|
128
|
+
value={formData.password}
|
|
129
|
+
onChange={handleChange}
|
|
130
|
+
placeholder="Enter your password"
|
|
131
|
+
className="w-full px-3 py-2.5 bg-gray-50 border border-gray-200 rounded-lg font-articulat-cf text-gray-800 placeholder:text-gray-400 pr-10 text-sm"
|
|
132
|
+
required
|
|
133
|
+
disabled={isLoading}
|
|
134
|
+
autoComplete="current-password"
|
|
135
|
+
/>
|
|
136
|
+
<button
|
|
137
|
+
type="button"
|
|
138
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
139
|
+
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 transition-colors"
|
|
140
|
+
disabled={isLoading}
|
|
141
|
+
>
|
|
142
|
+
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
{/* Remember Me & Forgot Password */}
|
|
148
|
+
<div className="flex items-center justify-between">
|
|
149
|
+
<label className="flex items-center cursor-pointer">
|
|
150
|
+
<input
|
|
151
|
+
type="checkbox"
|
|
152
|
+
checked={rememberMe}
|
|
153
|
+
onChange={(e) => setRememberMe(e.target.checked)}
|
|
154
|
+
disabled={isLoading}
|
|
155
|
+
className="w-3.5 h-3.5 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 focus:ring-1"
|
|
156
|
+
/>
|
|
157
|
+
<span className="ml-2 text-xs text-gray-600 font-articulat-cf">Remember me</span>
|
|
158
|
+
</label>
|
|
159
|
+
<button
|
|
160
|
+
type="button"
|
|
161
|
+
onClick={handleForgotPassword}
|
|
162
|
+
className="text-xs text-blue-600 hover:text-blue-700 font-articulat-cf font-medium transition-colors"
|
|
163
|
+
disabled={isLoading}
|
|
164
|
+
>
|
|
165
|
+
Forgot password?
|
|
166
|
+
</button>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
{/* Submit Button */}
|
|
170
|
+
<button
|
|
171
|
+
type="submit"
|
|
172
|
+
disabled={isLoading}
|
|
173
|
+
className="mx-auto w-max bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-articulatcf-demibold py-2.5 px-4 rounded-lg transition-all duration-200 shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center text-sm"
|
|
174
|
+
>
|
|
175
|
+
{isLoading ? (
|
|
176
|
+
<>
|
|
177
|
+
<div className="w-4 h-4 border-2 border-white/20 border-t-white rounded-full animate-spin mr-2"></div>
|
|
178
|
+
Signing In...
|
|
179
|
+
</>
|
|
180
|
+
) : (
|
|
181
|
+
<>
|
|
182
|
+
<User className="w-4 h-4 mr-2" />
|
|
183
|
+
Sign In
|
|
184
|
+
</>
|
|
185
|
+
)}
|
|
186
|
+
</button>
|
|
187
|
+
</form>
|
|
188
|
+
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export default Login;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useTheme } from "../contexts/ThemeContext";
|
|
2
|
+
import { useNavigate } from "react-router-dom";
|
|
3
|
+
import { Home, User, AlertCircle } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
const NotFound = () => {
|
|
6
|
+
const { isDark } = useTheme();
|
|
7
|
+
const navigate = useNavigate();
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className={`min-h-screen w-full flex items-center justify-center p-4 ${isDark ? 'bg-gray-900' : 'bg-gray-50'} transition-colors duration-200`}>
|
|
11
|
+
<div className="max-w-2xl w-full text-center">
|
|
12
|
+
{/* Animated 404 Icon */}
|
|
13
|
+
<div className="mb-8 relative">
|
|
14
|
+
<div className={`inline-flex items-center justify-center w-32 h-32 rounded-full ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'} border-2 shadow-xl mb-4`}>
|
|
15
|
+
<AlertCircle className={`w-16 h-16 ${isDark ? 'text-blue-400' : 'text-blue-500'}`} strokeWidth={1.5} />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
{/* 404 Large Text */}
|
|
20
|
+
<h1 className={`text-8xl md:text-9xl font-bold mb-6 ${isDark ? 'text-gray-100' : 'text-gray-900'}`}>
|
|
21
|
+
404
|
|
22
|
+
</h1>
|
|
23
|
+
|
|
24
|
+
{/* Error Message */}
|
|
25
|
+
<h2 className={`text-2xl md:text-3xl font-bold mb-4 ${isDark ? 'text-gray-200' : 'text-gray-800'}`}>
|
|
26
|
+
Page Not Found
|
|
27
|
+
</h2>
|
|
28
|
+
|
|
29
|
+
<p className={`text-lg mb-8 ${isDark ? 'text-gray-400' : 'text-gray-600'} max-w-md mx-auto`}>
|
|
30
|
+
The page you're looking for doesn't exist or has been moved.
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
{/* Divider */}
|
|
34
|
+
<div className={`w-24 h-1 mx-auto mb-8 rounded-full ${isDark ? 'bg-blue-600' : 'bg-blue-500'}`}></div>
|
|
35
|
+
|
|
36
|
+
{/* Navigation Buttons */}
|
|
37
|
+
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => navigate('/')}
|
|
40
|
+
className={`flex items-center justify-center gap-2 px-8 py-3 rounded-lg font-medium transition-all duration-200 w-full sm:w-auto cursor-pointer ${isDark
|
|
41
|
+
? 'bg-blue-600 hover:bg-blue-700 text-white'
|
|
42
|
+
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
|
43
|
+
} shadow-lg hover:shadow-xl transform hover:-translate-y-0.5`}
|
|
44
|
+
>
|
|
45
|
+
<Home className="w-5 h-5" />
|
|
46
|
+
<span>Go to Main Page</span>
|
|
47
|
+
</button>
|
|
48
|
+
|
|
49
|
+
<button
|
|
50
|
+
onClick={() => navigate('/profile')}
|
|
51
|
+
className={`flex items-center justify-center gap-2 px-8 py-3 rounded-lg font-medium transition-all duration-200 w-full sm:w-auto cursor-pointer ${isDark
|
|
52
|
+
? 'bg-gray-800 hover:bg-gray-700 text-gray-200 border border-gray-700'
|
|
53
|
+
: 'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300'
|
|
54
|
+
} shadow-lg hover:shadow-xl transform hover:-translate-y-0.5`}
|
|
55
|
+
>
|
|
56
|
+
<User className="w-5 h-5" />
|
|
57
|
+
<span>View Profile</span>
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Additional Help */}
|
|
62
|
+
<p className={`mt-12 text-sm ${isDark ? 'text-gray-500' : 'text-gray-500'}`}>
|
|
63
|
+
Need help? Contact our support team
|
|
64
|
+
</p>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export default NotFound;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { useTheme } from '../contexts/ThemeContext';
|
|
3
|
+
import { axiosSettingsClient } from '../config/axiosClient';
|
|
4
|
+
import SpinningLoader from '../components/Loader';
|
|
5
|
+
|
|
6
|
+
const Settings = () => {
|
|
7
|
+
const { isDark } = useTheme();
|
|
8
|
+
const [sections, setSections] = useState([]);
|
|
9
|
+
const [loading, setLoading] = useState(false);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
|
|
12
|
+
const fetchSettingsInfo = async () => {
|
|
13
|
+
setLoading(true);
|
|
14
|
+
setError(null);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const response = await axiosSettingsClient.get('getSettingsHealth');
|
|
18
|
+
setSections(response.data);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
setError(err.message);
|
|
21
|
+
} finally {
|
|
22
|
+
setLoading(false);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
fetchSettingsInfo();
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className={`min-h-screen py-14 px-8 transition-colors duration-300
|
|
32
|
+
${isDark ? "bg-[#0d0d0d] text-gray-200" : "bg-gray-100 text-gray-800"}`}>
|
|
33
|
+
|
|
34
|
+
<h1 className="text-4xl font-extrabold text-center mb-10 tracking-wide">
|
|
35
|
+
⚙️ Settings Overview
|
|
36
|
+
</h1>
|
|
37
|
+
|
|
38
|
+
{loading && <div className="flex justify-center"><SpinningLoader size={50} /></div>}
|
|
39
|
+
{error && <p className="text-center text-red-500">{error}</p>}
|
|
40
|
+
|
|
41
|
+
<div className="grid gap-6 max-w-5xl mx-auto">
|
|
42
|
+
{sections.map((section, index) => (
|
|
43
|
+
<div key={index}
|
|
44
|
+
className={`p-6 rounded-2xl shadow-xl border transition-all duration-300
|
|
45
|
+
${isDark
|
|
46
|
+
? "bg-[#111] border-gray-800 hover:border-gray-600"
|
|
47
|
+
: "bg-white border-gray-200 hover:border-gray-300"}`}>
|
|
48
|
+
|
|
49
|
+
<h2 className="text-2xl font-semibold flex items-center gap-2 mb-4">
|
|
50
|
+
<span className="text-3xl">{section.icon}</span> {section.title}
|
|
51
|
+
</h2>
|
|
52
|
+
|
|
53
|
+
<ul className="space-y-2">
|
|
54
|
+
{section.items.map((item, i) => (
|
|
55
|
+
<li key={i} className="flex items-start gap-3">
|
|
56
|
+
<span className="text-blue-500 mt-1">•</span>
|
|
57
|
+
<span className="opacity-90">{item}</span>
|
|
58
|
+
</li>
|
|
59
|
+
))}
|
|
60
|
+
</ul>
|
|
61
|
+
|
|
62
|
+
</div>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default Settings;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
export default {
|
|
3
|
+
content: [
|
|
4
|
+
"./index.html",
|
|
5
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
6
|
+
],
|
|
7
|
+
theme: {
|
|
8
|
+
extend: {
|
|
9
|
+
animation: {
|
|
10
|
+
'fade-in': 'fadeIn 0.5s ease-in-out',
|
|
11
|
+
'slide-in': 'slideIn 0.3s ease-out',
|
|
12
|
+
'bounce-subtle': 'bounceSubtle 0.6s ease-in-out',
|
|
13
|
+
},
|
|
14
|
+
keyframes: {
|
|
15
|
+
fadeIn: {
|
|
16
|
+
'0%': { opacity: '0' },
|
|
17
|
+
'100%': { opacity: '1' },
|
|
18
|
+
},
|
|
19
|
+
slideIn: {
|
|
20
|
+
'0%': { transform: 'translateX(-100%)' },
|
|
21
|
+
'100%': { transform: 'translateX(0)' },
|
|
22
|
+
},
|
|
23
|
+
bounceSubtle: {
|
|
24
|
+
'0%, 100%': { transform: 'translateY(0)' },
|
|
25
|
+
'50%': { transform: 'translateY(-5px)' },
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
plugins: [
|
|
31
|
+
require('daisyui'),
|
|
32
|
+
],
|
|
33
|
+
daisyui: {
|
|
34
|
+
themes: [
|
|
35
|
+
{
|
|
36
|
+
light: {
|
|
37
|
+
"primary": "#3b82f6",
|
|
38
|
+
"primary-content": "#ffffff",
|
|
39
|
+
"secondary": "#6366f1",
|
|
40
|
+
"secondary-content": "#ffffff",
|
|
41
|
+
"accent": "#06b6d4",
|
|
42
|
+
"accent-content": "#ffffff",
|
|
43
|
+
"neutral": "#374151",
|
|
44
|
+
"neutral-content": "#ffffff",
|
|
45
|
+
"base-100": "#ffffff",
|
|
46
|
+
"base-200": "#f8fafc",
|
|
47
|
+
"base-300": "#e2e8f0",
|
|
48
|
+
"base-content": "#1f2937",
|
|
49
|
+
"info": "#0ea5e9",
|
|
50
|
+
"info-content": "#ffffff",
|
|
51
|
+
"success": "#10b981",
|
|
52
|
+
"success-content": "#ffffff",
|
|
53
|
+
"warning": "#f59e0b",
|
|
54
|
+
"warning-content": "#ffffff",
|
|
55
|
+
"error": "#ef4444",
|
|
56
|
+
"error-content": "#ffffff",
|
|
57
|
+
},
|
|
58
|
+
dark: {
|
|
59
|
+
"primary": "#3b82f6",
|
|
60
|
+
"primary-content": "#ffffff",
|
|
61
|
+
"secondary": "#6366f1",
|
|
62
|
+
"secondary-content": "#ffffff",
|
|
63
|
+
"accent": "#06b6d4",
|
|
64
|
+
"accent-content": "#ffffff",
|
|
65
|
+
"neutral": "#1f2937",
|
|
66
|
+
"neutral-content": "#ffffff",
|
|
67
|
+
"base-100": "#0f172a",
|
|
68
|
+
"base-200": "#1e293b",
|
|
69
|
+
"base-300": "#334155",
|
|
70
|
+
"base-content": "#f1f5f9",
|
|
71
|
+
"info": "#0ea5e9",
|
|
72
|
+
"info-content": "#ffffff",
|
|
73
|
+
"success": "#10b981",
|
|
74
|
+
"success-content": "#ffffff",
|
|
75
|
+
"warning": "#f59e0b",
|
|
76
|
+
"warning-content": "#ffffff",
|
|
77
|
+
"error": "#ef4444",
|
|
78
|
+
"error-content": "#ffffff",
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
darkTheme: "dark",
|
|
83
|
+
base: true,
|
|
84
|
+
styled: true,
|
|
85
|
+
utils: true,
|
|
86
|
+
prefix: "",
|
|
87
|
+
logs: true,
|
|
88
|
+
themeRoot: ":root",
|
|
89
|
+
},
|
|
90
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react-swc'
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
|
|
6
|
+
// https://vite.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [tailwindcss(), react()],
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
'@': path.resolve(__dirname, './src'),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
css: {
|
|
15
|
+
preprocessorOptions: {
|
|
16
|
+
scss: {
|
|
17
|
+
additionalData: `@import "@/styles/variables.scss";`
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
server: {
|
|
22
|
+
port: 5173,
|
|
23
|
+
open: false
|
|
24
|
+
},
|
|
25
|
+
build: {
|
|
26
|
+
outDir: 'dist',
|
|
27
|
+
sourcemap: true,
|
|
28
|
+
rollupOptions: {
|
|
29
|
+
output: {
|
|
30
|
+
manualChunks: {
|
|
31
|
+
vendor: ['react', 'react-dom'],
|
|
32
|
+
aws: ['aws-amplify'],
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
File without changes
|