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