jac-client 0.2.10__py3-none-any.whl → 0.2.12__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.
Files changed (85) hide show
  1. jac_client/examples/all-in-one/button.jac +4 -3
  2. jac_client/examples/all-in-one/components/CategoryFilter.jac +36 -24
  3. jac_client/examples/all-in-one/components/Header.jac +12 -8
  4. jac_client/examples/all-in-one/components/ProfitOverview.jac +49 -35
  5. jac_client/examples/all-in-one/components/Summary.jac +59 -36
  6. jac_client/examples/all-in-one/components/TransactionForm.jac +142 -112
  7. jac_client/examples/all-in-one/components/TransactionItem.jac +37 -30
  8. jac_client/examples/all-in-one/components/TransactionList.jac +33 -26
  9. jac_client/examples/all-in-one/components/button.jac +4 -3
  10. jac_client/examples/all-in-one/components/navigation.jac +111 -117
  11. jac_client/examples/all-in-one/constants/categories.jac +23 -24
  12. jac_client/examples/all-in-one/constants/clients.jac +7 -8
  13. jac_client/examples/all-in-one/context/BudgetContext.jac +9 -6
  14. jac_client/examples/all-in-one/hooks/useBudget.jac +18 -12
  15. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +14 -13
  16. jac_client/examples/all-in-one/main.jac +340 -371
  17. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +19 -12
  18. jac_client/examples/all-in-one/pages/FeaturesTest.jac +31 -15
  19. jac_client/examples/all-in-one/pages/LandingPage.jac +113 -90
  20. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +34 -39
  21. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +464 -352
  22. jac_client/examples/all-in-one/pages/loginPage.jac +114 -119
  23. jac_client/examples/all-in-one/pages/nestedDemo.jac +43 -50
  24. jac_client/examples/all-in-one/pages/notFound.jac +14 -15
  25. jac_client/examples/all-in-one/pages/signupPage.jac +113 -119
  26. jac_client/examples/all-in-one/utils/formatters.jac +5 -8
  27. jac_client/examples/asset-serving/css-with-image/main.jac +77 -73
  28. jac_client/examples/asset-serving/image-asset/main.jac +47 -46
  29. jac_client/examples/asset-serving/import-alias/main.jac +93 -95
  30. jac_client/examples/basic/main.jac +17 -15
  31. jac_client/examples/basic-auth/main.jac +246 -254
  32. jac_client/examples/basic-auth-with-router/main.jac +272 -285
  33. jac_client/examples/basic-full-stack/main.jac +245 -242
  34. jac_client/examples/css-styling/js-styling/main.jac +41 -62
  35. jac_client/examples/css-styling/material-ui/main.jac +90 -90
  36. jac_client/examples/css-styling/pure-css/main.jac +35 -44
  37. jac_client/examples/css-styling/sass-example/main.jac +35 -44
  38. jac_client/examples/css-styling/styled-components/main.jac +38 -47
  39. jac_client/examples/css-styling/tailwind-example/main.jac +54 -43
  40. jac_client/examples/full-stack-with-auth/main.jac +407 -433
  41. jac_client/examples/little-x/main.jac +306 -344
  42. jac_client/examples/little-x/src/submit-button.jac +15 -14
  43. jac_client/examples/nested-folders/nested-advance/main.jac +18 -27
  44. jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +4 -6
  45. jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +9 -13
  46. jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +29 -32
  47. jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +12 -18
  48. jac_client/examples/nested-folders/nested-basic/main.jac +7 -5
  49. jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
  50. jac_client/examples/nested-folders/nested-basic/src/components/button.jac +4 -3
  51. jac_client/examples/ts-support/main.jac +26 -26
  52. jac_client/examples/with-router/main.jac +186 -223
  53. jac_client/plugin/client_runtime.cl.jac +5 -3
  54. jac_client/plugin/impl/client_runtime.impl.jac +1 -1
  55. jac_client/plugin/plugin_config.jac +53 -99
  56. jac_client/plugin/src/__init__.jac +0 -2
  57. jac_client/plugin/src/compiler.jac +0 -1
  58. jac_client/plugin/src/impl/compiler.impl.jac +49 -17
  59. jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
  60. jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
  61. jac_client/plugin/src/impl/vite_bundler.impl.jac +146 -84
  62. jac_client/plugin/src/targets/impl/desktop_target.impl.jac +54 -41
  63. jac_client/plugin/utils/__init__.jac +3 -0
  64. jac_client/plugin/utils/bun_installer.jac +16 -0
  65. jac_client/plugin/utils/client_deps.jac +14 -0
  66. jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
  67. jac_client/plugin/utils/impl/client_deps.impl.jac +73 -0
  68. jac_client/templates/client.jacpack +0 -4
  69. jac_client/templates/fullstack.jacpack +1 -5
  70. jac_client/tests/conftest.py +56 -41
  71. jac_client/tests/fixtures/spawn_test/app.jac +49 -52
  72. jac_client/tests/fixtures/with-ts/app.jac +27 -27
  73. jac_client/tests/test_cli.py +71 -6
  74. jac_client/tests/test_helpers.py +11 -18
  75. jac_client/tests/test_it.py +1 -1
  76. {jac_client-0.2.10.dist-info → jac_client-0.2.12.dist-info}/METADATA +5 -5
  77. jac_client-0.2.12.dist-info/RECORD +115 -0
  78. {jac_client-0.2.10.dist-info → jac_client-0.2.12.dist-info}/WHEEL +1 -1
  79. jac_client/plugin/src/babel_processor.jac +0 -18
  80. jac_client/plugin/src/impl/babel_processor.impl.jac +0 -89
  81. jac_client/plugin/utils/impl/node_installer.impl.jac +0 -249
  82. jac_client/plugin/utils/node_installer.jac +0 -41
  83. jac_client-0.2.10.dist-info/RECORD +0 -115
  84. {jac_client-0.2.10.dist-info → jac_client-0.2.12.dist-info}/entry_points.txt +0 -0
  85. {jac_client-0.2.10.dist-info → jac_client-0.2.12.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
- cl import from ..utils.formatters { formatCurrency, formatDate }
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 <div className="transaction-item">
25
- <div className="tx-left">
26
- <span
27
- className="tx-category"
28
- style={{"backgroundColor": color}}
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
- <span className="tx-date">{formatDate(date)}</span>
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
- </div>
40
-
41
- <div className="tx-right">
42
- <span className={("tx-amount income") if isIncome else ("tx-amount expense")}>
43
- {("+") if isIncome else ("-")}{formatCurrency(amount)}
44
- </span>
45
- <button
46
- className="delete-btn"
47
- onClick={lambda:onDelete(id)}
48
- title="Delete transaction"
49
- >
50
- X
51
- </button>
52
- </div>
53
- </div>;
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
- cl import from .TransactionItem { TransactionItem }
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 <div className="empty-state">
11
- <p>No transactions yet.</p>
12
- <p>Add your first income or expense above!</p>
13
- </div>;
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 <div className="transaction-list">
17
- <h3 className="list-title">
18
- Transactions ({transactions.length})
19
- </h3>
20
-
21
- {transactions.map(lambda tx: dict -> any {
22
- return <TransactionItem
23
- key={tx["id"]}
24
- id={tx["id"]}
25
- description={tx["description"]}
26
- amount={tx["amount"]}
27
- category={tx["category"]}
28
- txType={tx["type"]}
29
- date={tx["date"]}
30
- isBusiness={tx["isBusinessTransaction"] || false}
31
- clientName={tx["clientName"] || None}
32
- onDelete={onDelete}
33
- />;
34
- })}
35
- </div>;
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,7 +1,8 @@
1
1
  cl import from antd { Button }
2
2
 
3
3
  cl def:pub CustomButton -> any {
4
- return <Button>
5
- Nested Button
6
- </Button>;
4
+ return
5
+ <Button>
6
+ Nested Button
7
+ </Button>;
7
8
  }
@@ -1,132 +1,126 @@
1
- cl import from "@jac-client/utils" {
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
- location = useLocation();
12
- isLoggedIn = jacIsLoggedIn();
13
- navigate = useNavigate();
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
- def handleLogout(e: any) -> None {
29
- e.preventDefault();
30
- jacLogout();
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
- "background": "#ef4444",
41
- "color": "#ffffff",
42
- "border": "none",
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
- "cursor": "pointer",
45
- "fontWeight": "600"
46
- }}
47
- >
48
- Logout
49
- </button>;
50
- } else {
51
- authButtons = <>
52
- <Link
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
- return <nav
68
- style={{
69
- "padding": "1rem",
70
- "backgroundColor": "#f5f5f5",
71
- "marginBottom": "2rem",
72
- "boxShadow": "0 2px 4px rgba(0,0,0,0.1)"
73
- }}
74
- >
75
- <div
76
- style={{
77
- "maxWidth": "1200px",
78
- "margin": "0 auto",
79
- "display": "flex",
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
- {isLoggedIn
89
- and (
90
- <>
91
- <Link
92
- to="/"
93
- style={linkStyle("/")}
94
- >
95
- Home
96
- </Link>
97
- <Link
98
- to="/nested"
99
- style={linkStyle("/nested")}
100
- >
101
- Nested Imports
102
- </Link>
103
- <Link
104
- to="/features-test"
105
- style={linkStyle("/features-test")}
106
- >
107
- Features Test
108
- </Link>
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
- {authButtons}
128
- </div>
129
- </div>
130
- </nav>;
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
- "INCOME": "#10b981",
18
- "FOOD": "#f59e0b",
19
- "TRANSPORT": "#3b82f6",
20
- "UTILITIES": "#8b5cf6",
21
- "ENTERTAINMENT": "#ec4899",
22
- "OTHER": "#6b7280"
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
- "INCOME": "Income",
28
- "FOOD": "Food & Dining",
29
- "TRANSPORT": "Transport",
30
- "UTILITIES": "Utilities",
31
- "ENTERTAINMENT": "Entertainment",
32
- "OTHER": "Other"
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 = ["INCOME", "FOOD", "TRANSPORT", "UTILITIES", "ENTERTAINMENT", "OTHER"];
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
- "Acme Corp",
7
- "TechStart Inc",
8
- "Design Studio Co",
9
- "Marketing Agency",
10
- "Freelance Marketplace",
11
- "Other"
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
- cl import from react { createContext, useContext }
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 <BudgetContext.Provider value={budget}>
16
- {children}
17
- </BudgetContext.Provider>;
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() -> dict {
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
- cl import from react { useCallback, useMemo }
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() -> dict {
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(description: str, amount: float, category: str, txType: str, isBusiness: bool, clientName: str) -> None {
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(lambda tx: dict -> bool {
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() -> list {
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
- cl import from react { useEffect }
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(lambda -> any {
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(lambda -> None {
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
- }, [key, storedValue]);
31
+ }},
32
+ [key, storedValue]
33
+ );
33
34
 
34
35
  return [storedValue, setStoredValue];
35
36
  }