jac-client 0.2.6__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.
Files changed (119) hide show
  1. jac_client/examples/all-in-one/{src/button.jac → button.jac} +4 -3
  2. jac_client/examples/all-in-one/components/CategoryFilter.jac +47 -0
  3. jac_client/examples/all-in-one/components/Header.jac +17 -0
  4. jac_client/examples/all-in-one/components/ProfitOverview.jac +64 -0
  5. jac_client/examples/all-in-one/components/Summary.jac +76 -0
  6. jac_client/examples/all-in-one/components/TransactionForm.jac +188 -0
  7. jac_client/examples/all-in-one/components/TransactionItem.jac +62 -0
  8. jac_client/examples/all-in-one/components/TransactionList.jac +44 -0
  9. jac_client/examples/all-in-one/components/button.jac +8 -0
  10. jac_client/examples/all-in-one/components/navigation.jac +126 -0
  11. jac_client/examples/all-in-one/constants/categories.jac +36 -0
  12. jac_client/examples/all-in-one/constants/clients.jac +12 -0
  13. jac_client/examples/all-in-one/context/BudgetContext.jac +31 -0
  14. jac_client/examples/all-in-one/hooks/useBudget.jac +122 -0
  15. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +37 -0
  16. jac_client/examples/all-in-one/main.jac +542 -0
  17. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +140 -0
  18. jac_client/examples/all-in-one/pages/FeaturesTest.jac +157 -0
  19. jac_client/examples/all-in-one/pages/LandingPage.jac +124 -0
  20. jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
  21. jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
  22. jac_client/examples/all-in-one/pages/loginPage.jac +127 -0
  23. jac_client/examples/all-in-one/pages/nestedDemo.jac +54 -0
  24. jac_client/examples/all-in-one/pages/notFound.jac +18 -0
  25. jac_client/examples/all-in-one/pages/signupPage.jac +127 -0
  26. jac_client/examples/all-in-one/utils/formatters.jac +49 -0
  27. jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
  28. jac_client/examples/asset-serving/image-asset/main.jac +56 -0
  29. jac_client/examples/asset-serving/import-alias/main.jac +109 -0
  30. jac_client/examples/basic/main.jac +23 -0
  31. jac_client/examples/basic-auth/main.jac +363 -0
  32. jac_client/examples/basic-auth-with-router/main.jac +451 -0
  33. jac_client/examples/basic-full-stack/main.jac +362 -0
  34. jac_client/examples/css-styling/js-styling/main.jac +63 -0
  35. jac_client/examples/css-styling/material-ui/main.jac +122 -0
  36. jac_client/examples/css-styling/pure-css/main.jac +55 -0
  37. jac_client/examples/css-styling/sass-example/main.jac +55 -0
  38. jac_client/examples/css-styling/styled-components/main.jac +62 -0
  39. jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
  40. jac_client/examples/full-stack-with-auth/main.jac +696 -0
  41. jac_client/examples/little-x/main.jac +681 -0
  42. jac_client/examples/little-x/src/submit-button.jac +15 -14
  43. jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
  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/{src/app.jac → 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 +35 -0
  52. jac_client/examples/with-router/main.jac +286 -0
  53. jac_client/plugin/cli.jac +507 -470
  54. jac_client/plugin/client.jac +30 -12
  55. jac_client/plugin/client_runtime.cl.jac +25 -15
  56. jac_client/plugin/impl/client.impl.jac +126 -26
  57. jac_client/plugin/impl/client_runtime.impl.jac +182 -10
  58. jac_client/plugin/plugin_config.jac +216 -34
  59. jac_client/plugin/src/__init__.jac +0 -2
  60. jac_client/plugin/src/compiler.jac +2 -2
  61. jac_client/plugin/src/config_loader.jac +1 -0
  62. jac_client/plugin/src/desktop_config.jac +31 -0
  63. jac_client/plugin/src/impl/compiler.impl.jac +99 -30
  64. jac_client/plugin/src/impl/config_loader.impl.jac +8 -0
  65. jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
  66. jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
  67. jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
  68. jac_client/plugin/src/impl/vite_bundler.impl.jac +384 -144
  69. jac_client/plugin/src/package_installer.jac +1 -1
  70. jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
  71. jac_client/plugin/src/targets/desktop_target.jac +37 -0
  72. jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
  73. jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
  74. jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
  75. jac_client/plugin/src/targets/register.jac +21 -0
  76. jac_client/plugin/src/targets/registry.jac +87 -0
  77. jac_client/plugin/src/targets/web_target.jac +35 -0
  78. jac_client/plugin/src/vite_bundler.jac +15 -1
  79. jac_client/plugin/utils/__init__.jac +3 -0
  80. jac_client/plugin/utils/bun_installer.jac +16 -0
  81. jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
  82. jac_client/templates/client.jacpack +72 -0
  83. jac_client/templates/fullstack.jacpack +61 -0
  84. jac_client/tests/conftest.py +110 -52
  85. jac_client/tests/fixtures/spawn_test/app.jac +64 -70
  86. jac_client/tests/fixtures/with-ts/app.jac +28 -28
  87. jac_client/tests/test_cli.py +280 -113
  88. jac_client/tests/test_e2e.py +232 -0
  89. jac_client/tests/test_helpers.py +58 -0
  90. jac_client/tests/test_it.py +325 -154
  91. jac_client/tests/test_it_desktop.py +891 -0
  92. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/METADATA +20 -11
  93. jac_client-0.2.11.dist-info/RECORD +113 -0
  94. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/WHEEL +1 -1
  95. jac_client/examples/all-in-one/src/app.jac +0 -841
  96. jac_client/examples/all-in-one/src/components/button.jac +0 -7
  97. jac_client/examples/asset-serving/css-with-image/src/app.jac +0 -88
  98. jac_client/examples/asset-serving/image-asset/src/app.jac +0 -55
  99. jac_client/examples/asset-serving/import-alias/src/app.jac +0 -111
  100. jac_client/examples/basic/src/app.jac +0 -21
  101. jac_client/examples/basic-auth/src/app.jac +0 -377
  102. jac_client/examples/basic-auth-with-router/src/app.jac +0 -464
  103. jac_client/examples/basic-full-stack/src/app.jac +0 -365
  104. jac_client/examples/css-styling/js-styling/src/app.jac +0 -84
  105. jac_client/examples/css-styling/material-ui/src/app.jac +0 -122
  106. jac_client/examples/css-styling/pure-css/src/app.jac +0 -64
  107. jac_client/examples/css-styling/sass-example/src/app.jac +0 -64
  108. jac_client/examples/css-styling/styled-components/src/app.jac +0 -71
  109. jac_client/examples/css-styling/tailwind-example/src/app.jac +0 -63
  110. jac_client/examples/full-stack-with-auth/src/app.jac +0 -722
  111. jac_client/examples/little-x/src/app.jac +0 -719
  112. jac_client/examples/nested-folders/nested-advance/src/app.jac +0 -35
  113. jac_client/examples/ts-support/src/app.jac +0 -35
  114. jac_client/examples/with-router/src/app.jac +0 -323
  115. jac_client/plugin/src/babel_processor.jac +0 -18
  116. jac_client/plugin/src/impl/babel_processor.impl.jac +0 -84
  117. jac_client-0.2.6.dist-info/RECORD +0 -74
  118. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/entry_points.txt +0 -0
  119. {jac_client-0.2.6.dist-info → jac_client-0.2.11.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  cl import from antd { Button }
2
2
 
3
3
  cl def:pub CustomButtonRoot -> any {
4
- return <Button>
5
- Root Button
6
- </Button>;
4
+ return
5
+ <Button>
6
+ Root Button
7
+ </Button>;
7
8
  }
@@ -0,0 +1,47 @@
1
+ # Category filter component
2
+ # Demonstrates: iteration with map, conditional classes, events
3
+ cl import from ..constants.categories {
4
+ CATEGORIES,
5
+ CATEGORY_LABELS,
6
+ CATEGORY_COLORS
7
+ }
8
+
9
+ cl {
10
+ # Filter buttons for categories
11
+ def:pub CategoryFilter(selectedCategory: str, onSelect: any) -> any {
12
+ # Add "ALL" to the beginning of categories
13
+ allCategories = ["ALL"].concat(CATEGORIES);
14
+
15
+ return
16
+ <div className="category-filter">
17
+ <h3 className="filter-title">
18
+ Filter by Category
19
+ </h3>
20
+ <div className="filter-buttons">
21
+ {allCategories.map(
22
+ lambda cat: str -> any { isActive = selectedCategory == cat;color = CATEGORY_COLORS[
23
+ cat
24
+ ]
25
+ if cat != "ALL"
26
+ else "#374151";return
27
+ <button
28
+ key={cat}
29
+ className={("filter-btn active")
30
+ if isActive
31
+ else ("filter-btn")}
32
+ style={{
33
+ "borderColor": color,
34
+ "backgroundColor": (color)
35
+ if isActive
36
+ else ("transparent"),
37
+ "color": ("#fff") if isActive else (color)
38
+ }}
39
+ onClick={lambda : onSelect(cat)}
40
+ >
41
+ {(cat) if cat == "ALL" else (CATEGORY_LABELS[cat])}
42
+ </button>; }
43
+ )}
44
+ </div>
45
+ </div>;
46
+ }
47
+ }
@@ -0,0 +1,17 @@
1
+ # Header component
2
+ # Demonstrates: simple component, CSS classes
3
+ cl {
4
+ def:pub Header -> any {
5
+ return
6
+ <header className="header">
7
+ <div className="header-content">
8
+ <h1 className="header-title">
9
+ Budget Planner
10
+ </h1>
11
+ <p className="header-subtitle">
12
+ Track your income and expenses
13
+ </p>
14
+ </div>
15
+ </header>;
16
+ }
17
+ }
@@ -0,0 +1,64 @@
1
+ # Profit Overview component - monthly profit snapshot
2
+ # Demonstrates: context usage, calculated displays, formatting
3
+ cl import from ..context.BudgetContext {
4
+ useBudgetContext
5
+ }
6
+ cl import from ..utils.formatters { formatCurrency }
7
+
8
+ cl {
9
+ def:pub ProfitOverview -> any {
10
+ budget = useBudgetContext();
11
+
12
+ businessIncome = budget["businessIncome"];
13
+ businessExpenses = budget["businessExpenses"];
14
+ taxReserve = budget["taxReserve"];
15
+ netProfit = budget["netProfit"];
16
+
17
+ return
18
+ <div className="profit-overview">
19
+ <h3 className="profit-title">
20
+ Monthly Profit Snapshot
21
+ </h3>
22
+ <div className="profit-breakdown">
23
+ <div className="profit-row income">
24
+ <span className="profit-label">
25
+ Business Income
26
+ </span>
27
+ <span className="profit-value positive">
28
+ +{formatCurrency(businessIncome)}
29
+ </span>
30
+ </div>
31
+ <div className="profit-row expense">
32
+ <span className="profit-label">
33
+ Business Expenses
34
+ </span>
35
+ <span className="profit-value negative">
36
+ -{formatCurrency(businessExpenses)}
37
+ </span>
38
+ </div>
39
+ <div className="profit-row tax">
40
+ <span className="profit-label">
41
+ Tax Reserve (20%)
42
+ </span>
43
+ <span className="profit-value negative">
44
+ -{formatCurrency(taxReserve)}
45
+ </span>
46
+ </div>
47
+ <div className="profit-row total">
48
+ <span className="profit-label">
49
+ Net Profit
50
+ </span>
51
+ <span
52
+ className={("profit-value bold positive")
53
+ if netProfit >= 0
54
+ else ("profit-value bold negative")}
55
+ >
56
+ {("+") if netProfit > 0 else ("")}{formatCurrency(
57
+ netProfit
58
+ )}
59
+ </span>
60
+ </div>
61
+ </div>
62
+ </div>;
63
+ }
64
+ }
@@ -0,0 +1,76 @@
1
+ # Summary component - displays budget totals with business/personal breakdown
2
+ # Demonstrates: context usage, conditional rendering, ternary operator, 6-card layout
3
+ cl import from ..context.BudgetContext {
4
+ useBudgetContext
5
+ }
6
+ cl import from ..utils.formatters { formatCurrency }
7
+
8
+ cl {
9
+ def:pub Summary -> any {
10
+ budget = useBudgetContext();
11
+
12
+ # Business/Personal breakdown
13
+ businessIncome = budget["businessIncome"];
14
+ businessExpenses = budget["businessExpenses"];
15
+ personalIncome = budget["personalIncome"];
16
+ personalExpenses = budget["personalExpenses"];
17
+ taxReserve = budget["taxReserve"];
18
+ netProfit = budget["netProfit"];
19
+
20
+ return
21
+ <div className="summary">
22
+ <div className="summary-card business-income">
23
+ <span className="summary-label">
24
+ Business Income
25
+ </span>
26
+ <span className="summary-value">
27
+ {formatCurrency(businessIncome)}
28
+ </span>
29
+ </div>
30
+ <div className="summary-card business-expenses">
31
+ <span className="summary-label">
32
+ Business Expenses
33
+ </span>
34
+ <span className="summary-value">
35
+ {formatCurrency(businessExpenses)}
36
+ </span>
37
+ </div>
38
+ <div className="summary-card personal-income">
39
+ <span className="summary-label">
40
+ Personal Income
41
+ </span>
42
+ <span className="summary-value">
43
+ {formatCurrency(personalIncome)}
44
+ </span>
45
+ </div>
46
+ <div className="summary-card personal-expenses">
47
+ <span className="summary-label">
48
+ Personal Expenses
49
+ </span>
50
+ <span className="summary-value">
51
+ {formatCurrency(personalExpenses)}
52
+ </span>
53
+ </div>
54
+ <div className="summary-card tax-reserve">
55
+ <span className="summary-label">
56
+ Tax Reserve (20%)
57
+ </span>
58
+ <span className="summary-value">
59
+ {formatCurrency(taxReserve)}
60
+ </span>
61
+ </div>
62
+ <div
63
+ className={("summary-card net-profit positive")
64
+ if netProfit >= 0
65
+ else ("summary-card net-profit negative")}
66
+ >
67
+ <span className="summary-label">
68
+ Net Profit
69
+ </span>
70
+ <span className="summary-value">
71
+ {("+") if netProfit > 0 else ("")}{formatCurrency(netProfit)}
72
+ </span>
73
+ </div>
74
+ </div>;
75
+ }
76
+ }
@@ -0,0 +1,188 @@
1
+ # Transaction form component
2
+ # Demonstrates: form handling, useState, events, select options
3
+ cl import from ..context.BudgetContext {
4
+ useBudgetContext
5
+ }
6
+ cl import from ..constants.categories { CATEGORIES, CATEGORY_LABELS }
7
+ cl import from ..constants.clients { CLIENTS }
8
+
9
+ cl {
10
+ def:pub TransactionForm -> any {
11
+ [description, setDescription] = useState("");
12
+ [amount, setAmount] = useState("");
13
+ [category, setCategory] = useState("OTHER");
14
+ [txType, setTxType] = useState("expense");
15
+ [isBusiness, setIsBusiness] = useState(false);
16
+ [clientName, setClientName] = useState("");
17
+ budget = useBudgetContext();
18
+
19
+ def handleSubmit(e: any) -> None {
20
+ e.preventDefault();
21
+
22
+ # Validate inputs
23
+ trimmedDesc = description.trim();
24
+ if trimmedDesc == "" or amount == "" {
25
+ return;
26
+ }
27
+
28
+ parsedAmount = parseFloat(amount);
29
+ if isNaN(parsedAmount) or parsedAmount <= 0 {
30
+ return;
31
+ }
32
+
33
+ # Add the transaction
34
+ budget["addTransaction"](
35
+ trimmedDesc, parsedAmount, category, txType, isBusiness, clientName
36
+ );
37
+
38
+ # Reset form
39
+ setDescription("");
40
+ setAmount("");
41
+ setCategory("OTHER");
42
+ setIsBusiness(false);
43
+ setClientName("");
44
+ }
45
+
46
+ # Filter categories based on type (income only has INCOME category)
47
+ availableCategories = CATEGORIES.filter(
48
+ lambda cat: str -> bool { if txType == "income" {
49
+ return cat == "INCOME";
50
+ }return cat != "INCOME"; }
51
+ );
52
+
53
+ # Show client dropdown only for business income
54
+ showClientDropdown = txType == "income" and isBusiness;
55
+
56
+ return
57
+ <form className="transaction-form" onSubmit={handleSubmit}>
58
+ <div className="form-row">
59
+ <div className="form-group type-toggle">
60
+ <button
61
+ type="button"
62
+ className={("toggle-btn active")
63
+ if txType == "expense"
64
+ else ("toggle-btn")}
65
+ onClick={lambda : setTxType("expense")}
66
+ >
67
+ Expense
68
+ </button>
69
+ <button
70
+ type="button"
71
+ className={("toggle-btn active income")
72
+ if txType == "income"
73
+ else ("toggle-btn")}
74
+ onClick={lambda : setTxType("income")}
75
+ >
76
+ Income
77
+ </button>
78
+ </div>
79
+ <div className="form-group business-toggle">
80
+ <button
81
+ type="button"
82
+ className={("toggle-btn active")
83
+ if isBusiness
84
+ else ("toggle-btn")}
85
+ onClick={lambda : setIsBusiness(true)}
86
+ >
87
+ Business
88
+ </button>
89
+ <button
90
+ type="button"
91
+ className={("toggle-btn active")
92
+ if not isBusiness
93
+ else ("toggle-btn")}
94
+ onClick={lambda : setIsBusiness(false)}
95
+ >
96
+ Personal
97
+ </button>
98
+ </div>
99
+ </div>
100
+ <div className="form-row">
101
+ <div className="form-group">
102
+ <label htmlFor="description">
103
+ Description
104
+ </label>
105
+ <input
106
+ id="description"
107
+ type="text"
108
+ value={description}
109
+ onChange={lambda e: any -> None { setDescription(
110
+ e.target.value
111
+ );}}
112
+ placeholder="Enter description..."
113
+ className="form-input"
114
+ />
115
+ </div>
116
+ <div className="form-group">
117
+ <label htmlFor="amount">
118
+ Amount
119
+ </label>
120
+ <input
121
+ id="amount"
122
+ type="number"
123
+ value={amount}
124
+ onChange={lambda e: any -> None { setAmount(
125
+ e.target.value
126
+ );}}
127
+ placeholder="0.00"
128
+ min="0"
129
+ step="0.01"
130
+ className="form-input"
131
+ />
132
+ </div>
133
+ <div className="form-group">
134
+ <label htmlFor="category">
135
+ Category
136
+ </label>
137
+ <select
138
+ id="category"
139
+ value={category}
140
+ onChange={lambda e: any -> None { setCategory(
141
+ e.target.value
142
+ );}}
143
+ className="form-select"
144
+ >
145
+ {availableCategories.map(
146
+ lambda cat: str -> any { return
147
+ <option key={cat} value={cat}>
148
+ {CATEGORY_LABELS[cat]}
149
+ </option>; }
150
+ )}
151
+ </select>
152
+ </div>
153
+ {showClientDropdown
154
+ and <div className="form-group">
155
+ <label htmlFor="client">
156
+ Client
157
+ </label>
158
+ <select
159
+ id="client"
160
+ value={clientName}
161
+ onChange={lambda e: any -> None { setClientName(
162
+ e.target.value
163
+ );}}
164
+ className="form-select"
165
+ >
166
+ <option value="">
167
+ Select Client (Optional)
168
+ </option>
169
+ {CLIENTS.map(
170
+ lambda client: str -> any { return
171
+ <option key={client} value={client}>
172
+ {client}
173
+ </option>; }
174
+ )}
175
+ </select>
176
+ </div>}
177
+ <div className="form-group">
178
+ <label>
179
+ Action
180
+ </label>
181
+ <button type="submit" className="submit-btn">
182
+ Add {(txType[0].toUpperCase() + txType.slice(1))}
183
+ </button>
184
+ </div>
185
+ </div>
186
+ </form>;
187
+ }
188
+ }
@@ -0,0 +1,62 @@
1
+ # Single transaction item component
2
+ # Demonstrates: NEW PROPS PATTERN (direct parameters, NOT props dict)
3
+ cl import from ..utils.formatters {
4
+ formatCurrency,
5
+ formatDate
6
+ }
7
+ cl import from ..constants.categories { CATEGORY_COLORS, CATEGORY_LABELS }
8
+
9
+ cl {
10
+ # NEW WAY: Direct parameters instead of props: dict
11
+ def:pub TransactionItem(
12
+ id: str,
13
+ description: str,
14
+ amount: float,
15
+ category: str,
16
+ txType: str,
17
+ date: str,
18
+ isBusiness: bool,
19
+ clientName: any,
20
+ onDelete: any
21
+ ) -> any {
22
+ isIncome = txType == "income";
23
+ color = CATEGORY_COLORS[category];
24
+ label = CATEGORY_LABELS[category];
25
+
26
+ return
27
+ <div className="transaction-item">
28
+ <div className="tx-left">
29
+ <span className="tx-category" style={{"backgroundColor": color}}>
30
+ {label}
31
+ </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>
43
+ </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>;
61
+ }
62
+ }
@@ -0,0 +1,44 @@
1
+ # Transaction list component
2
+ # Demonstrates: rendering lists with map, passing props to child components
3
+ cl import from .TransactionItem {
4
+ TransactionItem
5
+ }
6
+
7
+ cl {
8
+ # Props: transactions list and delete handler
9
+ def:pub TransactionList(transactions: list, onDelete: any) -> any {
10
+ if transactions.length == 0 {
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>;
20
+ }
21
+
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>;
43
+ }
44
+ }
@@ -0,0 +1,8 @@
1
+ cl import from antd { Button }
2
+
3
+ cl def:pub CustomButton -> any {
4
+ return
5
+ <Button>
6
+ Nested Button
7
+ </Button>;
8
+ }
@@ -0,0 +1,126 @@
1
+ cl import from "@jac/runtime" {
2
+ Link,
3
+ useNavigate,
4
+ useLocation,
5
+ jacLogout,
6
+ jacIsLoggedIn
7
+ }
8
+
9
+ cl {
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
+ }
27
+
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={{
39
+ "padding": "0.5rem 1rem",
40
+ "background": "#ef4444",
41
+ "color": "#ffffff",
42
+ "border": "none",
43
+ "borderRadius": "4px",
44
+ "cursor": "pointer",
45
+ "fontWeight": "600"
46
+ }}
47
+ >
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
+ }}
69
+ >
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
+ }
126
+ }