jac-client 0.2.9__tar.gz → 0.2.11__tar.gz
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-0.2.9 → jac_client-0.2.11}/PKG-INFO +5 -5
- {jac_client-0.2.9 → jac_client-0.2.11}/README.md +3 -3
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/button.jac +4 -3
- jac_client-0.2.11/jac_client/examples/all-in-one/components/CategoryFilter.jac +47 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/Header.jac +17 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/ProfitOverview.jac +64 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/Summary.jac +76 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/TransactionForm.jac +188 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/TransactionItem.jac +62 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/TransactionList.jac +44 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/button.jac +8 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/components/navigation.jac +126 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/constants/categories.jac +36 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/constants/clients.jac +12 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/context/BudgetContext.jac +9 -6
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/hooks/useBudget.jac +18 -12
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/hooks/useLocalStorage.jac +14 -13
- jac_client-0.2.11/jac_client/examples/all-in-one/main.jac +542 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/pages/BudgetPlanner.jac +19 -12
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/pages/FeaturesTest.jac +31 -15
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/LandingPage.jac +124 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +65 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +675 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/loginPage.jac +127 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/nestedDemo.jac +54 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/notFound.jac +18 -0
- jac_client-0.2.11/jac_client/examples/all-in-one/pages/signupPage.jac +127 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/utils/formatters.jac +5 -8
- jac_client-0.2.11/jac_client/examples/asset-serving/css-with-image/main.jac +92 -0
- jac_client-0.2.11/jac_client/examples/asset-serving/image-asset/main.jac +56 -0
- jac_client-0.2.11/jac_client/examples/asset-serving/import-alias/main.jac +109 -0
- jac_client-0.2.11/jac_client/examples/basic/main.jac +23 -0
- jac_client-0.2.11/jac_client/examples/basic-auth/main.jac +363 -0
- jac_client-0.2.11/jac_client/examples/basic-auth-with-router/main.jac +451 -0
- jac_client-0.2.11/jac_client/examples/basic-full-stack/main.jac +362 -0
- jac_client-0.2.11/jac_client/examples/css-styling/js-styling/main.jac +63 -0
- jac_client-0.2.11/jac_client/examples/css-styling/material-ui/main.jac +122 -0
- jac_client-0.2.11/jac_client/examples/css-styling/pure-css/main.jac +55 -0
- jac_client-0.2.11/jac_client/examples/css-styling/sass-example/main.jac +55 -0
- jac_client-0.2.11/jac_client/examples/css-styling/styled-components/main.jac +62 -0
- jac_client-0.2.11/jac_client/examples/css-styling/tailwind-example/main.jac +74 -0
- jac_client-0.2.11/jac_client/examples/full-stack-with-auth/main.jac +696 -0
- jac_client-0.2.11/jac_client/examples/little-x/main.jac +681 -0
- jac_client-0.2.11/jac_client/examples/little-x/src/submit-button.jac +17 -0
- jac_client-0.2.11/jac_client/examples/nested-folders/nested-advance/main.jac +26 -0
- jac_client-0.2.11/jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +9 -0
- jac_client-0.2.11/jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +15 -0
- jac_client-0.2.11/jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +40 -0
- jac_client-0.2.11/jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +19 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/nested-folders/nested-basic/main.jac +7 -5
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/nested-folders/nested-basic/src/button.jac +4 -3
- jac_client-0.2.11/jac_client/examples/nested-folders/nested-basic/src/components/button.jac +8 -0
- jac_client-0.2.11/jac_client/examples/ts-support/main.jac +35 -0
- jac_client-0.2.11/jac_client/examples/with-router/main.jac +286 -0
- jac_client-0.2.11/jac_client/plugin/cli.jac +584 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/client_runtime.cl.jac +5 -3
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/impl/client_runtime.impl.jac +1 -1
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/plugin_config.jac +53 -99
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/__init__.jac +0 -2
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/compiler.jac +0 -1
- jac_client-0.2.11/jac_client/plugin/src/desktop_config.jac +31 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/compiler.impl.jac +49 -17
- jac_client-0.2.11/jac_client/plugin/src/impl/desktop_config.impl.jac +191 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/jac_to_js.impl.jac +5 -1
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/package_installer.impl.jac +20 -20
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/vite_bundler.impl.jac +121 -75
- jac_client-0.2.11/jac_client/plugin/src/targets/desktop/sidecar/main.py +144 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/desktop_target.jac +37 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/impl/desktop_target.impl.jac +2347 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/impl/registry.impl.jac +64 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/impl/web_target.impl.jac +157 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/register.jac +21 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/registry.jac +87 -0
- jac_client-0.2.11/jac_client/plugin/src/targets/web_target.jac +35 -0
- jac_client-0.2.11/jac_client/plugin/utils/__init__.jac +3 -0
- jac_client-0.2.11/jac_client/plugin/utils/bun_installer.jac +16 -0
- jac_client-0.2.11/jac_client/plugin/utils/impl/bun_installer.impl.jac +99 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/templates/fullstack.jacpack +1 -1
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/conftest.py +56 -41
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/spawn_test/app.jac +49 -52
- jac_client-0.2.11/jac_client/tests/fixtures/with-ts/app.jac +35 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/test_cli.py +3 -6
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/test_helpers.py +11 -18
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/test_it.py +1 -1
- jac_client-0.2.11/jac_client/tests/test_it_desktop.py +891 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client.egg-info/PKG-INFO +5 -5
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client.egg-info/SOURCES.txt +13 -4
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client.egg-info/requires.txt +1 -1
- {jac_client-0.2.9 → jac_client-0.2.11}/pyproject.toml +2 -2
- jac_client-0.2.9/jac_client/examples/all-in-one/components/CategoryFilter.jac +0 -35
- jac_client-0.2.9/jac_client/examples/all-in-one/components/Header.jac +0 -13
- jac_client-0.2.9/jac_client/examples/all-in-one/components/ProfitOverview.jac +0 -50
- jac_client-0.2.9/jac_client/examples/all-in-one/components/Summary.jac +0 -53
- jac_client-0.2.9/jac_client/examples/all-in-one/components/TransactionForm.jac +0 -158
- jac_client-0.2.9/jac_client/examples/all-in-one/components/TransactionItem.jac +0 -55
- jac_client-0.2.9/jac_client/examples/all-in-one/components/TransactionList.jac +0 -37
- jac_client-0.2.9/jac_client/examples/all-in-one/components/button.jac +0 -7
- jac_client-0.2.9/jac_client/examples/all-in-one/components/navigation.jac +0 -132
- jac_client-0.2.9/jac_client/examples/all-in-one/constants/categories.jac +0 -37
- jac_client-0.2.9/jac_client/examples/all-in-one/constants/clients.jac +0 -13
- jac_client-0.2.9/jac_client/examples/all-in-one/main.jac +0 -573
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/LandingPage.jac +0 -101
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/budget_planner_ui.cl.jac +0 -70
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/features_test_ui.cl.jac +0 -563
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/loginPage.jac +0 -132
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/nestedDemo.jac +0 -61
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/notFound.jac +0 -19
- jac_client-0.2.9/jac_client/examples/all-in-one/pages/signupPage.jac +0 -133
- jac_client-0.2.9/jac_client/examples/asset-serving/css-with-image/main.jac +0 -88
- jac_client-0.2.9/jac_client/examples/asset-serving/image-asset/main.jac +0 -55
- jac_client-0.2.9/jac_client/examples/asset-serving/import-alias/main.jac +0 -111
- jac_client-0.2.9/jac_client/examples/basic/main.jac +0 -21
- jac_client-0.2.9/jac_client/examples/basic-auth/main.jac +0 -371
- jac_client-0.2.9/jac_client/examples/basic-auth-with-router/main.jac +0 -464
- jac_client-0.2.9/jac_client/examples/basic-full-stack/main.jac +0 -359
- jac_client-0.2.9/jac_client/examples/css-styling/js-styling/main.jac +0 -84
- jac_client-0.2.9/jac_client/examples/css-styling/material-ui/main.jac +0 -122
- jac_client-0.2.9/jac_client/examples/css-styling/pure-css/main.jac +0 -64
- jac_client-0.2.9/jac_client/examples/css-styling/sass-example/main.jac +0 -64
- jac_client-0.2.9/jac_client/examples/css-styling/styled-components/main.jac +0 -71
- jac_client-0.2.9/jac_client/examples/css-styling/tailwind-example/main.jac +0 -63
- jac_client-0.2.9/jac_client/examples/full-stack-with-auth/main.jac +0 -722
- jac_client-0.2.9/jac_client/examples/little-x/main.jac +0 -719
- jac_client-0.2.9/jac_client/examples/little-x/src/submit-button.jac +0 -16
- jac_client-0.2.9/jac_client/examples/nested-folders/nested-advance/main.jac +0 -35
- jac_client-0.2.9/jac_client/examples/nested-folders/nested-advance/src/ButtonRoot.jac +0 -11
- jac_client-0.2.9/jac_client/examples/nested-folders/nested-advance/src/level1/ButtonSecondL.jac +0 -19
- jac_client-0.2.9/jac_client/examples/nested-folders/nested-advance/src/level1/Card.jac +0 -43
- jac_client-0.2.9/jac_client/examples/nested-folders/nested-advance/src/level1/level2/ButtonThirdL.jac +0 -25
- jac_client-0.2.9/jac_client/examples/nested-folders/nested-basic/src/components/button.jac +0 -7
- jac_client-0.2.9/jac_client/examples/ts-support/main.jac +0 -35
- jac_client-0.2.9/jac_client/examples/with-router/main.jac +0 -323
- jac_client-0.2.9/jac_client/plugin/cli.jac +0 -231
- jac_client-0.2.9/jac_client/plugin/src/babel_processor.jac +0 -18
- jac_client-0.2.9/jac_client/plugin/src/impl/babel_processor.impl.jac +0 -89
- jac_client-0.2.9/jac_client/plugin/utils/__init__.jac +0 -1
- jac_client-0.2.9/jac_client/plugin/utils/impl/node_installer.impl.jac +0 -249
- jac_client-0.2.9/jac_client/plugin/utils/node_installer.jac +0 -41
- jac_client-0.2.9/jac_client/tests/fixtures/with-ts/app.jac +0 -35
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/examples/all-in-one/assets/workers/worker.py +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/client.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/impl/client.impl.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/impl/vite_client_bundle.impl.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/asset_processor.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/config_loader.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/asset_processor.impl.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/config_loader.impl.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/impl/import_processor.impl.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/import_processor.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/jac_to_js.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/package_installer.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/src/vite_bundler.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/plugin/vite_client_bundle.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/templates/client.jacpack +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/__init__.py +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/basic-app/app.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/cl_file/app.cl.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/cl_file/app.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/client_app_with_antd/app.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/js_import/app.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/relative_import/app.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/relative_import/button.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/fixtures/test_fragments_spread/app.jac +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client/tests/test_e2e.py +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client.egg-info/dependency_links.txt +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client.egg-info/entry_points.txt +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/jac_client.egg-info/top_level.txt +0 -0
- {jac_client-0.2.9 → jac_client-0.2.11}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jac-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.11
|
|
4
4
|
Summary: Build full-stack web applications with Jac - one language for frontend and backend.
|
|
5
5
|
Author-email: Jason Mars <jason@mars.ninja>
|
|
6
6
|
Maintainer-email: Jason Mars <jason@mars.ninja>
|
|
@@ -11,7 +11,7 @@ Project-URL: Documentation, https://jac-lang.org
|
|
|
11
11
|
Keywords: jac,jaclang,jaseci,frontend,full-stack,web-development
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
|
-
Requires-Dist: jaclang>=0.9.
|
|
14
|
+
Requires-Dist: jaclang>=0.9.11
|
|
15
15
|
Provides-Extra: dev
|
|
16
16
|
Requires-Dist: python-dotenv==1.0.1; extra == "dev"
|
|
17
17
|
Requires-Dist: pytest==8.3.5; extra == "dev"
|
|
@@ -56,7 +56,7 @@ Visit `http://localhost:8000` to see your app! (The `app` component is served at
|
|
|
56
56
|
|
|
57
57
|
You can also access the app at `http://localhost:8000/cl/app`.
|
|
58
58
|
|
|
59
|
-
> **Note**: The `--
|
|
59
|
+
> **Note**: The `--use client` flag creates a client-side project with an organized folder structure. Without it, `jac create` creates a standard Jac project.
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
@@ -113,7 +113,7 @@ cl {
|
|
|
113
113
|
```jac
|
|
114
114
|
# useState is auto-injected, only import useEffect
|
|
115
115
|
cl import from react { useEffect }
|
|
116
|
-
cl import from '@jac
|
|
116
|
+
cl import from '@jac/runtime' { jacSpawn }
|
|
117
117
|
|
|
118
118
|
# Backend: Jac nodes and walkers
|
|
119
119
|
node Todo {
|
|
@@ -164,7 +164,7 @@ cl {
|
|
|
164
164
|
## Requirements
|
|
165
165
|
|
|
166
166
|
- Python: 3.12+
|
|
167
|
-
-
|
|
167
|
+
- Bun: For package management and Vite bundling ([install](https://bun.sh))
|
|
168
168
|
- Jac Language: `jaclang` (installed automatically)
|
|
169
169
|
|
|
170
170
|
---
|
|
@@ -38,7 +38,7 @@ Visit `http://localhost:8000` to see your app! (The `app` component is served at
|
|
|
38
38
|
|
|
39
39
|
You can also access the app at `http://localhost:8000/cl/app`.
|
|
40
40
|
|
|
41
|
-
> **Note**: The `--
|
|
41
|
+
> **Note**: The `--use client` flag creates a client-side project with an organized folder structure. Without it, `jac create` creates a standard Jac project.
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
@@ -95,7 +95,7 @@ cl {
|
|
|
95
95
|
```jac
|
|
96
96
|
# useState is auto-injected, only import useEffect
|
|
97
97
|
cl import from react { useEffect }
|
|
98
|
-
cl import from '@jac
|
|
98
|
+
cl import from '@jac/runtime' { jacSpawn }
|
|
99
99
|
|
|
100
100
|
# Backend: Jac nodes and walkers
|
|
101
101
|
node Todo {
|
|
@@ -146,7 +146,7 @@ cl {
|
|
|
146
146
|
## Requirements
|
|
147
147
|
|
|
148
148
|
- Python: 3.12+
|
|
149
|
-
-
|
|
149
|
+
- Bun: For package management and Vite bundling ([install](https://bun.sh))
|
|
150
150
|
- Jac Language: `jaclang` (installed automatically)
|
|
151
151
|
|
|
152
152
|
---
|
|
@@ -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
|
+
}
|