jac-client 0.2.6__py3-none-any.whl → 0.2.7__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 +473 -741
- jac_client/examples/all-in-one/src/components/CategoryFilter.jac +35 -0
- jac_client/examples/all-in-one/src/components/Header.jac +13 -0
- jac_client/examples/all-in-one/src/components/ProfitOverview.jac +50 -0
- jac_client/examples/all-in-one/src/components/Summary.jac +53 -0
- jac_client/examples/all-in-one/src/components/TransactionForm.jac +158 -0
- jac_client/examples/all-in-one/src/components/TransactionItem.jac +55 -0
- jac_client/examples/all-in-one/src/components/TransactionList.jac +37 -0
- jac_client/examples/all-in-one/src/components/navigation.jac +132 -0
- jac_client/examples/all-in-one/src/constants/categories.jac +37 -0
- jac_client/examples/all-in-one/src/constants/clients.jac +13 -0
- jac_client/examples/all-in-one/src/context/BudgetContext.jac +28 -0
- jac_client/examples/all-in-one/src/hooks/useBudget.jac +116 -0
- jac_client/examples/all-in-one/src/hooks/useLocalStorage.jac +36 -0
- jac_client/examples/all-in-one/src/pages/BudgetPlanner.cl.jac +70 -0
- jac_client/examples/all-in-one/src/pages/BudgetPlanner.jac +126 -0
- jac_client/examples/all-in-one/src/pages/FeaturesTest.cl.jac +552 -0
- jac_client/examples/all-in-one/src/pages/FeaturesTest.jac +126 -0
- jac_client/examples/all-in-one/src/pages/LandingPage.jac +101 -0
- jac_client/examples/all-in-one/src/pages/loginPage.jac +132 -0
- jac_client/examples/all-in-one/src/pages/nestedDemo.jac +61 -0
- jac_client/examples/all-in-one/src/pages/notFound.jac +24 -0
- jac_client/examples/all-in-one/src/pages/signupPage.jac +133 -0
- jac_client/examples/all-in-one/src/utils/formatters.jac +52 -0
- jac_client/examples/asset-serving/css-with-image/src/app.jac +3 -3
- jac_client/examples/asset-serving/image-asset/src/app.jac +3 -3
- jac_client/examples/asset-serving/import-alias/src/app.jac +3 -3
- jac_client/examples/basic/src/app.jac +3 -3
- jac_client/examples/basic-auth/src/app.jac +31 -37
- jac_client/examples/basic-auth-with-router/src/app.jac +16 -16
- jac_client/examples/basic-full-stack/src/app.jac +24 -30
- jac_client/examples/css-styling/js-styling/src/app.jac +5 -5
- jac_client/examples/css-styling/material-ui/src/app.jac +5 -5
- jac_client/examples/css-styling/pure-css/src/app.jac +5 -5
- jac_client/examples/css-styling/sass-example/src/app.jac +5 -5
- jac_client/examples/css-styling/styled-components/src/app.jac +5 -5
- jac_client/examples/css-styling/tailwind-example/src/app.jac +5 -5
- jac_client/examples/full-stack-with-auth/src/app.jac +16 -16
- jac_client/examples/ts-support/src/app.jac +4 -4
- jac_client/examples/with-router/src/app.jac +4 -4
- jac_client/plugin/cli.jac +155 -203
- jac_client/plugin/client_runtime.cl.jac +5 -1
- jac_client/plugin/impl/client.impl.jac +74 -12
- jac_client/plugin/plugin_config.jac +11 -11
- jac_client/plugin/src/compiler.jac +2 -1
- jac_client/plugin/src/impl/babel_processor.impl.jac +22 -17
- jac_client/plugin/src/impl/compiler.impl.jac +57 -18
- jac_client/plugin/src/impl/vite_bundler.impl.jac +66 -102
- jac_client/plugin/src/package_installer.jac +1 -1
- jac_client/plugin/src/vite_bundler.jac +1 -0
- jac_client/tests/conftest.py +10 -8
- jac_client/tests/fixtures/spawn_test/app.jac +15 -18
- jac_client/tests/fixtures/with-ts/app.jac +4 -4
- jac_client/tests/test_cli.py +99 -45
- jac_client/tests/test_it.py +290 -79
- {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/METADATA +16 -7
- jac_client-0.2.7.dist-info/RECORD +97 -0
- jac_client-0.2.6.dist-info/RECORD +0 -74
- {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/WHEEL +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/entry_points.txt +0 -0
- {jac_client-0.2.6.dist-info → jac_client-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Landing Page - Ledgerly Brand
|
|
2
|
+
# Professional landing page with hero, features, and CTA sections
|
|
3
|
+
# Demonstrates: routing with Link, dark theme design, responsive layout
|
|
4
|
+
cl import from "@jac-client/utils" {
|
|
5
|
+
Link,
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
cl {
|
|
12
|
+
def:pub LandingPage() -> any {
|
|
13
|
+
return <div className="landing-container">
|
|
14
|
+
<section className="hero-section">
|
|
15
|
+
<h1 className="brand-name">Ledgerly</h1>
|
|
16
|
+
<p className="tagline">Know your real income</p>
|
|
17
|
+
|
|
18
|
+
<h2 className="headline">
|
|
19
|
+
Track Business & Personal Finances Separately
|
|
20
|
+
</h2>
|
|
21
|
+
<p className="subheadline">
|
|
22
|
+
Built for freelancers who need to see their true income
|
|
23
|
+
after taxes and business expenses.
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<div style={{"display": "flex", "gap": "15px", "justifyContent": "center"}}>
|
|
27
|
+
<Link to="/app">
|
|
28
|
+
<button className="cta-button">
|
|
29
|
+
Let's Plan
|
|
30
|
+
</button>
|
|
31
|
+
</Link>
|
|
32
|
+
<Link to="/features">
|
|
33
|
+
<button className="cta-button" style={{
|
|
34
|
+
"backgroundColor": "#8b5cf6",
|
|
35
|
+
"border": "2px solid #8b5cf6"
|
|
36
|
+
}}>
|
|
37
|
+
Test Features
|
|
38
|
+
</button>
|
|
39
|
+
</Link>
|
|
40
|
+
</div>
|
|
41
|
+
</section>
|
|
42
|
+
|
|
43
|
+
<section className="features-section">
|
|
44
|
+
<h2 className="features-title">Built for Freelancers</h2>
|
|
45
|
+
|
|
46
|
+
<div className="features-grid">
|
|
47
|
+
<div className="feature-card">
|
|
48
|
+
<div className="feature-icon">💼</div>
|
|
49
|
+
<h3 className="feature-title">Business vs Personal</h3>
|
|
50
|
+
<p className="feature-description">
|
|
51
|
+
Tag every transaction as business or personal.
|
|
52
|
+
See your income streams separately and understand where your money really comes from.
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div className="feature-card">
|
|
57
|
+
<div className="feature-icon">📊</div>
|
|
58
|
+
<h3 className="feature-title">Auto Tax Reserve</h3>
|
|
59
|
+
<p className="feature-description">
|
|
60
|
+
Automatically calculate 20% tax reserve on business income.
|
|
61
|
+
Know your true net profit after taxes without surprises.
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div className="feature-card">
|
|
66
|
+
<div className="feature-icon">🏷️</div>
|
|
67
|
+
<h3 className="feature-title">Client Tagging</h3>
|
|
68
|
+
<p className="feature-description">
|
|
69
|
+
Tag business income by client name. Track which clients
|
|
70
|
+
are generating the most revenue for your business.
|
|
71
|
+
</p>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</section>
|
|
75
|
+
|
|
76
|
+
<section className="cta-section">
|
|
77
|
+
<h2 className="cta-section-title">Ready to know your real income?</h2>
|
|
78
|
+
<p className="cta-section-text">
|
|
79
|
+
Start tracking your freelance finances the smart way.
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
<div style={{"display": "flex", "gap": "15px", "justifyContent": "center"}}>
|
|
83
|
+
<Link to="/app">
|
|
84
|
+
<button className="cta-button">
|
|
85
|
+
Get Started Free
|
|
86
|
+
</button>
|
|
87
|
+
</Link>
|
|
88
|
+
<Link to="/features">
|
|
89
|
+
<button className="cta-button" style={{
|
|
90
|
+
"backgroundColor": "#8b5cf6",
|
|
91
|
+
"border": "2px solid #8b5cf6"
|
|
92
|
+
}}>
|
|
93
|
+
Explore Features
|
|
94
|
+
</button>
|
|
95
|
+
</Link>
|
|
96
|
+
</div>
|
|
97
|
+
</section>
|
|
98
|
+
</div>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
cl import from "@jac-client/utils" {
|
|
2
|
+
Link,
|
|
3
|
+
useNavigate,
|
|
4
|
+
jacLogin
|
|
5
|
+
}
|
|
6
|
+
cl {
|
|
7
|
+
def:pub LoginPage -> any {
|
|
8
|
+
has username: str = "";
|
|
9
|
+
has password: str = "";
|
|
10
|
+
has error: str = "";
|
|
11
|
+
navigate = useNavigate();
|
|
12
|
+
|
|
13
|
+
async def handleLogin(e: any) -> None {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
error = "";
|
|
16
|
+
if not username or not password {
|
|
17
|
+
error = "Please fill in all fields";
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
success = await jacLogin(username, password);
|
|
21
|
+
if success {
|
|
22
|
+
navigate("/");
|
|
23
|
+
} else {
|
|
24
|
+
error = "Invalid credentials";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def handleUsernameChange(e: any) -> None {
|
|
29
|
+
username = e.target.value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def handlePasswordChange(e: any) -> None {
|
|
33
|
+
password = e.target.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
errorDisplay = None;
|
|
37
|
+
if error {
|
|
38
|
+
errorDisplay = <div
|
|
39
|
+
style={{"color": "#dc2626", "fontSize": "14px", "marginBottom": "12px"}}
|
|
40
|
+
>
|
|
41
|
+
{error}
|
|
42
|
+
</div>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return <div
|
|
46
|
+
style={{
|
|
47
|
+
"minHeight": "calc(100vh - 56px)",
|
|
48
|
+
"display": "flex",
|
|
49
|
+
"alignItems": "center",
|
|
50
|
+
"justifyContent": "center",
|
|
51
|
+
"background": "#f5f5f5"
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
<div
|
|
55
|
+
style={{
|
|
56
|
+
"background": "#ffffff",
|
|
57
|
+
"padding": "32px",
|
|
58
|
+
"borderRadius": "8px",
|
|
59
|
+
"width": "300px",
|
|
60
|
+
"boxShadow": "0 2px 8px rgba(0,0,0,0.1)"
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
<h2
|
|
64
|
+
style={{"marginBottom": "24px", "textAlign": "center"}}
|
|
65
|
+
>
|
|
66
|
+
Login
|
|
67
|
+
</h2>
|
|
68
|
+
<form
|
|
69
|
+
onSubmit={handleLogin}
|
|
70
|
+
>
|
|
71
|
+
<input
|
|
72
|
+
type="text"
|
|
73
|
+
value={username}
|
|
74
|
+
onChange={handleUsernameChange}
|
|
75
|
+
placeholder="Username"
|
|
76
|
+
style={{
|
|
77
|
+
"width": "100%",
|
|
78
|
+
"padding": "10px",
|
|
79
|
+
"marginBottom": "12px",
|
|
80
|
+
"border": "1px solid #ddd",
|
|
81
|
+
"borderRadius": "4px",
|
|
82
|
+
"boxSizing": "border-box"
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
<input
|
|
86
|
+
type="password"
|
|
87
|
+
value={password}
|
|
88
|
+
onChange={handlePasswordChange}
|
|
89
|
+
placeholder="Password"
|
|
90
|
+
style={{
|
|
91
|
+
"width": "100%",
|
|
92
|
+
"padding": "10px",
|
|
93
|
+
"marginBottom": "12px",
|
|
94
|
+
"border": "1px solid #ddd",
|
|
95
|
+
"borderRadius": "4px",
|
|
96
|
+
"boxSizing": "border-box"
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
{errorDisplay}
|
|
100
|
+
<button
|
|
101
|
+
type="submit"
|
|
102
|
+
style={{
|
|
103
|
+
"width": "100%",
|
|
104
|
+
"padding": "10px",
|
|
105
|
+
"background": "#3b82f6",
|
|
106
|
+
"color": "#ffffff",
|
|
107
|
+
"border": "none",
|
|
108
|
+
"borderRadius": "4px",
|
|
109
|
+
"cursor": "pointer",
|
|
110
|
+
"fontWeight": "600"
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
Login
|
|
114
|
+
</button>
|
|
115
|
+
</form>
|
|
116
|
+
<p
|
|
117
|
+
style={{
|
|
118
|
+
"textAlign": "center",
|
|
119
|
+
"marginTop": "16px",
|
|
120
|
+
"fontSize": "14px"
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
Need an account?
|
|
124
|
+
{" "}
|
|
125
|
+
<Link to="/signup">
|
|
126
|
+
Sign up
|
|
127
|
+
</Link>
|
|
128
|
+
</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Nested folder imports (same pattern as nested-basic/)
|
|
2
|
+
cl import from ..components.button {
|
|
3
|
+
CustomButton
|
|
4
|
+
}
|
|
5
|
+
cl import from ..button { CustomButtonRoot }
|
|
6
|
+
cl import from "@jac-client/utils" {
|
|
7
|
+
|
|
8
|
+
Navigate,
|
|
9
|
+
jacIsLoggedIn
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
cl {
|
|
14
|
+
def:pub NestedImportsDemo -> any {
|
|
15
|
+
# Check if user is logged in, redirect if not
|
|
16
|
+
if not jacIsLoggedIn() {
|
|
17
|
+
return <Navigate to="/login" />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return <div
|
|
21
|
+
style={{
|
|
22
|
+
"padding": "2rem",
|
|
23
|
+
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<h1>
|
|
27
|
+
📁 Nested Folder Imports
|
|
28
|
+
</h1>
|
|
29
|
+
<p>
|
|
30
|
+
This page mirrors the
|
|
31
|
+
{" "}
|
|
32
|
+
<code>
|
|
33
|
+
nested-folders/nested-basic
|
|
34
|
+
</code>
|
|
35
|
+
{" "}
|
|
36
|
+
example by importing components from both
|
|
37
|
+
{" "}
|
|
38
|
+
<code>
|
|
39
|
+
components.button
|
|
40
|
+
</code>
|
|
41
|
+
{" "}
|
|
42
|
+
and
|
|
43
|
+
{" "}
|
|
44
|
+
<code>
|
|
45
|
+
button
|
|
46
|
+
</code>
|
|
47
|
+
</p>
|
|
48
|
+
<p
|
|
49
|
+
style={{"marginTop": "0.75rem"}}
|
|
50
|
+
>
|
|
51
|
+
Both buttons below are rendered via relative imports:
|
|
52
|
+
</p>
|
|
53
|
+
<div
|
|
54
|
+
style={{"display": "flex", "gap": "1rem", "marginTop": "1.5rem"}}
|
|
55
|
+
>
|
|
56
|
+
<CustomButton />
|
|
57
|
+
<CustomButtonRoot />
|
|
58
|
+
</div>
|
|
59
|
+
</div>;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
cl import from "@jac-client/utils" {
|
|
2
|
+
Link
|
|
3
|
+
}
|
|
4
|
+
cl {
|
|
5
|
+
def:pub NotFound -> any {
|
|
6
|
+
return <div
|
|
7
|
+
style={{
|
|
8
|
+
"padding": "2rem",
|
|
9
|
+
"textAlign": "center",
|
|
10
|
+
"fontFamily": "system-ui, -apple-system, sans-serif"
|
|
11
|
+
}}
|
|
12
|
+
>
|
|
13
|
+
<h1>
|
|
14
|
+
🔍 404 - Page Not Found
|
|
15
|
+
</h1>
|
|
16
|
+
<p>
|
|
17
|
+
The page you are looking for does not exist.
|
|
18
|
+
</p>
|
|
19
|
+
<Link to="/">
|
|
20
|
+
← Back to Home
|
|
21
|
+
</Link>
|
|
22
|
+
</div>;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
cl import from "@jac-client/utils" {
|
|
2
|
+
Link,
|
|
3
|
+
useNavigate,
|
|
4
|
+
jacSignup
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
cl {
|
|
8
|
+
def:pub SignupPage -> any {
|
|
9
|
+
has username: str = "";
|
|
10
|
+
has password: str = "";
|
|
11
|
+
has error: str = "";
|
|
12
|
+
navigate = useNavigate();
|
|
13
|
+
|
|
14
|
+
async def handleSignup(e: any) -> None {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
error = "";
|
|
17
|
+
if not username or not password {
|
|
18
|
+
error = "Please fill in all fields";
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
result = await jacSignup(username, password);
|
|
22
|
+
if result["success"] {
|
|
23
|
+
navigate("/");
|
|
24
|
+
} else {
|
|
25
|
+
error = result["error"] if result["error"] else "Signup failed";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
def handleUsernameChange(e: any) -> None {
|
|
30
|
+
username = e.target.value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
def handlePasswordChange(e: any) -> None {
|
|
34
|
+
password = e.target.value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
errorDisplay = None;
|
|
38
|
+
if error {
|
|
39
|
+
errorDisplay = <div
|
|
40
|
+
style={{"color": "#dc2626", "fontSize": "14px", "marginBottom": "12px"}}
|
|
41
|
+
>
|
|
42
|
+
{error}
|
|
43
|
+
</div>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return <div
|
|
47
|
+
style={{
|
|
48
|
+
"minHeight": "calc(100vh - 56px)",
|
|
49
|
+
"display": "flex",
|
|
50
|
+
"alignItems": "center",
|
|
51
|
+
"justifyContent": "center",
|
|
52
|
+
"background": "#f5f5f5"
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
<div
|
|
56
|
+
style={{
|
|
57
|
+
"background": "#ffffff",
|
|
58
|
+
"padding": "32px",
|
|
59
|
+
"borderRadius": "8px",
|
|
60
|
+
"width": "300px",
|
|
61
|
+
"boxShadow": "0 2px 8px rgba(0,0,0,0.1)"
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
<h2
|
|
65
|
+
style={{"marginBottom": "24px", "textAlign": "center"}}
|
|
66
|
+
>
|
|
67
|
+
Sign Up
|
|
68
|
+
</h2>
|
|
69
|
+
<form
|
|
70
|
+
onSubmit={handleSignup}
|
|
71
|
+
>
|
|
72
|
+
<input
|
|
73
|
+
type="text"
|
|
74
|
+
value={username}
|
|
75
|
+
onChange={handleUsernameChange}
|
|
76
|
+
placeholder="Username"
|
|
77
|
+
style={{
|
|
78
|
+
"width": "100%",
|
|
79
|
+
"padding": "10px",
|
|
80
|
+
"marginBottom": "12px",
|
|
81
|
+
"border": "1px solid #ddd",
|
|
82
|
+
"borderRadius": "4px",
|
|
83
|
+
"boxSizing": "border-box"
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
<input
|
|
87
|
+
type="password"
|
|
88
|
+
value={password}
|
|
89
|
+
onChange={handlePasswordChange}
|
|
90
|
+
placeholder="Password"
|
|
91
|
+
style={{
|
|
92
|
+
"width": "100%",
|
|
93
|
+
"padding": "10px",
|
|
94
|
+
"marginBottom": "12px",
|
|
95
|
+
"border": "1px solid #ddd",
|
|
96
|
+
"borderRadius": "4px",
|
|
97
|
+
"boxSizing": "border-box"
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
{errorDisplay}
|
|
101
|
+
<button
|
|
102
|
+
type="submit"
|
|
103
|
+
style={{
|
|
104
|
+
"width": "100%",
|
|
105
|
+
"padding": "10px",
|
|
106
|
+
"background": "#3b82f6",
|
|
107
|
+
"color": "#ffffff",
|
|
108
|
+
"border": "none",
|
|
109
|
+
"borderRadius": "4px",
|
|
110
|
+
"cursor": "pointer",
|
|
111
|
+
"fontWeight": "600"
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
Sign Up
|
|
115
|
+
</button>
|
|
116
|
+
</form>
|
|
117
|
+
<p
|
|
118
|
+
style={{
|
|
119
|
+
"textAlign": "center",
|
|
120
|
+
"marginTop": "16px",
|
|
121
|
+
"fontSize": "14px"
|
|
122
|
+
}}
|
|
123
|
+
>
|
|
124
|
+
Have an account?
|
|
125
|
+
{" "}
|
|
126
|
+
<Link to="/login">
|
|
127
|
+
Login
|
|
128
|
+
</Link>
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
</div>;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Utility functions for formatting
|
|
2
|
+
# Demonstrates: def:pub exports, string methods
|
|
3
|
+
|
|
4
|
+
cl {
|
|
5
|
+
# Format number as currency - demonstrates string methods
|
|
6
|
+
def:pub formatCurrency(amount: float) -> str {
|
|
7
|
+
# Use toFixed for decimal formatting
|
|
8
|
+
formatted = amount.toFixed(2);
|
|
9
|
+
return "$" + formatted;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Format ISO date string to readable format
|
|
13
|
+
def:pub formatDate(dateStr: str) -> str {
|
|
14
|
+
date = Reflect.construct(Date, [dateStr]);
|
|
15
|
+
return date.toLocaleDateString("en-US", {
|
|
16
|
+
"month": "short",
|
|
17
|
+
"day": "numeric",
|
|
18
|
+
"year": "numeric"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Capitalize first letter of string
|
|
23
|
+
def:pub capitalize(text: str) -> str {
|
|
24
|
+
if text.length == 0 {
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
return text[0].toUpperCase() + text.slice(1).toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Truncate string with ellipsis
|
|
31
|
+
def:pub truncate(text: str, maxLength: int) -> str {
|
|
32
|
+
if text.length <= maxLength {
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
return text.slice(0, maxLength) + "...";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Format large numbers with K/M suffix
|
|
39
|
+
def:pub formatCompact(num: float) -> str {
|
|
40
|
+
if num >= 1000000 {
|
|
41
|
+
divided = num / 1000000;
|
|
42
|
+
rounded = (Math.round(divided * 10) / 10);
|
|
43
|
+
return (rounded.toString().concat("M"));
|
|
44
|
+
}
|
|
45
|
+
if num >= 1000 {
|
|
46
|
+
divided = num / 1000;
|
|
47
|
+
rounded = (Math.round(divided * 10) / 10);
|
|
48
|
+
return rounded.toString().concat("K");
|
|
49
|
+
}
|
|
50
|
+
return Math.round(num).toString();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Pages
|
|
2
|
-
cl import from react {
|
|
2
|
+
cl import from react { useEffect }
|
|
3
3
|
cl import ".styles.css";
|
|
4
4
|
|
|
5
5
|
cl {
|
|
6
6
|
def:pub app -> any {
|
|
7
|
-
|
|
7
|
+
has count: int = 0;
|
|
8
8
|
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
9
9
|
return <div
|
|
10
10
|
style={{
|
|
@@ -37,7 +37,7 @@ cl {
|
|
|
37
37
|
times!
|
|
38
38
|
</p>
|
|
39
39
|
<button
|
|
40
|
-
onClick={lambda e: any -> None{
|
|
40
|
+
onClick={lambda e: any -> None{ count = count + 1;} }
|
|
41
41
|
style={{
|
|
42
42
|
padding: "10px 20px",
|
|
43
43
|
fontSize: "16px",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Pages
|
|
2
|
-
cl import from react {
|
|
2
|
+
cl import from react { useEffect }
|
|
3
3
|
|
|
4
4
|
cl {
|
|
5
5
|
def:pub app -> any {
|
|
6
|
-
|
|
6
|
+
has count: int = 0;
|
|
7
7
|
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
8
8
|
return <div
|
|
9
9
|
style={{
|
|
@@ -36,7 +36,7 @@ cl {
|
|
|
36
36
|
times!
|
|
37
37
|
</p>
|
|
38
38
|
<button
|
|
39
|
-
onClick={lambda e: any -> None{
|
|
39
|
+
onClick={lambda e: any -> None{ count = count + 1;} }
|
|
40
40
|
style={{
|
|
41
41
|
padding: "10px 20px",
|
|
42
42
|
fontSize: "16px",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Pages
|
|
2
|
-
cl import from react {
|
|
2
|
+
cl import from react { useEffect }
|
|
3
3
|
# Import image using the @jac-client/assets alias
|
|
4
4
|
cl import from "@jac-client/assets/burger.png" {
|
|
5
5
|
default as burgerImage
|
|
@@ -7,7 +7,7 @@ cl import from "@jac-client/assets/burger.png" {
|
|
|
7
7
|
|
|
8
8
|
cl {
|
|
9
9
|
def:pub app -> any {
|
|
10
|
-
|
|
10
|
+
has count: int = 0;
|
|
11
11
|
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
12
12
|
return <div
|
|
13
13
|
style={{
|
|
@@ -49,7 +49,7 @@ cl {
|
|
|
49
49
|
times!
|
|
50
50
|
</p>
|
|
51
51
|
<button
|
|
52
|
-
onClick={lambda e: any -> None{
|
|
52
|
+
onClick={lambda e: any -> None{ count = count + 1;} }
|
|
53
53
|
style={{
|
|
54
54
|
padding: "10px 20px",
|
|
55
55
|
fontSize: "16px",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Pages
|
|
2
|
-
cl import from react {
|
|
2
|
+
cl import from react { useEffect }
|
|
3
3
|
cl {
|
|
4
4
|
def:pub app -> any {
|
|
5
|
-
|
|
5
|
+
has count: int = 0;
|
|
6
6
|
useEffect(lambda -> None{ console.log("Count: ", count);} , [count]);
|
|
7
7
|
return <div>
|
|
8
8
|
<h1>
|
|
@@ -12,7 +12,7 @@ cl {
|
|
|
12
12
|
Count: {count}
|
|
13
13
|
</p>
|
|
14
14
|
<button
|
|
15
|
-
onClick={lambda e: any -> None{
|
|
15
|
+
onClick={lambda e: any -> None{ count = count + 1;} }
|
|
16
16
|
>
|
|
17
17
|
Increment
|
|
18
18
|
</button>
|