jac-client 0.2.3__py3-none-any.whl → 0.2.8__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 (224) hide show
  1. jac_client/examples/all-in-one/app.jac +494 -347
  2. jac_client/examples/all-in-one/assets/workers/worker.py +5 -0
  3. jac_client/examples/all-in-one/button.jac +1 -1
  4. jac_client/examples/all-in-one/components/CategoryFilter.jac +35 -0
  5. jac_client/examples/all-in-one/components/Header.jac +13 -0
  6. jac_client/examples/all-in-one/components/ProfitOverview.jac +50 -0
  7. jac_client/examples/all-in-one/components/Summary.jac +53 -0
  8. jac_client/examples/all-in-one/components/TransactionForm.jac +158 -0
  9. jac_client/examples/all-in-one/components/TransactionItem.jac +55 -0
  10. jac_client/examples/all-in-one/components/TransactionList.jac +37 -0
  11. jac_client/examples/all-in-one/components/button.jac +1 -1
  12. jac_client/examples/all-in-one/components/navigation.jac +132 -0
  13. jac_client/examples/all-in-one/constants/categories.jac +37 -0
  14. jac_client/examples/all-in-one/constants/clients.jac +13 -0
  15. jac_client/examples/all-in-one/context/BudgetContext.jac +28 -0
  16. jac_client/examples/all-in-one/hooks/useBudget.jac +116 -0
  17. jac_client/examples/all-in-one/hooks/useLocalStorage.jac +36 -0
  18. jac_client/examples/all-in-one/pages/BudgetPlanner.cl.jac +70 -0
  19. jac_client/examples/all-in-one/pages/BudgetPlanner.jac +126 -0
  20. jac_client/examples/all-in-one/pages/FeaturesTest.cl.jac +552 -0
  21. jac_client/examples/all-in-one/pages/FeaturesTest.jac +126 -0
  22. jac_client/examples/all-in-one/pages/LandingPage.jac +101 -0
  23. jac_client/examples/all-in-one/pages/loginPage.jac +132 -0
  24. jac_client/examples/all-in-one/pages/nestedDemo.jac +61 -0
  25. jac_client/examples/all-in-one/pages/notFound.jac +24 -0
  26. jac_client/examples/all-in-one/pages/signupPage.jac +133 -0
  27. jac_client/examples/all-in-one/utils/formatters.jac +52 -0
  28. jac_client/examples/asset-serving/css-with-image/{app.jac → src/app.jac} +4 -4
  29. jac_client/examples/asset-serving/image-asset/{app.jac → src/app.jac} +4 -4
  30. jac_client/examples/asset-serving/import-alias/{app.jac → src/app.jac} +5 -5
  31. jac_client/examples/basic/{app.jac → src/app.jac} +4 -4
  32. jac_client/examples/basic-auth/src/app.jac +371 -0
  33. jac_client/examples/basic-auth-with-router/{app.jac → src/app.jac} +28 -28
  34. jac_client/examples/basic-full-stack/{app.jac → src/app.jac} +166 -127
  35. jac_client/examples/css-styling/js-styling/{app.jac → src/app.jac} +7 -7
  36. jac_client/examples/css-styling/material-ui/{app.jac → src/app.jac} +6 -6
  37. jac_client/examples/css-styling/pure-css/{app.jac → src/app.jac} +7 -7
  38. jac_client/examples/css-styling/sass-example/{app.jac → src/app.jac} +7 -7
  39. jac_client/examples/css-styling/styled-components/{app.jac → src/app.jac} +6 -6
  40. jac_client/examples/css-styling/tailwind-example/{app.jac → src/app.jac} +7 -7
  41. jac_client/examples/full-stack-with-auth/{app.jac → src/app.jac} +47 -47
  42. jac_client/examples/little-x/{app.jac → src/app.jac} +27 -32
  43. jac_client/examples/little-x/src/submit-button.jac +16 -0
  44. jac_client/examples/nested-folders/nested-advance/{ButtonRoot.jac → src/ButtonRoot.jac} +1 -1
  45. jac_client/examples/nested-folders/nested-advance/{app.jac → src/app.jac} +1 -1
  46. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/ButtonSecondL.jac +1 -1
  47. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/Card.jac +1 -1
  48. jac_client/examples/nested-folders/nested-advance/{level1 → src/level1}/level2/ButtonThirdL.jac +1 -1
  49. jac_client/examples/nested-folders/nested-basic/{app.jac → src/app.jac} +2 -2
  50. jac_client/examples/nested-folders/nested-basic/{button.jac → src/button.jac} +1 -1
  51. jac_client/examples/nested-folders/nested-basic/{components → src/components}/button.jac +1 -1
  52. jac_client/examples/ts-support/src/app.jac +35 -0
  53. jac_client/examples/with-router/{app.jac → src/app.jac} +15 -15
  54. jac_client/plugin/cli.jac +504 -0
  55. jac_client/plugin/client.jac +45 -0
  56. jac_client/plugin/client_runtime.cl.jac +42 -0
  57. jac_client/plugin/impl/client.impl.jac +193 -0
  58. jac_client/plugin/impl/client_runtime.impl.jac +195 -0
  59. jac_client/plugin/impl/vite_client_bundle.impl.jac +72 -0
  60. jac_client/plugin/plugin_config.jac +195 -0
  61. jac_client/plugin/src/__init__.jac +20 -0
  62. jac_client/plugin/src/asset_processor.jac +33 -0
  63. jac_client/plugin/src/babel_processor.jac +18 -0
  64. jac_client/plugin/src/compiler.jac +67 -0
  65. jac_client/plugin/src/config_loader.jac +32 -0
  66. jac_client/plugin/src/impl/asset_processor.impl.jac +127 -0
  67. jac_client/plugin/src/impl/babel_processor.impl.jac +89 -0
  68. jac_client/plugin/src/impl/compiler.impl.jac +288 -0
  69. jac_client/plugin/src/impl/config_loader.impl.jac +119 -0
  70. jac_client/plugin/src/impl/import_processor.impl.jac +33 -0
  71. jac_client/plugin/src/impl/jac_to_js.impl.jac +41 -0
  72. jac_client/plugin/src/impl/package_installer.impl.jac +105 -0
  73. jac_client/plugin/src/impl/vite_bundler.impl.jac +626 -0
  74. jac_client/plugin/src/import_processor.jac +19 -0
  75. jac_client/plugin/src/jac_to_js.jac +35 -0
  76. jac_client/plugin/src/package_installer.jac +26 -0
  77. jac_client/plugin/src/vite_bundler.jac +44 -0
  78. jac_client/plugin/vite_client_bundle.jac +31 -0
  79. jac_client/tests/conftest.py +283 -0
  80. jac_client/tests/fixtures/basic-app/app.jac +2 -2
  81. jac_client/tests/fixtures/cl_file/app.cl.jac +2 -2
  82. jac_client/tests/fixtures/client_app_with_antd/app.jac +1 -1
  83. jac_client/tests/fixtures/js_import/app.jac +5 -5
  84. jac_client/tests/fixtures/spawn_test/app.jac +15 -18
  85. jac_client/tests/fixtures/with-ts/app.jac +35 -0
  86. jac_client/tests/test_cli.py +811 -0
  87. jac_client/tests/test_it.py +592 -97
  88. {jac_client-0.2.3.dist-info → jac_client-0.2.8.dist-info}/METADATA +41 -34
  89. jac_client-0.2.8.dist-info/RECORD +97 -0
  90. {jac_client-0.2.3.dist-info → jac_client-0.2.8.dist-info}/WHEEL +2 -1
  91. jac_client-0.2.8.dist-info/entry_points.txt +4 -0
  92. jac_client-0.2.8.dist-info/top_level.txt +1 -0
  93. jac_client/docs/README.md +0 -689
  94. jac_client/docs/advanced-state.md +0 -1265
  95. jac_client/docs/asset-serving/intro.md +0 -209
  96. jac_client/docs/assets/pipe_line-v2.svg +0 -32
  97. jac_client/docs/assets/pipe_line.png +0 -0
  98. jac_client/docs/file-system/app.jac.md +0 -121
  99. jac_client/docs/file-system/backend-frontend.md +0 -217
  100. jac_client/docs/file-system/intro.md +0 -72
  101. jac_client/docs/file-system/nested-imports.md +0 -348
  102. jac_client/docs/guide-example/intro.md +0 -115
  103. jac_client/docs/guide-example/step-01-setup.md +0 -270
  104. jac_client/docs/guide-example/step-02-components.md +0 -416
  105. jac_client/docs/guide-example/step-03-styling.md +0 -478
  106. jac_client/docs/guide-example/step-04-todo-ui.md +0 -477
  107. jac_client/docs/guide-example/step-05-local-state.md +0 -530
  108. jac_client/docs/guide-example/step-06-events.md +0 -749
  109. jac_client/docs/guide-example/step-07-effects.md +0 -468
  110. jac_client/docs/guide-example/step-08-walkers.md +0 -534
  111. jac_client/docs/guide-example/step-09-authentication.md +0 -586
  112. jac_client/docs/guide-example/step-10-routing.md +0 -539
  113. jac_client/docs/guide-example/step-11-final.md +0 -963
  114. jac_client/docs/imports.md +0 -1141
  115. jac_client/docs/lifecycle-hooks.md +0 -773
  116. jac_client/docs/routing.md +0 -659
  117. jac_client/docs/styling/intro.md +0 -249
  118. jac_client/docs/styling/js-styling.md +0 -367
  119. jac_client/docs/styling/material-ui.md +0 -341
  120. jac_client/docs/styling/pure-css.md +0 -299
  121. jac_client/docs/styling/sass.md +0 -403
  122. jac_client/docs/styling/styled-components.md +0 -395
  123. jac_client/docs/styling/tailwind.md +0 -298
  124. jac_client/examples/all-in-one/.babelrc +0 -9
  125. jac_client/examples/all-in-one/README.md +0 -16
  126. jac_client/examples/all-in-one/assets/burger.png +0 -0
  127. jac_client/examples/all-in-one/package.json +0 -29
  128. jac_client/examples/all-in-one/styles.css +0 -26
  129. jac_client/examples/all-in-one/vite.config.js +0 -28
  130. jac_client/examples/asset-serving/css-with-image/.babelrc +0 -9
  131. jac_client/examples/asset-serving/css-with-image/README.md +0 -91
  132. jac_client/examples/asset-serving/css-with-image/assets/burger.png +0 -0
  133. jac_client/examples/asset-serving/css-with-image/package.json +0 -28
  134. jac_client/examples/asset-serving/css-with-image/styles.css +0 -26
  135. jac_client/examples/asset-serving/css-with-image/vite.config.js +0 -28
  136. jac_client/examples/asset-serving/image-asset/.babelrc +0 -9
  137. jac_client/examples/asset-serving/image-asset/README.md +0 -119
  138. jac_client/examples/asset-serving/image-asset/assets/burger.png +0 -0
  139. jac_client/examples/asset-serving/image-asset/package.json +0 -28
  140. jac_client/examples/asset-serving/image-asset/styles.css +0 -26
  141. jac_client/examples/asset-serving/image-asset/vite.config.js +0 -28
  142. jac_client/examples/asset-serving/import-alias/.babelrc +0 -9
  143. jac_client/examples/asset-serving/import-alias/README.md +0 -83
  144. jac_client/examples/asset-serving/import-alias/assets/burger.png +0 -0
  145. jac_client/examples/asset-serving/import-alias/package.json +0 -28
  146. jac_client/examples/asset-serving/import-alias/vite.config.js +0 -28
  147. jac_client/examples/basic/.babelrc +0 -9
  148. jac_client/examples/basic/README.md +0 -16
  149. jac_client/examples/basic/package.json +0 -27
  150. jac_client/examples/basic/vite.config.js +0 -27
  151. jac_client/examples/basic-auth/.babelrc +0 -9
  152. jac_client/examples/basic-auth/README.md +0 -16
  153. jac_client/examples/basic-auth/app.jac +0 -308
  154. jac_client/examples/basic-auth/package.json +0 -27
  155. jac_client/examples/basic-auth/vite.config.js +0 -27
  156. jac_client/examples/basic-auth-with-router/.babelrc +0 -9
  157. jac_client/examples/basic-auth-with-router/README.md +0 -60
  158. jac_client/examples/basic-auth-with-router/package.json +0 -28
  159. jac_client/examples/basic-auth-with-router/vite.config.js +0 -27
  160. jac_client/examples/basic-full-stack/.babelrc +0 -9
  161. jac_client/examples/basic-full-stack/README.md +0 -18
  162. jac_client/examples/basic-full-stack/package.json +0 -28
  163. jac_client/examples/basic-full-stack/vite.config.js +0 -27
  164. jac_client/examples/css-styling/js-styling/.babelrc +0 -9
  165. jac_client/examples/css-styling/js-styling/README.md +0 -183
  166. jac_client/examples/css-styling/js-styling/package.json +0 -28
  167. jac_client/examples/css-styling/js-styling/styles.js +0 -100
  168. jac_client/examples/css-styling/js-styling/vite.config.js +0 -27
  169. jac_client/examples/css-styling/material-ui/.babelrc +0 -9
  170. jac_client/examples/css-styling/material-ui/README.md +0 -16
  171. jac_client/examples/css-styling/material-ui/package.json +0 -32
  172. jac_client/examples/css-styling/material-ui/vite.config.js +0 -27
  173. jac_client/examples/css-styling/pure-css/.babelrc +0 -9
  174. jac_client/examples/css-styling/pure-css/README.md +0 -16
  175. jac_client/examples/css-styling/pure-css/package.json +0 -28
  176. jac_client/examples/css-styling/pure-css/styles.css +0 -111
  177. jac_client/examples/css-styling/pure-css/vite.config.js +0 -27
  178. jac_client/examples/css-styling/sass-example/.babelrc +0 -9
  179. jac_client/examples/css-styling/sass-example/README.md +0 -16
  180. jac_client/examples/css-styling/sass-example/package.json +0 -29
  181. jac_client/examples/css-styling/sass-example/styles.scss +0 -153
  182. jac_client/examples/css-styling/sass-example/vite.config.js +0 -27
  183. jac_client/examples/css-styling/styled-components/.babelrc +0 -9
  184. jac_client/examples/css-styling/styled-components/README.md +0 -16
  185. jac_client/examples/css-styling/styled-components/package.json +0 -29
  186. jac_client/examples/css-styling/styled-components/styled.js +0 -90
  187. jac_client/examples/css-styling/styled-components/vite.config.js +0 -27
  188. jac_client/examples/css-styling/tailwind-example/.babelrc +0 -9
  189. jac_client/examples/css-styling/tailwind-example/README.md +0 -16
  190. jac_client/examples/css-styling/tailwind-example/global.css +0 -1
  191. jac_client/examples/css-styling/tailwind-example/package.json +0 -30
  192. jac_client/examples/css-styling/tailwind-example/vite.config.js +0 -29
  193. jac_client/examples/full-stack-with-auth/.babelrc +0 -9
  194. jac_client/examples/full-stack-with-auth/README.md +0 -16
  195. jac_client/examples/full-stack-with-auth/package.json +0 -28
  196. jac_client/examples/full-stack-with-auth/vite.config.js +0 -29
  197. jac_client/examples/little-x/package.json +0 -23
  198. jac_client/examples/little-x/submit-button.jac +0 -8
  199. jac_client/examples/nested-folders/nested-advance/.babelrc +0 -9
  200. jac_client/examples/nested-folders/nested-advance/README.md +0 -77
  201. jac_client/examples/nested-folders/nested-advance/package.json +0 -29
  202. jac_client/examples/nested-folders/nested-advance/vite.config.js +0 -28
  203. jac_client/examples/nested-folders/nested-basic/.babelrc +0 -9
  204. jac_client/examples/nested-folders/nested-basic/README.md +0 -183
  205. jac_client/examples/nested-folders/nested-basic/app.js +0 -7
  206. jac_client/examples/nested-folders/nested-basic/package.json +0 -28
  207. jac_client/examples/nested-folders/nested-basic/vite.config.js +0 -27
  208. jac_client/examples/with-router/.babelrc +0 -9
  209. jac_client/examples/with-router/README.md +0 -17
  210. jac_client/examples/with-router/package.json +0 -28
  211. jac_client/examples/with-router/vite.config.js +0 -27
  212. jac_client/plugin/cli.py +0 -244
  213. jac_client/plugin/client.py +0 -152
  214. jac_client/plugin/client_runtime.jac +0 -234
  215. jac_client/plugin/vite_client_bundle.py +0 -503
  216. jac_client/tests/fixtures/js_import/utils.js +0 -21
  217. jac_client/tests/fixtures/package-lock.json +0 -329
  218. jac_client/tests/fixtures/package.json +0 -11
  219. jac_client/tests/test_asset_examples.py +0 -322
  220. jac_client/tests/test_cl.py +0 -530
  221. jac_client/tests/test_create_jac_app.py +0 -131
  222. jac_client/tests/test_nested_file.py +0 -374
  223. jac_client-0.2.3.dist-info/RECORD +0 -171
  224. jac_client-0.2.3.dist-info/entry_points.txt +0 -4
@@ -0,0 +1,5 @@
1
+ # worker.py
2
+
3
+
4
+ def handle_message() -> str:
5
+ return "✓ Message processed by Python Worker | Successfully handled by worker.py"
@@ -1,6 +1,6 @@
1
1
  cl import from antd { Button }
2
2
 
3
- cl def CustomButtonRoot() -> any {
3
+ cl def:pub CustomButtonRoot -> any {
4
4
  return <Button>
5
5
  Root Button
6
6
  </Button>;
@@ -0,0 +1,35 @@
1
+ # Category filter component
2
+ # Demonstrates: iteration with map, conditional classes, events
3
+
4
+ cl import from ..constants.categories { CATEGORIES, CATEGORY_LABELS, CATEGORY_COLORS }
5
+
6
+ cl {
7
+ # Filter buttons for categories
8
+ def:pub CategoryFilter(selectedCategory: str, onSelect: any) -> any {
9
+ # Add "ALL" to the beginning of categories
10
+ allCategories = ["ALL"].concat(CATEGORIES);
11
+
12
+ return <div className="category-filter">
13
+ <h3 className="filter-title">Filter by Category</h3>
14
+ <div className="filter-buttons">
15
+ {allCategories.map(lambda cat: str -> any {
16
+ isActive = selectedCategory == cat;
17
+ color = CATEGORY_COLORS[cat] if cat != "ALL" else "#374151";
18
+
19
+ return <button
20
+ key={cat}
21
+ className={("filter-btn active") if isActive else ("filter-btn")}
22
+ style={{
23
+ "borderColor": color,
24
+ "backgroundColor": (color) if isActive else ("transparent"),
25
+ "color": ("#fff") if isActive else (color)
26
+ }}
27
+ onClick={lambda: onSelect(cat)}
28
+ >
29
+ {(cat) if cat == "ALL" else (CATEGORY_LABELS[cat])}
30
+ </button>;
31
+ })}
32
+ </div>
33
+ </div>;
34
+ }
35
+ }
@@ -0,0 +1,13 @@
1
+ # Header component
2
+ # Demonstrates: simple component, CSS classes
3
+
4
+ cl {
5
+ def:pub Header() -> any {
6
+ return <header className="header">
7
+ <div className="header-content">
8
+ <h1 className="header-title">Budget Planner</h1>
9
+ <p className="header-subtitle">Track your income and expenses</p>
10
+ </div>
11
+ </header>;
12
+ }
13
+ }
@@ -0,0 +1,50 @@
1
+ # Profit Overview component - monthly profit snapshot
2
+ # Demonstrates: context usage, calculated displays, formatting
3
+
4
+ cl import from ..context.BudgetContext { useBudgetContext }
5
+ cl import from ..utils.formatters { formatCurrency }
6
+
7
+ cl {
8
+ def:pub ProfitOverview() -> any {
9
+ budget = useBudgetContext();
10
+
11
+ businessIncome = budget["businessIncome"];
12
+ businessExpenses = budget["businessExpenses"];
13
+ taxReserve = budget["taxReserve"];
14
+ netProfit = budget["netProfit"];
15
+
16
+ return <div className="profit-overview">
17
+ <h3 className="profit-title">Monthly Profit Snapshot</h3>
18
+
19
+ <div className="profit-breakdown">
20
+ <div className="profit-row income">
21
+ <span className="profit-label">Business Income</span>
22
+ <span className="profit-value positive">
23
+ +{formatCurrency(businessIncome)}
24
+ </span>
25
+ </div>
26
+
27
+ <div className="profit-row expense">
28
+ <span className="profit-label">Business Expenses</span>
29
+ <span className="profit-value negative">
30
+ -{formatCurrency(businessExpenses)}
31
+ </span>
32
+ </div>
33
+
34
+ <div className="profit-row tax">
35
+ <span className="profit-label">Tax Reserve (20%)</span>
36
+ <span className="profit-value negative">
37
+ -{formatCurrency(taxReserve)}
38
+ </span>
39
+ </div>
40
+
41
+ <div className="profit-row total">
42
+ <span className="profit-label">Net Profit</span>
43
+ <span className={("profit-value bold positive") if netProfit >= 0 else ("profit-value bold negative")}>
44
+ {("+") if netProfit > 0 else ("")}{formatCurrency(netProfit)}
45
+ </span>
46
+ </div>
47
+ </div>
48
+ </div>;
49
+ }
50
+ }
@@ -0,0 +1,53 @@
1
+ # Summary component - displays budget totals with business/personal breakdown
2
+ # Demonstrates: context usage, conditional rendering, ternary operator, 6-card layout
3
+
4
+ cl import from ..context.BudgetContext { useBudgetContext }
5
+ cl import from ..utils.formatters { formatCurrency }
6
+
7
+ cl {
8
+ def:pub Summary() -> any {
9
+ budget = useBudgetContext();
10
+
11
+ # Business/Personal breakdown
12
+ businessIncome = budget["businessIncome"];
13
+ businessExpenses = budget["businessExpenses"];
14
+ personalIncome = budget["personalIncome"];
15
+ personalExpenses = budget["personalExpenses"];
16
+ taxReserve = budget["taxReserve"];
17
+ netProfit = budget["netProfit"];
18
+
19
+ return <div className="summary">
20
+ <div className="summary-card business-income">
21
+ <span className="summary-label">Business Income</span>
22
+ <span className="summary-value">{formatCurrency(businessIncome)}</span>
23
+ </div>
24
+
25
+ <div className="summary-card business-expenses">
26
+ <span className="summary-label">Business Expenses</span>
27
+ <span className="summary-value">{formatCurrency(businessExpenses)}</span>
28
+ </div>
29
+
30
+ <div className="summary-card personal-income">
31
+ <span className="summary-label">Personal Income</span>
32
+ <span className="summary-value">{formatCurrency(personalIncome)}</span>
33
+ </div>
34
+
35
+ <div className="summary-card personal-expenses">
36
+ <span className="summary-label">Personal Expenses</span>
37
+ <span className="summary-value">{formatCurrency(personalExpenses)}</span>
38
+ </div>
39
+
40
+ <div className="summary-card tax-reserve">
41
+ <span className="summary-label">Tax Reserve (20%)</span>
42
+ <span className="summary-value">{formatCurrency(taxReserve)}</span>
43
+ </div>
44
+
45
+ <div className={("summary-card net-profit positive") if netProfit >= 0 else ("summary-card net-profit negative")}>
46
+ <span className="summary-label">Net Profit</span>
47
+ <span className="summary-value">
48
+ {("+") if netProfit > 0 else ("")}{formatCurrency(netProfit)}
49
+ </span>
50
+ </div>
51
+ </div>;
52
+ }
53
+ }
@@ -0,0 +1,158 @@
1
+ # Transaction form component
2
+ # Demonstrates: form handling, useState, events, select options
3
+
4
+ cl import from ..context.BudgetContext { useBudgetContext }
5
+ cl import from ..constants.categories { CATEGORIES, CATEGORY_LABELS }
6
+ cl import from ..constants.clients { CLIENTS }
7
+
8
+ cl {
9
+ def:pub TransactionForm() -> any {
10
+ [description, setDescription] = useState("");
11
+ [amount, setAmount] = useState("");
12
+ [category, setCategory] = useState("OTHER");
13
+ [txType, setTxType] = useState("expense");
14
+ [isBusiness, setIsBusiness] = useState(false);
15
+ [clientName, setClientName] = useState("");
16
+ budget = useBudgetContext();
17
+
18
+ def handleSubmit(e: any) -> None {
19
+ e.preventDefault();
20
+
21
+ # Validate inputs
22
+ trimmedDesc = description.trim();
23
+ if trimmedDesc == "" or amount == "" {
24
+ return;
25
+ }
26
+
27
+ parsedAmount = parseFloat(amount);
28
+ if isNaN(parsedAmount) or parsedAmount <= 0 {
29
+ return;
30
+ }
31
+
32
+ # Add the transaction
33
+ budget["addTransaction"](trimmedDesc, parsedAmount, category, txType, isBusiness, clientName);
34
+
35
+ # Reset form
36
+ setDescription("");
37
+ setAmount("");
38
+ setCategory("OTHER");
39
+ setIsBusiness(false);
40
+ setClientName("");
41
+ }
42
+
43
+ # Filter categories based on type (income only has INCOME category)
44
+ availableCategories = CATEGORIES.filter(lambda cat: str -> bool {
45
+ if txType == "income" {
46
+ return cat == "INCOME";
47
+ }
48
+ return cat != "INCOME";
49
+ });
50
+
51
+ # Show client dropdown only for business income
52
+ showClientDropdown = (txType == "income" and isBusiness);
53
+
54
+ return <form className="transaction-form" onSubmit={handleSubmit}>
55
+ <div className="form-row">
56
+ <div className="form-group type-toggle">
57
+ <button
58
+ type="button"
59
+ className={("toggle-btn active") if txType == "expense" else ("toggle-btn")}
60
+ onClick={lambda: setTxType("expense")}
61
+ >
62
+ Expense
63
+ </button>
64
+ <button
65
+ type="button"
66
+ className={("toggle-btn active income") if txType == "income" else ("toggle-btn")}
67
+ onClick={lambda: setTxType("income")}
68
+ >
69
+ Income
70
+ </button>
71
+ </div>
72
+
73
+ <div className="form-group business-toggle">
74
+ <button
75
+ type="button"
76
+ className={("toggle-btn active") if isBusiness else ("toggle-btn")}
77
+ onClick={lambda: setIsBusiness(true)}
78
+ >
79
+ Business
80
+ </button>
81
+ <button
82
+ type="button"
83
+ className={("toggle-btn active") if not isBusiness else ("toggle-btn")}
84
+ onClick={lambda: setIsBusiness(false)}
85
+ >
86
+ Personal
87
+ </button>
88
+ </div>
89
+ </div>
90
+
91
+ <div className="form-row">
92
+ <div className="form-group">
93
+ <label htmlFor="description">Description</label>
94
+ <input
95
+ id="description"
96
+ type="text"
97
+ value={description}
98
+ onChange={lambda e: any -> None { setDescription(e.target.value); }}
99
+ placeholder="Enter description..."
100
+ className="form-input"
101
+ />
102
+ </div>
103
+
104
+ <div className="form-group">
105
+ <label htmlFor="amount">Amount</label>
106
+ <input
107
+ id="amount"
108
+ type="number"
109
+ value={amount}
110
+ onChange={lambda e: any -> None { setAmount(e.target.value); }}
111
+ placeholder="0.00"
112
+ min="0"
113
+ step="0.01"
114
+ className="form-input"
115
+ />
116
+ </div>
117
+
118
+ <div className="form-group">
119
+ <label htmlFor="category">Category</label>
120
+ <select
121
+ id="category"
122
+ value={category}
123
+ onChange={lambda e: any -> None { setCategory(e.target.value); }}
124
+ className="form-select"
125
+ >
126
+ {availableCategories.map(lambda cat: str -> any {
127
+ return <option key={cat} value={cat}>
128
+ {CATEGORY_LABELS[cat]}
129
+ </option>;
130
+ })}
131
+ </select>
132
+ </div>
133
+
134
+ {showClientDropdown and <div className="form-group">
135
+ <label htmlFor="client">Client</label>
136
+ <select
137
+ id="client"
138
+ value={clientName}
139
+ onChange={lambda e: any -> None { setClientName(e.target.value); }}
140
+ className="form-select"
141
+ >
142
+ <option value="">Select Client (Optional)</option>
143
+ {CLIENTS.map(lambda client: str -> any {
144
+ return <option key={client} value={client}>{client}</option>;
145
+ })}
146
+ </select>
147
+ </div>}
148
+
149
+ <div className="form-group">
150
+ <label>Action</label>
151
+ <button type="submit" className="submit-btn">
152
+ Add {(txType[0].toUpperCase() + txType.slice(1))}
153
+ </button>
154
+ </div>
155
+ </div>
156
+ </form>;
157
+ }
158
+ }
@@ -0,0 +1,55 @@
1
+ # Single transaction item component
2
+ # Demonstrates: NEW PROPS PATTERN (direct parameters, NOT props dict)
3
+
4
+ cl import from ..utils.formatters { formatCurrency, formatDate }
5
+ cl import from ..constants.categories { CATEGORY_COLORS, CATEGORY_LABELS }
6
+
7
+ cl {
8
+ # NEW WAY: Direct parameters instead of props: dict
9
+ def:pub TransactionItem(
10
+ id: str,
11
+ description: str,
12
+ amount: float,
13
+ category: str,
14
+ txType: str,
15
+ date: str,
16
+ isBusiness: bool,
17
+ clientName: any,
18
+ onDelete: any
19
+ ) -> any {
20
+ isIncome = txType == "income";
21
+ color = CATEGORY_COLORS[category];
22
+ label = CATEGORY_LABELS[category];
23
+
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>}
36
+ </span>
37
+ <span className="tx-date">{formatDate(date)}</span>
38
+ </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>;
54
+ }
55
+ }
@@ -0,0 +1,37 @@
1
+ # Transaction list component
2
+ # Demonstrates: rendering lists with map, passing props to child components
3
+
4
+ cl import from .TransactionItem { TransactionItem }
5
+
6
+ cl {
7
+ # Props: transactions list and delete handler
8
+ def:pub TransactionList(transactions: list, onDelete: any) -> any {
9
+ 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>;
14
+ }
15
+
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>;
36
+ }
37
+ }
@@ -1,6 +1,6 @@
1
1
  cl import from antd { Button }
2
2
 
3
- cl def CustomButton() -> any {
3
+ cl def:pub CustomButton -> any {
4
4
  return <Button>
5
5
  Nested Button
6
6
  </Button>;
@@ -0,0 +1,132 @@
1
+ cl import from "@jac-client/utils" {
2
+
3
+ Link,
4
+ useNavigate,
5
+ useLocation,
6
+ jacLogout,
7
+ jacIsLoggedIn
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
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
+ }
66
+
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"}}
87
+ >
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"}}
126
+ >
127
+ {authButtons}
128
+ </div>
129
+ </div>
130
+ </nav>;
131
+ }
132
+ }
@@ -0,0 +1,37 @@
1
+ # Category constants and enums for Budget Planner
2
+ # Demonstrates: enum:pub, glob:pub exports
3
+
4
+ cl {
5
+ # Category enum - demonstrates enum:pub export
6
+ enum:pub Category {
7
+ INCOME,
8
+ FOOD,
9
+ TRANSPORT,
10
+ UTILITIES,
11
+ ENTERTAINMENT,
12
+ OTHER
13
+ }
14
+
15
+ # Category colors - demonstrates glob:pub export
16
+ glob:pub CATEGORY_COLORS: dict = {
17
+ "INCOME": "#10b981",
18
+ "FOOD": "#f59e0b",
19
+ "TRANSPORT": "#3b82f6",
20
+ "UTILITIES": "#8b5cf6",
21
+ "ENTERTAINMENT": "#ec4899",
22
+ "OTHER": "#6b7280"
23
+ };
24
+
25
+ # Category labels for display
26
+ 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
+ };
34
+
35
+ # All category keys as a list
36
+ glob:pub CATEGORIES: list = ["INCOME", "FOOD", "TRANSPORT", "UTILITIES", "ENTERTAINMENT", "OTHER"];
37
+ }
@@ -0,0 +1,13 @@
1
+ # Client list for freelancer income tracking
2
+ # Demonstrates: glob exports, array constants
3
+
4
+ cl {
5
+ glob:pub CLIENTS = [
6
+ "Acme Corp",
7
+ "TechStart Inc",
8
+ "Design Studio Co",
9
+ "Marketing Agency",
10
+ "Freelance Marketplace",
11
+ "Other"
12
+ ];
13
+ }
@@ -0,0 +1,28 @@
1
+ # Budget Context for global state management
2
+ # Demonstrates: createContext, useContext, Context.Provider
3
+
4
+ cl import from react { createContext, useContext }
5
+ cl import from ..hooks.useBudget { useBudget }
6
+
7
+ cl {
8
+ # Create the context - demonstrates glob for context
9
+ glob:pub BudgetContext = createContext(None);
10
+
11
+ # Provider component - wraps app and provides budget state
12
+ def:pub BudgetProvider(children: any) -> any {
13
+ budget = useBudget();
14
+
15
+ return <BudgetContext.Provider value={budget}>
16
+ {children}
17
+ </BudgetContext.Provider>;
18
+ }
19
+
20
+ # Custom hook to access budget context
21
+ def:pub useBudgetContext() -> dict {
22
+ context = useContext(BudgetContext);
23
+ if context == None {
24
+ console.error("useBudgetContext must be used within BudgetProvider");
25
+ }
26
+ return context;
27
+ }
28
+ }