create-workerstack 0.1.1 → 0.1.3

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.
@@ -0,0 +1,80 @@
1
+ import { Typography, Box, Button } from '@mui/material';
2
+ import { Link } from 'react-router';
3
+
4
+ export default function NotFoundPage() {
5
+ return (
6
+ <Box
7
+ sx={{
8
+ minHeight: '100vh',
9
+ display: 'flex',
10
+ flexDirection: 'column',
11
+ alignItems: 'center',
12
+ justifyContent: 'center',
13
+ position: 'relative',
14
+ overflow: 'hidden',
15
+ }}
16
+ >
17
+ {/* Background glow */}
18
+ <Box
19
+ sx={{
20
+ position: 'absolute',
21
+ top: '50%',
22
+ left: '50%',
23
+ transform: 'translate(-50%, -50%)',
24
+ width: '400px',
25
+ height: '400px',
26
+ borderRadius: '50%',
27
+ background: 'radial-gradient(circle, rgba(124,58,237,0.1) 0%, transparent 70%)',
28
+ filter: 'blur(60px)',
29
+ pointerEvents: 'none',
30
+ }}
31
+ />
32
+
33
+ <Box sx={{ textAlign: 'center', position: 'relative', animation: 'fadeInUp 0.6s ease-out' }}>
34
+ <Typography
35
+ variant="h1"
36
+ sx={{
37
+ fontSize: { xs: '6rem', md: '10rem' },
38
+ fontWeight: 800,
39
+ fontFamily: "'Sora', sans-serif",
40
+ background: 'linear-gradient(135deg, #7C3AED 0%, #A78BFA 50%, #C4B5FD 100%)',
41
+ backgroundClip: 'text',
42
+ WebkitBackgroundClip: 'text',
43
+ WebkitTextFillColor: 'transparent',
44
+ lineHeight: 1,
45
+ }}
46
+ >
47
+ 404
48
+ </Typography>
49
+
50
+ <Typography
51
+ variant="h5"
52
+ sx={{
53
+ color: 'text.secondary',
54
+ fontWeight: 400,
55
+ mt: 2,
56
+ mb: 4,
57
+ }}
58
+ >
59
+ Page not found
60
+ </Typography>
61
+
62
+ <Button
63
+ component={Link}
64
+ to="/"
65
+ variant="contained"
66
+ size="large"
67
+ sx={{
68
+ bgcolor: 'primary.main',
69
+ fontWeight: 600,
70
+ px: 4,
71
+ py: 1.5,
72
+ '&:hover': { bgcolor: '#6D28D9' },
73
+ }}
74
+ >
75
+ Go Home
76
+ </Button>
77
+ </Box>
78
+ </Box>
79
+ );
80
+ }
@@ -0,0 +1,123 @@
1
+ import { useState, type FormEvent } from 'react';
2
+ import { TextField, Button, Alert, Typography, Box, CircularProgress } from '@mui/material';
3
+ import { Link, useNavigate, Navigate } from 'react-router';
4
+ import { signUp, useSession } from '@/client/lib/auth-client';
5
+ import { AuthLayout } from '../components/AuthLayout';
6
+
7
+ export default function RegisterPage() {
8
+ const { data: session, isPending: sessionLoading } = useSession();
9
+ const navigate = useNavigate();
10
+
11
+ const [name, setName] = useState('');
12
+ const [email, setEmail] = useState('');
13
+ const [password, setPassword] = useState('');
14
+ const [confirmPassword, setConfirmPassword] = useState('');
15
+ const [error, setError] = useState('');
16
+ const [loading, setLoading] = useState(false);
17
+
18
+ if (sessionLoading) return null;
19
+ if (session) return <Navigate to="/dashboard" replace />;
20
+
21
+ const handleSubmit = async (e: FormEvent) => {
22
+ e.preventDefault();
23
+ setError('');
24
+
25
+ if (password !== confirmPassword) {
26
+ setError('Passwords do not match');
27
+ return;
28
+ }
29
+
30
+ setLoading(true);
31
+
32
+ await signUp.email({
33
+ name,
34
+ email,
35
+ password,
36
+ }, {
37
+ onSuccess: () => navigate('/dashboard', { replace: true }),
38
+ onError: (ctx) => {
39
+ setError(ctx.error.message);
40
+ setLoading(false);
41
+ },
42
+ });
43
+ };
44
+
45
+ return (
46
+ <AuthLayout
47
+ title="Create an account"
48
+ subtitle="Get started with WorkerStack"
49
+ footer={
50
+ <Typography variant="body2" sx={{ color: 'text.secondary' }}>
51
+ Already have an account?{' '}
52
+ <Typography
53
+ component={Link}
54
+ to="/login"
55
+ variant="body2"
56
+ sx={{ color: 'primary.main', textDecoration: 'none', fontWeight: 600, '&:hover': { textDecoration: 'underline' } }}
57
+ >
58
+ Login
59
+ </Typography>
60
+ </Typography>
61
+ }
62
+ >
63
+ <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
64
+ {error && <Alert severity="error">{error}</Alert>}
65
+
66
+ <TextField
67
+ label="Name"
68
+ value={name}
69
+ onChange={(e) => setName(e.target.value)}
70
+ required
71
+ fullWidth
72
+ autoComplete="name"
73
+ autoFocus
74
+ />
75
+
76
+ <TextField
77
+ label="Email"
78
+ type="email"
79
+ value={email}
80
+ onChange={(e) => setEmail(e.target.value)}
81
+ required
82
+ fullWidth
83
+ autoComplete="email"
84
+ />
85
+
86
+ <TextField
87
+ label="Password"
88
+ type="password"
89
+ value={password}
90
+ onChange={(e) => setPassword(e.target.value)}
91
+ required
92
+ fullWidth
93
+ autoComplete="new-password"
94
+ />
95
+
96
+ <TextField
97
+ label="Confirm Password"
98
+ type="password"
99
+ value={confirmPassword}
100
+ onChange={(e) => setConfirmPassword(e.target.value)}
101
+ required
102
+ fullWidth
103
+ autoComplete="new-password"
104
+ />
105
+
106
+ <Button
107
+ type="submit"
108
+ variant="contained"
109
+ fullWidth
110
+ disabled={loading}
111
+ sx={{
112
+ py: 1.5,
113
+ bgcolor: 'primary.main',
114
+ fontWeight: 600,
115
+ '&:hover': { bgcolor: '#6D28D9' },
116
+ }}
117
+ >
118
+ {loading ? <CircularProgress size={24} color="inherit" /> : 'Create Account'}
119
+ </Button>
120
+ </Box>
121
+ </AuthLayout>
122
+ );
123
+ }
@@ -0,0 +1,143 @@
1
+ import { useState, type FormEvent } from 'react';
2
+ import { TextField, Button, Alert, Typography, Box, CircularProgress } from '@mui/material';
3
+ import { Link, useSearchParams } from 'react-router';
4
+ import { authClient } from '@/client/lib/auth-client';
5
+ import { AuthLayout } from '../components/AuthLayout';
6
+
7
+ export default function ResetPasswordPage() {
8
+ const [searchParams] = useSearchParams();
9
+ const token = searchParams.get('token');
10
+
11
+ const [password, setPassword] = useState('');
12
+ const [confirmPassword, setConfirmPassword] = useState('');
13
+ const [error, setError] = useState('');
14
+ const [success, setSuccess] = useState(false);
15
+ const [loading, setLoading] = useState(false);
16
+
17
+ if (!token) {
18
+ return (
19
+ <AuthLayout
20
+ title="Invalid link"
21
+ subtitle="This password reset link is invalid or has expired"
22
+ footer={
23
+ <Typography variant="body2" sx={{ color: 'text.secondary' }}>
24
+ <Typography
25
+ component={Link}
26
+ to="/forgot-password"
27
+ variant="body2"
28
+ sx={{ color: 'primary.main', textDecoration: 'none', fontWeight: 600, '&:hover': { textDecoration: 'underline' } }}
29
+ >
30
+ Request a new reset link
31
+ </Typography>
32
+ </Typography>
33
+ }
34
+ >
35
+ <Alert severity="error">No reset token found in the URL.</Alert>
36
+ </AuthLayout>
37
+ );
38
+ }
39
+
40
+ const handleSubmit = async (e: FormEvent) => {
41
+ e.preventDefault();
42
+ setError('');
43
+
44
+ if (password !== confirmPassword) {
45
+ setError('Passwords do not match');
46
+ return;
47
+ }
48
+
49
+ setLoading(true);
50
+
51
+ const { error } = await authClient.resetPassword({
52
+ newPassword: password,
53
+ token,
54
+ });
55
+
56
+ setLoading(false);
57
+
58
+ if (error) {
59
+ setError(error.message);
60
+ } else {
61
+ setSuccess(true);
62
+ }
63
+ };
64
+
65
+ return (
66
+ <AuthLayout
67
+ title="Set a new password"
68
+ subtitle="Enter your new password below"
69
+ footer={
70
+ <Typography variant="body2" sx={{ color: 'text.secondary' }}>
71
+ <Typography
72
+ component={Link}
73
+ to="/login"
74
+ variant="body2"
75
+ sx={{ color: 'primary.main', textDecoration: 'none', fontWeight: 600, '&:hover': { textDecoration: 'underline' } }}
76
+ >
77
+ Back to login
78
+ </Typography>
79
+ </Typography>
80
+ }
81
+ >
82
+ {success ? (
83
+ <Box sx={{ textAlign: 'center' }}>
84
+ <Alert severity="success" sx={{ mb: 2 }}>
85
+ Your password has been reset successfully.
86
+ </Alert>
87
+ <Button
88
+ component={Link}
89
+ to="/login"
90
+ variant="contained"
91
+ sx={{
92
+ bgcolor: 'primary.main',
93
+ fontWeight: 600,
94
+ '&:hover': { bgcolor: '#6D28D9' },
95
+ }}
96
+ >
97
+ Go to Login
98
+ </Button>
99
+ </Box>
100
+ ) : (
101
+ <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
102
+ {error && <Alert severity="error">{error}</Alert>}
103
+
104
+ <TextField
105
+ label="New Password"
106
+ type="password"
107
+ value={password}
108
+ onChange={(e) => setPassword(e.target.value)}
109
+ required
110
+ fullWidth
111
+ autoComplete="new-password"
112
+ autoFocus
113
+ />
114
+
115
+ <TextField
116
+ label="Confirm New Password"
117
+ type="password"
118
+ value={confirmPassword}
119
+ onChange={(e) => setConfirmPassword(e.target.value)}
120
+ required
121
+ fullWidth
122
+ autoComplete="new-password"
123
+ />
124
+
125
+ <Button
126
+ type="submit"
127
+ variant="contained"
128
+ fullWidth
129
+ disabled={loading}
130
+ sx={{
131
+ py: 1.5,
132
+ bgcolor: 'primary.main',
133
+ fontWeight: 600,
134
+ '&:hover': { bgcolor: '#6D28D9' },
135
+ }}
136
+ >
137
+ {loading ? <CircularProgress size={24} color="inherit" /> : 'Reset Password'}
138
+ </Button>
139
+ </Box>
140
+ )}
141
+ </AuthLayout>
142
+ );
143
+ }
@@ -0,0 +1,30 @@
1
+ import { createTheme } from '@mui/material/styles';
2
+
3
+ const theme = createTheme({
4
+ cssVariables: { colorSchemeSelector: 'class' },
5
+ defaultColorScheme: 'dark',
6
+ colorSchemes: {
7
+ light: {
8
+ palette: {
9
+ primary: { main: '#7C3AED' },
10
+ background: { default: '#F8F9FA', paper: '#FFFFFF' },
11
+ text: { primary: '#1A1A2E', secondary: '#64748B' },
12
+ },
13
+ },
14
+ dark: {
15
+ palette: {
16
+ primary: { main: '#7C3AED' },
17
+ background: { default: '#0A0A0F', paper: '#12121A' },
18
+ text: { primary: '#F1F5F9', secondary: '#94A3B8' },
19
+ },
20
+ },
21
+ },
22
+ typography: {
23
+ fontFamily: "'Plus Jakarta Sans', sans-serif",
24
+ h1: { fontFamily: "'Sora', sans-serif" },
25
+ h2: { fontFamily: "'Sora', sans-serif" },
26
+ },
27
+ shape: { borderRadius: 12 },
28
+ });
29
+
30
+ export default theme;
@@ -11,8 +11,6 @@ html {
11
11
  }
12
12
 
13
13
  body {
14
- background-color: #0A0A0F;
15
- color: #F1F5F9;
16
14
  -webkit-font-smoothing: antialiased;
17
15
  -moz-osx-font-smoothing: grayscale;
18
16
  overflow-x: hidden;