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,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
  }
@@ -1,7 +1,10 @@
1
1
  # Category filter component
2
2
  # Demonstrates: iteration with map, conditional classes, events
3
-
4
- cl import from ..constants.categories { CATEGORIES, CATEGORY_LABELS, CATEGORY_COLORS }
3
+ cl import from ..constants.categories {
4
+ CATEGORIES,
5
+ CATEGORY_LABELS,
6
+ CATEGORY_COLORS
7
+ }
5
8
 
6
9
  cl {
7
10
  # Filter buttons for categories
@@ -9,27 +12,36 @@ cl {
9
12
  # Add "ALL" to the beginning of categories
10
13
  allCategories = ["ALL"].concat(CATEGORIES);
11
14
 
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>;
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>;
34
46
  }
35
47
  }
@@ -1,13 +1,17 @@
1
1
  # Header component
2
2
  # Demonstrates: simple component, CSS classes
3
-
4
3
  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>;
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>;
12
16
  }
13
17
  }
@@ -1,11 +1,12 @@
1
1
  # Profit Overview component - monthly profit snapshot
2
2
  # Demonstrates: context usage, calculated displays, formatting
3
-
4
- cl import from ..context.BudgetContext { useBudgetContext }
3
+ cl import from ..context.BudgetContext {
4
+ useBudgetContext
5
+ }
5
6
  cl import from ..utils.formatters { formatCurrency }
6
7
 
7
8
  cl {
8
- def:pub ProfitOverview() -> any {
9
+ def:pub ProfitOverview -> any {
9
10
  budget = useBudgetContext();
10
11
 
11
12
  businessIncome = budget["businessIncome"];
@@ -13,38 +14,51 @@ cl {
13
14
  taxReserve = budget["taxReserve"];
14
15
  netProfit = budget["netProfit"];
15
16
 
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>
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>
46
61
  </div>
47
- </div>
48
- </div>;
62
+ </div>;
49
63
  }
50
64
  }
@@ -1,11 +1,12 @@
1
1
  # Summary component - displays budget totals with business/personal breakdown
2
2
  # Demonstrates: context usage, conditional rendering, ternary operator, 6-card layout
3
-
4
- cl import from ..context.BudgetContext { useBudgetContext }
3
+ cl import from ..context.BudgetContext {
4
+ useBudgetContext
5
+ }
5
6
  cl import from ..utils.formatters { formatCurrency }
6
7
 
7
8
  cl {
8
- def:pub Summary() -> any {
9
+ def:pub Summary -> any {
9
10
  budget = useBudgetContext();
10
11
 
11
12
  # Business/Personal breakdown
@@ -16,38 +17,60 @@ cl {
16
17
  taxReserve = budget["taxReserve"];
17
18
  netProfit = budget["netProfit"];
18
19
 
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>;
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>;
52
75
  }
53
76
  }
@@ -1,12 +1,13 @@
1
1
  # Transaction form component
2
2
  # Demonstrates: form handling, useState, events, select options
3
-
4
- cl import from ..context.BudgetContext { useBudgetContext }
3
+ cl import from ..context.BudgetContext {
4
+ useBudgetContext
5
+ }
5
6
  cl import from ..constants.categories { CATEGORIES, CATEGORY_LABELS }
6
7
  cl import from ..constants.clients { CLIENTS }
7
8
 
8
9
  cl {
9
- def:pub TransactionForm() -> any {
10
+ def:pub TransactionForm -> any {
10
11
  [description, setDescription] = useState("");
11
12
  [amount, setAmount] = useState("");
12
13
  [category, setCategory] = useState("OTHER");
@@ -30,7 +31,9 @@ cl {
30
31
  }
31
32
 
32
33
  # Add the transaction
33
- budget["addTransaction"](trimmedDesc, parsedAmount, category, txType, isBusiness, clientName);
34
+ budget["addTransaction"](
35
+ trimmedDesc, parsedAmount, category, txType, isBusiness, clientName
36
+ );
34
37
 
35
38
  # Reset form
36
39
  setDescription("");
@@ -41,118 +44,145 @@ cl {
41
44
  }
42
45
 
43
46
  # Filter categories based on type (income only has INCOME category)
44
- availableCategories = CATEGORIES.filter(lambda cat: str -> bool {
45
- if txType == "income" {
47
+ availableCategories = CATEGORIES.filter(
48
+ lambda cat: str -> bool { if txType == "income" {
46
49
  return cat == "INCOME";
47
- }
48
- return cat != "INCOME";
49
- });
50
+ }return cat != "INCOME"; }
51
+ );
50
52
 
51
53
  # 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
- />
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>
116
99
  </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>
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>
154
185
  </div>
155
- </div>
156
- </form>;
186
+ </form>;
157
187
  }
158
188
  }