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,7 +1,9 @@
|
|
|
1
1
|
# Single transaction item component
|
|
2
2
|
# Demonstrates: NEW PROPS PATTERN (direct parameters, NOT props dict)
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
cl import from ..utils.formatters {
|
|
4
|
+
formatCurrency,
|
|
5
|
+
formatDate
|
|
6
|
+
}
|
|
5
7
|
cl import from ..constants.categories { CATEGORY_COLORS, CATEGORY_LABELS }
|
|
6
8
|
|
|
7
9
|
cl {
|
|
@@ -21,35 +23,40 @@ cl {
|
|
|
21
23
|
color = CATEGORY_COLORS[category];
|
|
22
24
|
label = CATEGORY_LABELS[category];
|
|
23
25
|
|
|
24
|
-
return
|
|
25
|
-
<div className="
|
|
26
|
-
<
|
|
27
|
-
className="tx-category"
|
|
28
|
-
|
|
29
|
-
>
|
|
30
|
-
{label}
|
|
31
|
-
</span>
|
|
32
|
-
<div className="tx-details">
|
|
33
|
-
<span className="tx-description">
|
|
34
|
-
{description}
|
|
35
|
-
{(clientName != None and isIncome) and <span className="tx-client"> • {clientName}</span>}
|
|
26
|
+
return
|
|
27
|
+
<div className="transaction-item">
|
|
28
|
+
<div className="tx-left">
|
|
29
|
+
<span className="tx-category" style={{"backgroundColor": color}}>
|
|
30
|
+
{label}
|
|
36
31
|
</span>
|
|
37
|
-
<
|
|
32
|
+
<div className="tx-details">
|
|
33
|
+
<span className="tx-description">
|
|
34
|
+
{description}{(clientName != None and isIncome)
|
|
35
|
+
and <span className="tx-client">
|
|
36
|
+
• {clientName}
|
|
37
|
+
</span>}
|
|
38
|
+
</span>
|
|
39
|
+
<span className="tx-date">
|
|
40
|
+
{formatDate(date)}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
38
43
|
</div>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
<div className="tx-right">
|
|
45
|
+
<span
|
|
46
|
+
className={("tx-amount income")
|
|
47
|
+
if isIncome
|
|
48
|
+
else ("tx-amount expense")}
|
|
49
|
+
>
|
|
50
|
+
{("+") if isIncome else ("-")}{formatCurrency(amount)}
|
|
51
|
+
</span>
|
|
52
|
+
<button
|
|
53
|
+
className="delete-btn"
|
|
54
|
+
onClick={lambda : onDelete(id)}
|
|
55
|
+
title="Delete transaction"
|
|
56
|
+
>
|
|
57
|
+
X
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>;
|
|
54
61
|
}
|
|
55
62
|
}
|
|
@@ -1,37 +1,44 @@
|
|
|
1
1
|
# Transaction list component
|
|
2
2
|
# Demonstrates: rendering lists with map, passing props to child components
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
cl import from .TransactionItem {
|
|
4
|
+
TransactionItem
|
|
5
|
+
}
|
|
5
6
|
|
|
6
7
|
cl {
|
|
7
8
|
# Props: transactions list and delete handler
|
|
8
9
|
def:pub TransactionList(transactions: list, onDelete: any) -> any {
|
|
9
10
|
if transactions.length == 0 {
|
|
10
|
-
return
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
return
|
|
12
|
+
<div className="empty-state">
|
|
13
|
+
<p>
|
|
14
|
+
No transactions yet.
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
Add your first income or expense above!
|
|
18
|
+
</p>
|
|
19
|
+
</div>;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
return
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
return
|
|
23
|
+
<div className="transaction-list">
|
|
24
|
+
<h3 className="list-title">
|
|
25
|
+
Transactions ({transactions.length})
|
|
26
|
+
</h3>
|
|
27
|
+
{transactions.map(
|
|
28
|
+
lambda tx: dict -> any { return
|
|
29
|
+
<TransactionItem
|
|
30
|
+
key={tx["id"]}
|
|
31
|
+
id={tx["id"]}
|
|
32
|
+
description={tx["description"]}
|
|
33
|
+
amount={tx["amount"]}
|
|
34
|
+
category={tx["category"]}
|
|
35
|
+
txType={tx["type"]}
|
|
36
|
+
date={tx["date"]}
|
|
37
|
+
isBusiness={tx["isBusinessTransaction"] || false}
|
|
38
|
+
clientName={tx["clientName"] || None}
|
|
39
|
+
onDelete={onDelete}
|
|
40
|
+
/>; }
|
|
41
|
+
)}
|
|
42
|
+
</div>;
|
|
36
43
|
}
|
|
37
44
|
}
|
|
@@ -1,132 +1,126 @@
|
|
|
1
|
-
cl import from "@jac
|
|
2
|
-
|
|
1
|
+
cl import from "@jac/runtime" {
|
|
3
2
|
Link,
|
|
4
3
|
useNavigate,
|
|
5
4
|
useLocation,
|
|
6
5
|
jacLogout,
|
|
7
6
|
jacIsLoggedIn
|
|
8
7
|
}
|
|
8
|
+
|
|
9
9
|
cl {
|
|
10
10
|
def:pub Navigation -> any {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def linkStyle(path: str) -> dict {
|
|
16
|
-
isActive = location.pathname == path;
|
|
17
|
-
return {
|
|
18
|
-
"padding": "0.5rem 1rem",
|
|
19
|
-
"textDecoration": "none",
|
|
20
|
-
"color": "#0066cc" if isActive else "#333",
|
|
21
|
-
"fontWeight": "bold" if isActive else "normal",
|
|
22
|
-
"backgroundColor": "#e3f2fd" if isActive else "transparent",
|
|
23
|
-
"borderRadius": "4px",
|
|
24
|
-
"display": "inline-block"
|
|
25
|
-
};
|
|
26
|
-
}
|
|
11
|
+
location = useLocation();
|
|
12
|
+
isLoggedIn = jacIsLoggedIn();
|
|
13
|
+
navigate = useNavigate();
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
navigate("/login");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
authButtons = None;
|
|
35
|
-
if isLoggedIn {
|
|
36
|
-
authButtons = <button
|
|
37
|
-
onClick={handleLogout}
|
|
38
|
-
style={{
|
|
15
|
+
def linkStyle(path: str) -> dict {
|
|
16
|
+
isActive = location.pathname == path;
|
|
17
|
+
return {
|
|
39
18
|
"padding": "0.5rem 1rem",
|
|
40
|
-
"
|
|
41
|
-
"color": "#
|
|
42
|
-
"
|
|
19
|
+
"textDecoration": "none",
|
|
20
|
+
"color": "#0066cc" if isActive else "#333",
|
|
21
|
+
"fontWeight": "bold" if isActive else "normal",
|
|
22
|
+
"backgroundColor": "#e3f2fd" if isActive else "transparent",
|
|
43
23
|
"borderRadius": "4px",
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
to="/login"
|
|
54
|
-
style={linkStyle("/login")}
|
|
55
|
-
>
|
|
56
|
-
Login
|
|
57
|
-
</Link>
|
|
58
|
-
<Link
|
|
59
|
-
to="/signup"
|
|
60
|
-
style={linkStyle("/signup")}
|
|
61
|
-
>
|
|
62
|
-
Sign Up
|
|
63
|
-
</Link>
|
|
64
|
-
</>;
|
|
65
|
-
}
|
|
24
|
+
"display": "inline-block"
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def handleLogout(e: any) -> None {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
jacLogout();
|
|
31
|
+
navigate("/login");
|
|
32
|
+
}
|
|
66
33
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
"gap": "1rem",
|
|
81
|
-
"alignItems": "center",
|
|
82
|
-
"justifyContent": "space-between"
|
|
83
|
-
}}
|
|
84
|
-
>
|
|
85
|
-
<div
|
|
86
|
-
style={{"display": "flex", "gap": "1rem", "alignItems": "center"}}
|
|
34
|
+
authButtons = None;
|
|
35
|
+
if isLoggedIn {
|
|
36
|
+
authButtons = <button
|
|
37
|
+
onClick={handleLogout}
|
|
38
|
+
style={{
|
|
39
|
+
"padding": "0.5rem 1rem",
|
|
40
|
+
"background": "#ef4444",
|
|
41
|
+
"color": "#ffffff",
|
|
42
|
+
"border": "none",
|
|
43
|
+
"borderRadius": "4px",
|
|
44
|
+
"cursor": "pointer",
|
|
45
|
+
"fontWeight": "600"
|
|
46
|
+
}}
|
|
87
47
|
>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<Link
|
|
110
|
-
to="/landing"
|
|
111
|
-
style={linkStyle("/landing")}
|
|
112
|
-
>
|
|
113
|
-
Landing Page
|
|
114
|
-
</Link>
|
|
115
|
-
<Link
|
|
116
|
-
to="/budget-planner"
|
|
117
|
-
style={linkStyle("/budget-planner")}
|
|
118
|
-
>
|
|
119
|
-
Budget Planner
|
|
120
|
-
</Link>
|
|
121
|
-
</>
|
|
122
|
-
)}
|
|
123
|
-
</div>
|
|
124
|
-
<div
|
|
125
|
-
style={{"display": "flex", "gap": "1rem", "alignItems": "center"}}
|
|
48
|
+
Logout
|
|
49
|
+
</button>;
|
|
50
|
+
} else {
|
|
51
|
+
authButtons = <>
|
|
52
|
+
<Link to="/login" style={linkStyle("/login")}>
|
|
53
|
+
Login
|
|
54
|
+
</Link>
|
|
55
|
+
<Link to="/signup" style={linkStyle("/signup")}>
|
|
56
|
+
Sign Up
|
|
57
|
+
</Link>
|
|
58
|
+
</>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return
|
|
62
|
+
<nav
|
|
63
|
+
style={{
|
|
64
|
+
"padding": "1rem",
|
|
65
|
+
"backgroundColor": "#f5f5f5",
|
|
66
|
+
"marginBottom": "2rem",
|
|
67
|
+
"boxShadow": "0 2px 4px rgba(0,0,0,0.1)"
|
|
68
|
+
}}
|
|
126
69
|
>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
70
|
+
<div
|
|
71
|
+
style={{
|
|
72
|
+
"maxWidth": "1200px",
|
|
73
|
+
"margin": "0 auto",
|
|
74
|
+
"display": "flex",
|
|
75
|
+
"gap": "1rem",
|
|
76
|
+
"alignItems": "center",
|
|
77
|
+
"justifyContent": "space-between"
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<div
|
|
81
|
+
style={{
|
|
82
|
+
"display": "flex",
|
|
83
|
+
"gap": "1rem",
|
|
84
|
+
"alignItems": "center"
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{isLoggedIn
|
|
88
|
+
and (
|
|
89
|
+
<>
|
|
90
|
+
<Link to="/" style={linkStyle("/")}>
|
|
91
|
+
Home
|
|
92
|
+
</Link>
|
|
93
|
+
<Link to="/nested" style={linkStyle("/nested")}>
|
|
94
|
+
Nested Imports
|
|
95
|
+
</Link>
|
|
96
|
+
<Link
|
|
97
|
+
to="/features-test"
|
|
98
|
+
style={linkStyle("/features-test")}
|
|
99
|
+
>
|
|
100
|
+
Features Test
|
|
101
|
+
</Link>
|
|
102
|
+
<Link to="/landing" style={linkStyle("/landing")}>
|
|
103
|
+
Landing Page
|
|
104
|
+
</Link>
|
|
105
|
+
<Link
|
|
106
|
+
to="/budget-planner"
|
|
107
|
+
style={linkStyle("/budget-planner")}
|
|
108
|
+
>
|
|
109
|
+
Budget Planner
|
|
110
|
+
</Link>
|
|
111
|
+
</>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
<div
|
|
115
|
+
style={{
|
|
116
|
+
"display": "flex",
|
|
117
|
+
"gap": "1rem",
|
|
118
|
+
"alignItems": "center"
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
{authButtons}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</nav>;
|
|
125
|
+
}
|
|
132
126
|
}
|
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
# Category constants and enums for Budget Planner
|
|
2
2
|
# Demonstrates: enum:pub, glob:pub exports
|
|
3
|
-
|
|
4
3
|
cl {
|
|
5
4
|
# Category enum - demonstrates enum:pub export
|
|
6
|
-
enum:pub Category {
|
|
7
|
-
INCOME,
|
|
8
|
-
FOOD,
|
|
9
|
-
TRANSPORT,
|
|
10
|
-
UTILITIES,
|
|
11
|
-
ENTERTAINMENT,
|
|
12
|
-
OTHER
|
|
13
|
-
}
|
|
5
|
+
enum:pub Category { INCOME, FOOD, TRANSPORT, UTILITIES, ENTERTAINMENT, OTHER }
|
|
14
6
|
|
|
15
7
|
# Category colors - demonstrates glob:pub export
|
|
16
8
|
glob:pub CATEGORY_COLORS: dict = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
"INCOME": "#10b981",
|
|
10
|
+
"FOOD": "#f59e0b",
|
|
11
|
+
"TRANSPORT": "#3b82f6",
|
|
12
|
+
"UTILITIES": "#8b5cf6",
|
|
13
|
+
"ENTERTAINMENT": "#ec4899",
|
|
14
|
+
"OTHER": "#6b7280"
|
|
15
|
+
};
|
|
24
16
|
|
|
25
17
|
# Category labels for display
|
|
26
18
|
glob:pub CATEGORY_LABELS: dict = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
"INCOME": "Income",
|
|
20
|
+
"FOOD": "Food & Dining",
|
|
21
|
+
"TRANSPORT": "Transport",
|
|
22
|
+
"UTILITIES": "Utilities",
|
|
23
|
+
"ENTERTAINMENT": "Entertainment",
|
|
24
|
+
"OTHER": "Other"
|
|
25
|
+
};
|
|
34
26
|
|
|
35
27
|
# All category keys as a list
|
|
36
|
-
glob:pub CATEGORIES: list = [
|
|
28
|
+
glob:pub CATEGORIES: list = [
|
|
29
|
+
"INCOME",
|
|
30
|
+
"FOOD",
|
|
31
|
+
"TRANSPORT",
|
|
32
|
+
"UTILITIES",
|
|
33
|
+
"ENTERTAINMENT",
|
|
34
|
+
"OTHER"
|
|
35
|
+
];
|
|
37
36
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# Client list for freelancer income tracking
|
|
2
2
|
# Demonstrates: glob exports, array constants
|
|
3
|
-
|
|
4
3
|
cl {
|
|
5
4
|
glob:pub CLIENTS = [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
"Acme Corp",
|
|
6
|
+
"TechStart Inc",
|
|
7
|
+
"Design Studio Co",
|
|
8
|
+
"Marketing Agency",
|
|
9
|
+
"Freelance Marketplace",
|
|
10
|
+
"Other"
|
|
11
|
+
];
|
|
13
12
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Budget Context for global state management
|
|
2
2
|
# Demonstrates: createContext, useContext, Context.Provider
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
cl import from react {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext
|
|
6
|
+
}
|
|
5
7
|
cl import from ..hooks.useBudget { useBudget }
|
|
6
8
|
|
|
7
9
|
cl {
|
|
@@ -12,13 +14,14 @@ cl {
|
|
|
12
14
|
def:pub BudgetProvider(children: any) -> any {
|
|
13
15
|
budget = useBudget();
|
|
14
16
|
|
|
15
|
-
return
|
|
16
|
-
{
|
|
17
|
-
|
|
17
|
+
return
|
|
18
|
+
<BudgetContext.Provider value={budget}>
|
|
19
|
+
{children}
|
|
20
|
+
</BudgetContext.Provider>;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
# Custom hook to access budget context
|
|
21
|
-
def:pub useBudgetContext
|
|
24
|
+
def:pub useBudgetContext -> dict {
|
|
22
25
|
context = useContext(BudgetContext);
|
|
23
26
|
if context == None {
|
|
24
27
|
console.error("useBudgetContext must be used within BudgetProvider");
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
# Custom hook for budget operations
|
|
2
2
|
# Demonstrates: custom hooks, useState, array methods
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
cl import from react {
|
|
4
|
+
useCallback,
|
|
5
|
+
useMemo
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
cl {
|
|
7
9
|
# Main budget management hook
|
|
8
|
-
def:pub useBudget
|
|
10
|
+
def:pub useBudget -> dict {
|
|
9
11
|
# Simple useState - no complex localStorage hook
|
|
10
12
|
[transactions, setTransactions] = useState([]);
|
|
11
13
|
|
|
12
14
|
# Add a new transaction
|
|
13
|
-
def addTransaction(
|
|
15
|
+
def addTransaction(
|
|
16
|
+
description: str,
|
|
17
|
+
amount: float,
|
|
18
|
+
category: str,
|
|
19
|
+
txType: str,
|
|
20
|
+
isBusiness: bool,
|
|
21
|
+
clientName: str
|
|
22
|
+
) -> None {
|
|
14
23
|
newTx = {
|
|
15
24
|
"id": Date.now().toString(),
|
|
16
25
|
"description": description,
|
|
@@ -26,9 +35,9 @@ cl {
|
|
|
26
35
|
|
|
27
36
|
# Delete a transaction by ID
|
|
28
37
|
def deleteTransaction(id: str) -> None {
|
|
29
|
-
filtered = transactions.filter(
|
|
30
|
-
return tx["id"] != id;
|
|
31
|
-
|
|
38
|
+
filtered = transactions.filter(
|
|
39
|
+
lambda tx: dict -> bool { return tx["id"] != id; }
|
|
40
|
+
);
|
|
32
41
|
setTransactions(filtered);
|
|
33
42
|
}
|
|
34
43
|
|
|
@@ -75,7 +84,7 @@ cl {
|
|
|
75
84
|
netProfit = businessIncome - businessExpenses - taxReserve;
|
|
76
85
|
|
|
77
86
|
# Get expense breakdown by category (for chart)
|
|
78
|
-
def getExpensesByCategory
|
|
87
|
+
def getExpensesByCategory -> list {
|
|
79
88
|
categoryTotals = {};
|
|
80
89
|
for tx in transactions {
|
|
81
90
|
if tx["type"] == "expense" {
|
|
@@ -89,10 +98,7 @@ cl {
|
|
|
89
98
|
}
|
|
90
99
|
result = [];
|
|
91
100
|
for key in Object.keys(categoryTotals) {
|
|
92
|
-
result = result.concat([{
|
|
93
|
-
"name": key,
|
|
94
|
-
"value": categoryTotals[key]
|
|
95
|
-
}]);
|
|
101
|
+
result = result.concat([{"name": key, "value": categoryTotals[key]}]);
|
|
96
102
|
}
|
|
97
103
|
return result;
|
|
98
104
|
}
|
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
# Custom hook for localStorage persistence
|
|
2
2
|
# Demonstrates: custom hooks, try/except error handling, useEffect
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
cl import from react {
|
|
4
|
+
useEffect
|
|
5
|
+
}
|
|
5
6
|
|
|
6
7
|
cl {
|
|
7
8
|
# Custom hook for syncing state with localStorage
|
|
8
9
|
def useLocalStorage(key: str, initialValue: list) -> list {
|
|
9
10
|
# Initialize state with value from localStorage or default
|
|
10
|
-
[storedValue, setStoredValue] = useState(
|
|
11
|
-
try {
|
|
11
|
+
[storedValue, setStoredValue] = useState(
|
|
12
|
+
lambda -> any { try {
|
|
12
13
|
item = window.localStorage.getItem(key);
|
|
13
14
|
if item {
|
|
14
15
|
return JSON.parse(item);
|
|
15
|
-
}else{
|
|
16
|
+
} else {
|
|
16
17
|
return initialValue;
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
} except Exception as e {
|
|
19
|
+
} except Exception as e {
|
|
20
20
|
console.log("Error reading from localStorage");
|
|
21
21
|
return initialValue;
|
|
22
|
-
}
|
|
23
|
-
|
|
22
|
+
}}
|
|
23
|
+
);
|
|
24
24
|
|
|
25
25
|
# Update localStorage when state changes
|
|
26
|
-
useEffect(
|
|
27
|
-
try {
|
|
26
|
+
useEffect(
|
|
27
|
+
lambda -> None { try {
|
|
28
28
|
window.localStorage.setItem(key, JSON.stringify(storedValue));
|
|
29
29
|
} except Exception as e {
|
|
30
30
|
console.log("Error saving to localStorage");
|
|
31
|
-
}
|
|
32
|
-
|
|
31
|
+
}},
|
|
32
|
+
[key, storedValue]
|
|
33
|
+
);
|
|
33
34
|
|
|
34
35
|
return [storedValue, setStoredValue];
|
|
35
36
|
}
|