jac-client 0.2.4__py3-none-any.whl → 0.2.5__py3-none-any.whl
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.
- jac_client/examples/all-in-one/src/app.jac +841 -0
- jac_client/examples/all-in-one/src/button.jac +7 -0
- jac_client/examples/all-in-one/src/components/button.jac +7 -0
- jac_client/examples/asset-serving/css-with-image/src/app.jac +88 -0
- jac_client/examples/asset-serving/image-asset/src/app.jac +55 -0
- jac_client/examples/asset-serving/import-alias/src/app.jac +111 -0
- jac_client/examples/basic/src/app.jac +21 -0
- jac_client/examples/basic-auth/src/app.jac +377 -0
- jac_client/examples/basic-auth-with-router/src/app.jac +464 -0
- jac_client/examples/basic-full-stack/src/app.jac +365 -0
- jac_client/examples/css-styling/js-styling/src/app.jac +84 -0
- jac_client/examples/css-styling/material-ui/src/app.jac +122 -0
- jac_client/examples/css-styling/pure-css/src/app.jac +64 -0
- jac_client/examples/css-styling/sass-example/src/app.jac +64 -0
- jac_client/examples/css-styling/styled-components/src/app.jac +71 -0
- jac_client/examples/css-styling/tailwind-example/src/app.jac +63 -0
- jac_client/examples/full-stack-with-auth/src/app.jac +722 -0
- jac_client/examples/little-x/src/app.jac +719 -0
- jac_client/examples/little-x/src/submit-button.jac +16 -0
- jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +11 -0
- jac_client/examples/nested-folders/nested-advance/src/app.jac +35 -0
- jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +19 -0
- jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +43 -0
- jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +25 -0
- jac_client/examples/nested-folders/nested-basic/src/app.jac +13 -0
- jac_client/examples/nested-folders/nested-basic/src/button.jac +7 -0
- jac_client/examples/nested-folders/nested-basic/src/components/button.jac +7 -0
- jac_client/examples/ts-support/src/app.jac +35 -0
- jac_client/examples/with-router/src/app.jac +323 -0
- jac_client/plugin/cli.jac +547 -0
- jac_client/plugin/client.jac +52 -0
- jac_client/plugin/client_runtime.cl.jac +38 -0
- jac_client/plugin/impl/client.impl.jac +134 -0
- jac_client/plugin/impl/client_runtime.impl.jac +177 -0
- jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
- jac_client/plugin/plugin_config.jac +195 -0
- jac_client/plugin/src/__init__.jac +20 -0
- jac_client/plugin/src/asset_processor.jac +33 -0
- jac_client/plugin/src/babel_processor.jac +18 -0
- jac_client/plugin/src/compiler.jac +66 -0
- jac_client/plugin/src/config_loader.jac +32 -0
- jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
- jac_client/plugin/src/impl/babel_processor.impl.jac +84 -0
- jac_client/plugin/src/impl/compiler.impl.jac +251 -0
- jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
- jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
- jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
- jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
- jac_client/plugin/src/impl/vite_bundler.impl.jac +513 -0
- jac_client/plugin/src/import_processor.jac +19 -0
- jac_client/plugin/src/jac_to_js.jac +35 -0
- jac_client/plugin/src/package_installer.jac +26 -0
- jac_client/plugin/src/vite_bundler.jac +36 -0
- jac_client/plugin/vite_client_bundle.jac +31 -0
- jac_client/tests/fixtures/basic-app/app.jac +23 -0
- jac_client/tests/fixtures/cl_file/app.cl.jac +48 -0
- jac_client/tests/fixtures/cl_file/app.jac +15 -0
- jac_client/tests/fixtures/client_app_with_antd/app.jac +34 -0
- jac_client/tests/fixtures/js_import/app.jac +34 -0
- jac_client/tests/fixtures/relative_import/app.jac +11 -0
- jac_client/tests/fixtures/relative_import/button.jac +7 -0
- jac_client/tests/fixtures/spawn_test/app.jac +129 -0
- jac_client/tests/fixtures/test_fragments_spread/app.jac +67 -0
- jac_client/tests/fixtures/with-ts/app.jac +35 -0
- {jac_client-0.2.4.dist-info → jac_client-0.2.5.dist-info}/METADATA +2 -2
- jac_client-0.2.5.dist-info/RECORD +74 -0
- jac_client-0.2.4.dist-info/RECORD +0 -10
- {jac_client-0.2.4.dist-info → jac_client-0.2.5.dist-info}/WHEEL +0 -0
- {jac_client-0.2.4.dist-info → jac_client-0.2.5.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.4.dist-info → jac_client-0.2.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import from react { useState, useEffect }
|
|
3
|
+
cl import ".styles.css";
|
|
4
|
+
|
|
5
|
+
cl {
|
|
6
|
+
def:pub app -> any {
|
|
7
|
+
[count, setCount] = useState(0);
|
|
8
|
+
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
9
|
+
return <div
|
|
10
|
+
style={{
|
|
11
|
+
padding: "20px",
|
|
12
|
+
textAlign: "center",
|
|
13
|
+
fontFamily: "Arial, sans-serif"
|
|
14
|
+
}}
|
|
15
|
+
>
|
|
16
|
+
<h1>
|
|
17
|
+
🍔 Burger Counter App
|
|
18
|
+
</h1>
|
|
19
|
+
<img
|
|
20
|
+
src="/static/assets/burger.png"
|
|
21
|
+
alt="Delicious Burger"
|
|
22
|
+
style={{
|
|
23
|
+
width: "200px",
|
|
24
|
+
height: "auto",
|
|
25
|
+
margin: "20px 0",
|
|
26
|
+
borderRadius: "10px",
|
|
27
|
+
boxShadow: "0 4px 8px rgba(0,0,0,0.2)"
|
|
28
|
+
}}
|
|
29
|
+
/>
|
|
30
|
+
<p
|
|
31
|
+
style={{fontSize: "18px", margin: "20px 0"}}
|
|
32
|
+
>
|
|
33
|
+
You've clicked the burger
|
|
34
|
+
<strong>
|
|
35
|
+
{count}
|
|
36
|
+
</strong>
|
|
37
|
+
times!
|
|
38
|
+
</p>
|
|
39
|
+
<button
|
|
40
|
+
onClick={lambda e: any -> None{ setCount(count + 1);} }
|
|
41
|
+
style={{
|
|
42
|
+
padding: "10px 20px",
|
|
43
|
+
fontSize: "16px",
|
|
44
|
+
backgroundColor: "#ff6b35",
|
|
45
|
+
color: "white",
|
|
46
|
+
border: "none",
|
|
47
|
+
borderRadius: "5px",
|
|
48
|
+
cursor: "pointer",
|
|
49
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
Click the Burger! 🍔
|
|
53
|
+
</button>
|
|
54
|
+
<h2
|
|
55
|
+
style={{marginTop: "40px", marginBottom: "20px"}}
|
|
56
|
+
>
|
|
57
|
+
CSS Asset Examples
|
|
58
|
+
</h2>
|
|
59
|
+
<div className="container">
|
|
60
|
+
<h3
|
|
61
|
+
style={{color: "white", textShadow: "2px 2px 4px rgba(0,0,0,0.5)"}}
|
|
62
|
+
>
|
|
63
|
+
Background Image Example
|
|
64
|
+
</h3>
|
|
65
|
+
<p
|
|
66
|
+
style={{color: "white", textShadow: "2px 2px 4px rgba(0,0,0,0.5)"}}
|
|
67
|
+
>
|
|
68
|
+
This container uses the burger image as a background via CSS
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="card">
|
|
72
|
+
<h3>
|
|
73
|
+
Image in Card
|
|
74
|
+
</h3>
|
|
75
|
+
<img
|
|
76
|
+
src="/static/assets/burger.png"
|
|
77
|
+
alt="Burger in Card"
|
|
78
|
+
className="burgerImage"
|
|
79
|
+
/>
|
|
80
|
+
<p
|
|
81
|
+
style={{marginTop: "15px", color: "#666"}}
|
|
82
|
+
>
|
|
83
|
+
This image is displayed within a styled card using CSS classes
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
</div>;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import from react { useState, useEffect }
|
|
3
|
+
|
|
4
|
+
cl {
|
|
5
|
+
def:pub app -> any {
|
|
6
|
+
[count, setCount] = useState(0);
|
|
7
|
+
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
8
|
+
return <div
|
|
9
|
+
style={{
|
|
10
|
+
padding: "20px",
|
|
11
|
+
textAlign: "center",
|
|
12
|
+
fontFamily: "Arial, sans-serif"
|
|
13
|
+
}}
|
|
14
|
+
>
|
|
15
|
+
<h1>
|
|
16
|
+
🍔 Burger Counter App
|
|
17
|
+
</h1>
|
|
18
|
+
<img
|
|
19
|
+
src="/static/assets/burger.png"
|
|
20
|
+
alt="Delicious Burger"
|
|
21
|
+
style={{
|
|
22
|
+
width: "200px",
|
|
23
|
+
height: "auto",
|
|
24
|
+
margin: "20px 0",
|
|
25
|
+
borderRadius: "10px",
|
|
26
|
+
boxShadow: "0 4px 8px rgba(0,0,0,0.2)"
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
<p
|
|
30
|
+
style={{fontSize: "18px", margin: "20px 0"}}
|
|
31
|
+
>
|
|
32
|
+
You've clicked the burger
|
|
33
|
+
<strong>
|
|
34
|
+
{count}
|
|
35
|
+
</strong>
|
|
36
|
+
times!
|
|
37
|
+
</p>
|
|
38
|
+
<button
|
|
39
|
+
onClick={lambda e: any -> None{ setCount(count + 1);} }
|
|
40
|
+
style={{
|
|
41
|
+
padding: "10px 20px",
|
|
42
|
+
fontSize: "16px",
|
|
43
|
+
backgroundColor: "#ff6b35",
|
|
44
|
+
color: "white",
|
|
45
|
+
border: "none",
|
|
46
|
+
borderRadius: "5px",
|
|
47
|
+
cursor: "pointer",
|
|
48
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
Click the Burger! 🍔
|
|
52
|
+
</button>
|
|
53
|
+
</div>;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import from react { useState, useEffect }
|
|
3
|
+
# Import image using the @jac-client/assets alias
|
|
4
|
+
cl import from "@jac-client/assets/burger.png" {
|
|
5
|
+
default as burgerImage
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
cl {
|
|
9
|
+
def:pub app -> any {
|
|
10
|
+
[count, setCount] = useState(0);
|
|
11
|
+
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
12
|
+
return <div
|
|
13
|
+
style={{
|
|
14
|
+
padding: "20px",
|
|
15
|
+
textAlign: "center",
|
|
16
|
+
fontFamily: "Arial, sans-serif"
|
|
17
|
+
}}
|
|
18
|
+
>
|
|
19
|
+
<h1>
|
|
20
|
+
🍔 Import Alias Example
|
|
21
|
+
</h1>
|
|
22
|
+
<p
|
|
23
|
+
style={{color: "#666", marginBottom: "20px"}}
|
|
24
|
+
>
|
|
25
|
+
Using
|
|
26
|
+
<code>
|
|
27
|
+
@jac-client/assets
|
|
28
|
+
</code>
|
|
29
|
+
alias to import assets
|
|
30
|
+
</p>
|
|
31
|
+
<img
|
|
32
|
+
src={burgerImage}
|
|
33
|
+
alt="Delicious Burger"
|
|
34
|
+
style={{
|
|
35
|
+
width: "200px",
|
|
36
|
+
height: "auto",
|
|
37
|
+
margin: "20px 0",
|
|
38
|
+
borderRadius: "10px",
|
|
39
|
+
boxShadow: "0 4px 8px rgba(0,0,0,0.2)"
|
|
40
|
+
}}
|
|
41
|
+
/>
|
|
42
|
+
<p
|
|
43
|
+
style={{fontSize: "18px", margin: "20px 0"}}
|
|
44
|
+
>
|
|
45
|
+
You've clicked the burger
|
|
46
|
+
<strong>
|
|
47
|
+
{count}
|
|
48
|
+
</strong>
|
|
49
|
+
times!
|
|
50
|
+
</p>
|
|
51
|
+
<button
|
|
52
|
+
onClick={lambda e: any -> None{ setCount(count + 1);} }
|
|
53
|
+
style={{
|
|
54
|
+
padding: "10px 20px",
|
|
55
|
+
fontSize: "16px",
|
|
56
|
+
backgroundColor: "#ff6b35",
|
|
57
|
+
color: "white",
|
|
58
|
+
border: "none",
|
|
59
|
+
borderRadius: "5px",
|
|
60
|
+
cursor: "pointer",
|
|
61
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.2)"
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
Click the Burger! 🍔
|
|
65
|
+
</button>
|
|
66
|
+
<div
|
|
67
|
+
style={{
|
|
68
|
+
marginTop: "30px",
|
|
69
|
+
padding: "15px",
|
|
70
|
+
backgroundColor: "#f5f5f5",
|
|
71
|
+
borderRadius: "5px",
|
|
72
|
+
fontSize: "12px",
|
|
73
|
+
textAlign: "left",
|
|
74
|
+
maxWidth: "600px",
|
|
75
|
+
margin: "30px auto"
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<strong>
|
|
79
|
+
How it works:
|
|
80
|
+
</strong>
|
|
81
|
+
<ul
|
|
82
|
+
style={{marginTop: "10px", paddingLeft: "20px"}}
|
|
83
|
+
>
|
|
84
|
+
<li>
|
|
85
|
+
Import using:
|
|
86
|
+
<code>
|
|
87
|
+
cl import from '@jac-client/assets/burger.png'
|
|
88
|
+
</code>
|
|
89
|
+
</li>
|
|
90
|
+
<li>
|
|
91
|
+
Vite processes the import and generates optimized URLs
|
|
92
|
+
</li>
|
|
93
|
+
<li>
|
|
94
|
+
Assets are automatically copied from
|
|
95
|
+
<code>
|
|
96
|
+
assets/
|
|
97
|
+
</code>
|
|
98
|
+
to
|
|
99
|
+
<code>
|
|
100
|
+
compiled/assets/
|
|
101
|
+
</code>
|
|
102
|
+
during build
|
|
103
|
+
</li>
|
|
104
|
+
<li>
|
|
105
|
+
Automatic hash generation for cache busting
|
|
106
|
+
</li>
|
|
107
|
+
</ul>
|
|
108
|
+
</div>
|
|
109
|
+
</div>;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Pages
|
|
2
|
+
cl import from react { useState, useEffect }
|
|
3
|
+
cl {
|
|
4
|
+
def:pub app -> any {
|
|
5
|
+
[count, setCount] = useState(0);
|
|
6
|
+
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
7
|
+
return <div>
|
|
8
|
+
<h1>
|
|
9
|
+
Hello, World!
|
|
10
|
+
</h1>
|
|
11
|
+
<p>
|
|
12
|
+
Count: {count}
|
|
13
|
+
</p>
|
|
14
|
+
<button
|
|
15
|
+
onClick={lambda e: any -> None{ setCount(count + 1);} }
|
|
16
|
+
>
|
|
17
|
+
Increment
|
|
18
|
+
</button>
|
|
19
|
+
</div>;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# Simple Authentication App with Jac Client Utils
|
|
2
|
+
cl import from react {
|
|
3
|
+
useState,
|
|
4
|
+
useEffect
|
|
5
|
+
}
|
|
6
|
+
cl import from '@jac-client/utils' { jacSignup, jacLogin, jacLogout, jacIsLoggedIn }
|
|
7
|
+
|
|
8
|
+
cl {
|
|
9
|
+
def:pub app -> any {
|
|
10
|
+
[isLoggedIn, setIsLoggedIn] = useState(False);
|
|
11
|
+
[isSignup, setIsSignup] = useState(False);
|
|
12
|
+
[username, setUsername] = useState("");
|
|
13
|
+
[password, setPassword] = useState("");
|
|
14
|
+
[error, setError] = useState("");
|
|
15
|
+
[loading, setLoading] = useState(False);
|
|
16
|
+
|
|
17
|
+
# Check login status on mount
|
|
18
|
+
useEffect(lambda -> None{ setIsLoggedIn(jacIsLoggedIn());} , []);
|
|
19
|
+
|
|
20
|
+
# Handle login
|
|
21
|
+
async def handleLogin -> None {
|
|
22
|
+
setError("");
|
|
23
|
+
if not username.trim() or not password {
|
|
24
|
+
setError("Please fill in all fields");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
setLoading(True);
|
|
28
|
+
success = await jacLogin(username, password);
|
|
29
|
+
setLoading(False);
|
|
30
|
+
if success {
|
|
31
|
+
setIsLoggedIn(True);
|
|
32
|
+
setUsername("");
|
|
33
|
+
setPassword("");
|
|
34
|
+
} else {
|
|
35
|
+
setError("Invalid username or password");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Handle signup
|
|
40
|
+
async def handleSignup -> None {
|
|
41
|
+
setError("");
|
|
42
|
+
if not username.trim() or not password {
|
|
43
|
+
setError("Please fill in all fields");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if password.length < 6 {
|
|
47
|
+
setError("Password must be at least 6 characters");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
setLoading(True);
|
|
51
|
+
result = await jacSignup(username, password);
|
|
52
|
+
setLoading(False);
|
|
53
|
+
if result["success"] {
|
|
54
|
+
setIsLoggedIn(True);
|
|
55
|
+
setUsername("");
|
|
56
|
+
setPassword("");
|
|
57
|
+
} else {
|
|
58
|
+
setError(result["error"] if result["error"] else "Signup failed");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Handle logout
|
|
63
|
+
def handleLogout -> None {
|
|
64
|
+
jacLogout();
|
|
65
|
+
setIsLoggedIn(False);
|
|
66
|
+
setUsername("");
|
|
67
|
+
setPassword("");
|
|
68
|
+
setError("");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Handle form submit
|
|
72
|
+
async def handleSubmit(e: any) -> None {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
if isSignup {
|
|
75
|
+
await handleSignup();
|
|
76
|
+
} else {
|
|
77
|
+
await handleLogin();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Logged in view - Dashboard
|
|
82
|
+
if isLoggedIn {
|
|
83
|
+
return <div
|
|
84
|
+
style={{
|
|
85
|
+
"minHeight": "100vh",
|
|
86
|
+
"background": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
87
|
+
"display": "flex",
|
|
88
|
+
"alignItems": "center",
|
|
89
|
+
"justifyContent": "center",
|
|
90
|
+
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<div
|
|
94
|
+
style={{
|
|
95
|
+
"background": "#ffffff",
|
|
96
|
+
"borderRadius": "16px",
|
|
97
|
+
"padding": "48px",
|
|
98
|
+
"boxShadow": "0 20px 60px rgba(0,0,0,0.3)",
|
|
99
|
+
"maxWidth": "500px",
|
|
100
|
+
"width": "90%",
|
|
101
|
+
"textAlign": "center"
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
<h1
|
|
105
|
+
style={{
|
|
106
|
+
"fontSize": "2.5rem",
|
|
107
|
+
"marginBottom": "16px",
|
|
108
|
+
"color": "#1f2937",
|
|
109
|
+
"fontWeight": "700"
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
✅ Welcome!
|
|
113
|
+
</h1>
|
|
114
|
+
<p
|
|
115
|
+
style={{
|
|
116
|
+
"fontSize": "1.1rem",
|
|
117
|
+
"color": "#6b7280",
|
|
118
|
+
"marginBottom": "32px"
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
You are successfully logged in
|
|
122
|
+
</p>
|
|
123
|
+
<div
|
|
124
|
+
style={{
|
|
125
|
+
"background": "#f3f4f6",
|
|
126
|
+
"padding": "24px",
|
|
127
|
+
"borderRadius": "12px",
|
|
128
|
+
"marginBottom": "32px"
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<p
|
|
132
|
+
style={{
|
|
133
|
+
"fontSize": "0.9rem",
|
|
134
|
+
"color": "#6b7280",
|
|
135
|
+
"marginBottom": "8px"
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
Logged in as:
|
|
139
|
+
</p>
|
|
140
|
+
<p
|
|
141
|
+
style={{
|
|
142
|
+
"fontSize": "1.2rem",
|
|
143
|
+
"fontWeight": "600",
|
|
144
|
+
"color": "#667eea"
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
User
|
|
148
|
+
</p>
|
|
149
|
+
</div>
|
|
150
|
+
<button
|
|
151
|
+
onClick={handleLogout}
|
|
152
|
+
style={{
|
|
153
|
+
"width": "100%",
|
|
154
|
+
"padding": "14px 24px",
|
|
155
|
+
"background": "#ef4444",
|
|
156
|
+
"color": "#ffffff",
|
|
157
|
+
"border": "none",
|
|
158
|
+
"borderRadius": "10px",
|
|
159
|
+
"fontSize": "16px",
|
|
160
|
+
"fontWeight": "600",
|
|
161
|
+
"cursor": "pointer",
|
|
162
|
+
"transition": "all 0.3s",
|
|
163
|
+
"boxShadow": "0 4px 12px rgba(239,68,68,0.3)"
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
Logout
|
|
167
|
+
</button>
|
|
168
|
+
</div>
|
|
169
|
+
</div>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# Login/Signup view
|
|
173
|
+
return <div
|
|
174
|
+
style={{
|
|
175
|
+
"minHeight": "100vh",
|
|
176
|
+
"background": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
177
|
+
"display": "flex",
|
|
178
|
+
"alignItems": "center",
|
|
179
|
+
"justifyContent": "center",
|
|
180
|
+
"fontFamily": "system-ui, -apple-system, sans-serif",
|
|
181
|
+
"padding": "20px"
|
|
182
|
+
}}
|
|
183
|
+
>
|
|
184
|
+
<div
|
|
185
|
+
style={{
|
|
186
|
+
"background": "#ffffff",
|
|
187
|
+
"borderRadius": "16px",
|
|
188
|
+
"padding": "48px",
|
|
189
|
+
"boxShadow": "0 20px 60px rgba(0,0,0,0.3)",
|
|
190
|
+
"maxWidth": "400px",
|
|
191
|
+
"width": "100%"
|
|
192
|
+
}}
|
|
193
|
+
>
|
|
194
|
+
# Header
|
|
195
|
+
<div
|
|
196
|
+
style={{"textAlign": "center", "marginBottom": "32px"}}
|
|
197
|
+
>
|
|
198
|
+
<h1
|
|
199
|
+
style={{
|
|
200
|
+
"fontSize": "2rem",
|
|
201
|
+
"fontWeight": "700",
|
|
202
|
+
"color": "#1f2937",
|
|
203
|
+
"marginBottom": "8px"
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
{("Create Account" if isSignup else "Welcome Back")}
|
|
207
|
+
</h1>
|
|
208
|
+
<p
|
|
209
|
+
style={{"color": "#6b7280", "fontSize": "0.95rem"}}
|
|
210
|
+
>
|
|
211
|
+
{(
|
|
212
|
+
"Sign up to get started"
|
|
213
|
+
if isSignup
|
|
214
|
+
else "Sign in to continue"
|
|
215
|
+
)}
|
|
216
|
+
</p>
|
|
217
|
+
</div>
|
|
218
|
+
# Form
|
|
219
|
+
<form
|
|
220
|
+
onSubmit={handleSubmit}
|
|
221
|
+
style={{"marginBottom": "24px"}}
|
|
222
|
+
>
|
|
223
|
+
<div
|
|
224
|
+
style={{"marginBottom": "20px"}}
|
|
225
|
+
>
|
|
226
|
+
<label
|
|
227
|
+
style={{
|
|
228
|
+
"display": "block",
|
|
229
|
+
"marginBottom": "8px",
|
|
230
|
+
"color": "#374151",
|
|
231
|
+
"fontSize": "14px",
|
|
232
|
+
"fontWeight": "600"
|
|
233
|
+
}}
|
|
234
|
+
>
|
|
235
|
+
Username
|
|
236
|
+
</label>
|
|
237
|
+
<input
|
|
238
|
+
type="text"
|
|
239
|
+
value={username}
|
|
240
|
+
onChange={lambda e: any -> None{ setUsername(
|
|
241
|
+
e.target.value
|
|
242
|
+
);} }
|
|
243
|
+
placeholder="Enter your username"
|
|
244
|
+
style={{
|
|
245
|
+
"width": "100%",
|
|
246
|
+
"padding": "12px 16px",
|
|
247
|
+
"border": "2px solid #e5e7eb",
|
|
248
|
+
"borderRadius": "10px",
|
|
249
|
+
"fontSize": "16px",
|
|
250
|
+
"outline": "none",
|
|
251
|
+
"transition": "border 0.2s",
|
|
252
|
+
"boxSizing": "border-box"
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
</div>
|
|
256
|
+
<div
|
|
257
|
+
style={{"marginBottom": "24px"}}
|
|
258
|
+
>
|
|
259
|
+
<label
|
|
260
|
+
style={{
|
|
261
|
+
"display": "block",
|
|
262
|
+
"marginBottom": "8px",
|
|
263
|
+
"color": "#374151",
|
|
264
|
+
"fontSize": "14px",
|
|
265
|
+
"fontWeight": "600"
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
Password
|
|
269
|
+
</label>
|
|
270
|
+
<input
|
|
271
|
+
type="password"
|
|
272
|
+
value={password}
|
|
273
|
+
onChange={lambda e: any -> None{ setPassword(
|
|
274
|
+
e.target.value
|
|
275
|
+
);} }
|
|
276
|
+
placeholder="Enter your password"
|
|
277
|
+
style={{
|
|
278
|
+
"width": "100%",
|
|
279
|
+
"padding": "12px 16px",
|
|
280
|
+
"border": "2px solid #e5e7eb",
|
|
281
|
+
"borderRadius": "10px",
|
|
282
|
+
"fontSize": "16px",
|
|
283
|
+
"outline": "none",
|
|
284
|
+
"transition": "border 0.2s",
|
|
285
|
+
"boxSizing": "border-box"
|
|
286
|
+
}}
|
|
287
|
+
/>
|
|
288
|
+
</div>
|
|
289
|
+
{(
|
|
290
|
+
<div
|
|
291
|
+
style={{
|
|
292
|
+
"padding": "12px",
|
|
293
|
+
"background": "#fee2e2",
|
|
294
|
+
"border": "1px solid #fecaca",
|
|
295
|
+
"borderRadius": "8px",
|
|
296
|
+
"marginBottom": "20px"
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
<p
|
|
300
|
+
style={{
|
|
301
|
+
"color": "#dc2626",
|
|
302
|
+
"fontSize": "14px",
|
|
303
|
+
"margin": "0"
|
|
304
|
+
}}
|
|
305
|
+
>
|
|
306
|
+
{error}
|
|
307
|
+
</p>
|
|
308
|
+
</div>
|
|
309
|
+
)
|
|
310
|
+
if error
|
|
311
|
+
else None}
|
|
312
|
+
<button
|
|
313
|
+
type="submit"
|
|
314
|
+
disabled={loading}
|
|
315
|
+
style={{
|
|
316
|
+
"width": "100%",
|
|
317
|
+
"padding": "14px 24px",
|
|
318
|
+
"background": (("#9ca3af" if loading else "#667eea")),
|
|
319
|
+
"color": "#ffffff",
|
|
320
|
+
"border": "none",
|
|
321
|
+
"borderRadius": "10px",
|
|
322
|
+
"fontSize": "16px",
|
|
323
|
+
"fontWeight": "600",
|
|
324
|
+
"cursor": (("not-allowed" if loading else "pointer")),
|
|
325
|
+
"transition": "all 0.3s",
|
|
326
|
+
"boxShadow": "0 4px 12px rgba(102,126,234,0.4)"
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
{(
|
|
330
|
+
(
|
|
331
|
+
"Processing..."
|
|
332
|
+
if loading
|
|
333
|
+
else ("Sign Up" if isSignup else "Sign In")
|
|
334
|
+
)
|
|
335
|
+
)}
|
|
336
|
+
</button>
|
|
337
|
+
</form>
|
|
338
|
+
# Toggle between login/signup
|
|
339
|
+
<div
|
|
340
|
+
style={{"textAlign": "center"}}
|
|
341
|
+
>
|
|
342
|
+
<p
|
|
343
|
+
style={{
|
|
344
|
+
"color": "#6b7280",
|
|
345
|
+
"fontSize": "14px",
|
|
346
|
+
"marginBottom": "8px"
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
{(
|
|
350
|
+
(
|
|
351
|
+
"Already have an account?"
|
|
352
|
+
if isSignup
|
|
353
|
+
else "Don't have an account?"
|
|
354
|
+
)
|
|
355
|
+
)}
|
|
356
|
+
</p>
|
|
357
|
+
<button
|
|
358
|
+
onClick={lambda -> None{ setIsSignup(not isSignup);setError(
|
|
359
|
+
""
|
|
360
|
+
);setUsername("");setPassword("");} }
|
|
361
|
+
style={{
|
|
362
|
+
"background": "none",
|
|
363
|
+
"border": "none",
|
|
364
|
+
"color": "#667eea",
|
|
365
|
+
"fontSize": "14px",
|
|
366
|
+
"fontWeight": "600",
|
|
367
|
+
"cursor": "pointer",
|
|
368
|
+
"textDecoration": "underline"
|
|
369
|
+
}}
|
|
370
|
+
>
|
|
371
|
+
{(("Sign In" if isSignup else "Create Account"))}
|
|
372
|
+
</button>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</div>;
|
|
376
|
+
}
|
|
377
|
+
}
|