zrb 1.2.2__py3-none-any.whl → 1.3.0__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 (33) hide show
  1. zrb/builtin/llm/llm_chat.py +42 -6
  2. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +28 -6
  3. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/gateway/view/content/my-module/my-entity.html +206 -178
  4. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +3 -1
  5. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +18 -1
  6. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +4 -0
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +20 -11
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +17 -2
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +4 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +19 -11
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +209 -180
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +362 -0
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +377 -0
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +68 -13
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +50 -29
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +3 -1
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +6 -5
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -3
  19. zrb/input/any_input.py +5 -0
  20. zrb/input/base_input.py +6 -0
  21. zrb/input/bool_input.py +2 -0
  22. zrb/input/float_input.py +2 -0
  23. zrb/input/int_input.py +2 -0
  24. zrb/input/option_input.py +2 -0
  25. zrb/input/password_input.py +2 -0
  26. zrb/input/text_input.py +2 -0
  27. zrb/runner/cli.py +1 -1
  28. zrb/runner/common_util.py +3 -3
  29. zrb/runner/web_route/task_input_api_route.py +1 -1
  30. {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/METADATA +84 -17
  31. {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/RECORD +33 -33
  32. {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/WHEEL +0 -0
  33. {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,362 @@
1
+ <link rel="stylesheet" href="/static/crud/style.css">
2
+
3
+ <main id="crud-app"
4
+ class="container"
5
+ data-page-size='{{ page_size | tojson }}'
6
+ data-page='{{ page | tojson }}'
7
+ data-sort='{{ sort | tojson }}'
8
+ data-filter='{{ filter | tojson }}'
9
+ data-allow-create='{{ allow_create | tojson }}'
10
+ data-allow-update='{{ allow_update | tojson }}'
11
+ data-allow-delete='{{ allow_delete | tojson }}'>
12
+ <article>
13
+ <h1>Role</h1>
14
+
15
+ <fieldset id="crud-table-fieldset" role="group" class="grid">
16
+ <input id="crud-filter-input" placeholder="🔍 Filter" aria-label="Search" />
17
+ <button id="crud-search-button">🔍 Search</button>
18
+ {% if allow_create %}
19
+ <button id="crud-show-create-button" class="contrast">➕ Add</button>
20
+ {% endif %}
21
+ </fieldset>
22
+
23
+ <div id="crud-table-container">
24
+ <table id="crud-table" class="striped">
25
+ <thead>
26
+ <tr>
27
+ <th scope="col">ID</th>
28
+ <th scope="col">Name</th>
29
+ <th scope="col">Description</th>
30
+ <th scope="col">Permissions</th>
31
+ {% if allow_update or allow_delete %}
32
+ <th scope="col">Actions</th>
33
+ {% endif %}
34
+ </tr>
35
+ </thead>
36
+ <tbody></tbody>
37
+ </table>
38
+ </div>
39
+ <div id="crud-pagination"></div>
40
+
41
+ {% if allow_create %}
42
+ <dialog id="crud-create-form-dialog">
43
+ <article>
44
+ <h2>New Role</h2>
45
+ <form id="crud-create-form">
46
+ <label>
47
+ Name:
48
+ <input type="text" name="name" required>
49
+ </label>
50
+ <label>
51
+ Description:
52
+ <input type="text" name="description" required>
53
+ </label>
54
+ <label>
55
+ Permission Names:
56
+ <textarea name="permission_names" required>[]</textarea>
57
+ </label>
58
+ <footer>
59
+ <button id="crud-create-button">➕ Save</button>
60
+ <button id="crud-cancel-create-button" class="secondary">❌ Cancel</button>
61
+ </footer>
62
+ </form>
63
+ </article>
64
+ </dialog>
65
+ {% endif %}
66
+
67
+ {% if allow_update %}
68
+ <dialog id="crud-update-form-dialog">
69
+ <article>
70
+ <h2>Update Role</h2>
71
+ <form id="crud-update-form">
72
+ <label>
73
+ Name:
74
+ <input type="text" name="name" required>
75
+ </label>
76
+ <label>
77
+ Description:
78
+ <input type="text" name="description" required>
79
+ </label>
80
+ <label>
81
+ Permission Names:
82
+ <textarea name="permission_names" required></textarea>
83
+ </label>
84
+ <footer>
85
+ <button id="crud-update-button">✏️ Save</button>
86
+ <button id="crud-cancel-update-button" class="secondary">❌ Cancel</button>
87
+ </footer>
88
+ </form>
89
+ </article>
90
+ </dialog>
91
+ {% endif %}
92
+
93
+ {% if allow_delete %}
94
+ <dialog id="crud-delete-form-dialog">
95
+ <article>
96
+ <h2>Delete Role</h2>
97
+ <form id="crud-delete-form">
98
+ <label>
99
+ Name:
100
+ <input type="text" name="name" readonly>
101
+ </label>
102
+ <label>
103
+ Description:
104
+ <input type="text" name="description" readonly>
105
+ </label>
106
+ <label>
107
+ Permission Names:
108
+ <textarea name="permission_names" readonly></textarea>
109
+ </label>
110
+ <footer>
111
+ <button id="crud-cancel-delete-button" class="secondary">❌ Cancel</button>
112
+ <button id="crud-delete-button">🗑️ Delete</button>
113
+ </footer>
114
+ </form>
115
+ </article>
116
+ </dialog>
117
+ {% endif %}
118
+
119
+ <dialog id="crud-alert-dialog">
120
+ <article>
121
+ <h2 id="crud-alert-title">Error</h2>
122
+ <pre id="crud-alert-message"></pre>
123
+ <footer>
124
+ <button id="crud-alert-close-button">Close</button>
125
+ </footer>
126
+ </article>
127
+ </dialog>
128
+ </article>
129
+ </main>
130
+
131
+ <script src="/static/crud/util.js"></script>
132
+ <script>
133
+ class CrudApp {
134
+ constructor(apiUrl, initialState) {
135
+ this.apiUrl = apiUrl;
136
+ this.state = { ...initialState };
137
+ this.init();
138
+ }
139
+
140
+ init() {
141
+ // Cache common elements
142
+ this.filterInput = document.getElementById("crud-filter-input");
143
+ this.searchButton = document.getElementById("crud-search-button");
144
+ this.filterInput.value = this.state.filter;
145
+
146
+ this.filterInput.addEventListener("change", (e) => this.applySearch(e));
147
+ this.searchButton.addEventListener("click", (e) => this.applySearch(e));
148
+
149
+ // Attach optional events if elements exist
150
+ this.attachEvent("crud-show-create-button", this.showCreateForm.bind(this));
151
+ this.attachEvent("crud-create-button", this.createRow.bind(this));
152
+ this.attachEvent("crud-cancel-create-button", this.hideCreateForm.bind(this));
153
+ this.attachEvent("crud-update-button", this.updateRow.bind(this));
154
+ this.attachEvent("crud-cancel-update-button", this.hideUpdateForm.bind(this));
155
+ this.attachEvent("crud-delete-button", this.deleteRow.bind(this));
156
+ this.attachEvent("crud-cancel-delete-button", this.hideDeleteForm.bind(this));
157
+ this.attachEvent("crud-alert-close-button", this.hideAlert.bind(this));
158
+
159
+ // Initial data fetch
160
+ this.fetchRows(this.state.currentPage);
161
+ }
162
+
163
+ attachEvent(elementId, handler) {
164
+ const el = document.getElementById(elementId);
165
+ if (el) el.addEventListener("click", handler);
166
+ }
167
+
168
+ async applySearch(event) {
169
+ if (event) event.preventDefault();
170
+ this.state.filter = this.filterInput.value;
171
+ await this.fetchRows(this.state.currentPage);
172
+ }
173
+
174
+ async fetchRows(page = null) {
175
+ try {
176
+ if (page !== null) {
177
+ this.state.currentPage = page;
178
+ }
179
+ const defaultSearchColumn = "name";
180
+ // Update address bar
181
+ const searchParam = CRUD_UTIL.getSearchParam(this.state, defaultSearchColumn, false);
182
+ const newUrl = `${window.location.pathname}?${searchParam}`;
183
+ window.history.pushState({ path: newUrl }, "", newUrl);
184
+
185
+ // Fetch table data
186
+ const apiSearchParam = CRUD_UTIL.getSearchParam(this.state, defaultSearchColumn, true);
187
+ const result = await UTIL.fetchAPI(`${this.apiUrl}?${apiSearchParam}`, { method: "GET" });
188
+ this.renderRows(result.data);
189
+ const crudPagination = document.getElementById("crud-pagination");
190
+ CRUD_UTIL.renderPagination(crudPagination, this, result.count);
191
+ } catch (error) {
192
+ console.error("Error fetching items:", error);
193
+ }
194
+ }
195
+
196
+ renderRows(rows) {
197
+ const tableBody = document.querySelector("#crud-table tbody");
198
+ let tableBodyHTML = "";
199
+ rows.forEach(row => {
200
+ const rowComponents = this.getRowComponents(row);
201
+ let actionColumn = "";
202
+ if (this.state.allowUpdate) {
203
+ actionColumn += `<button class="contrast" data-id="${row.id}" data-action="edit">✏️ Edit</button>`;
204
+ }
205
+ if (this.state.allowDelete) {
206
+ actionColumn += `<button class="secondary" data-id="${row.id}" data-action="delete">🗑️ Delete</button>`;
207
+ }
208
+ if (this.state.allowUpdate || this.state.allowDelete) {
209
+ actionColumn = `<td><fieldset class="grid" role="group">${actionColumn}</fieldset></td>`;
210
+ }
211
+ tableBodyHTML += `<tr>${rowComponents.join('')}${actionColumn}</tr>`;
212
+ });
213
+ tableBody.innerHTML = tableBodyHTML;
214
+ this.attachRowActionListeners();
215
+ }
216
+
217
+ attachRowActionListeners() {
218
+ document.querySelectorAll('button[data-action="edit"]').forEach(button => {
219
+ button.addEventListener("click", () => {
220
+ this.showUpdateForm(button.getAttribute("data-id"));
221
+ });
222
+ });
223
+ document.querySelectorAll('button[data-action="delete"]').forEach(button => {
224
+ button.addEventListener("click", () => {
225
+ this.showDeleteForm(button.getAttribute("data-id"));
226
+ });
227
+ });
228
+ }
229
+
230
+ getRowComponents(row) {
231
+ const rowComponents = [
232
+ `<td>${row.id}</td>`,
233
+ `<td>${row.name}</td>`,
234
+ `<td>${row.description}</td>`,
235
+ `<td>${row.permission_names.join(", ")}</td>`
236
+ ];
237
+ return rowComponents;
238
+ }
239
+
240
+ // Create methods
241
+ showCreateForm(event = null) {
242
+ if (event) event.preventDefault();
243
+ const createDialog = document.getElementById("crud-create-form-dialog");
244
+ const createForm = document.getElementById("crud-create-form");
245
+ UTIL.clearFormData(createForm);
246
+ createDialog.showModal();
247
+ }
248
+
249
+ async createRow(event = null) {
250
+ if (event) event.preventDefault();
251
+ try {
252
+ const createForm = document.getElementById("crud-create-form");
253
+ const formData = UTIL.getFormData(createForm);
254
+ formData.permission_names = JSON.parse(formData.permission_names);
255
+ await UTIL.fetchAPI(this.apiUrl, { method: "POST", body: JSON.stringify(formData) });
256
+ await this.fetchRows();
257
+ this.hideCreateForm();
258
+ } catch (error) {
259
+ console.error(error);
260
+ this.showAlert("Create Role Error", error);
261
+ }
262
+ }
263
+
264
+ hideCreateForm(event = null) {
265
+ if (event) event.preventDefault();
266
+ document.getElementById("crud-create-form-dialog").close();
267
+ }
268
+
269
+ // Update methods
270
+ async showUpdateForm(id) {
271
+ this.state.updatedRowId = id;
272
+ const updateDialog = document.getElementById("crud-update-form-dialog");
273
+ const updateForm = document.getElementById("crud-update-form");
274
+ const rawFormData = await UTIL.fetchAPI(`${this.apiUrl}/${id}`, { method: "GET" });
275
+ const { permission_names, ...formData } = rawFormData;
276
+ UTIL.setFormData(updateForm, formData);
277
+ updateForm.querySelector('[name="permission_names"]').value = JSON.stringify(permission_names);
278
+ updateDialog.showModal();
279
+ }
280
+
281
+ async updateRow(event = null) {
282
+ if (event) event.preventDefault();
283
+ try {
284
+ const updateForm = document.getElementById("crud-update-form");
285
+ const formData = UTIL.getFormData(updateForm);
286
+ formData.permission_names = JSON.parse(formData.permission_names);
287
+ await UTIL.fetchAPI(`${this.apiUrl}/${this.state.updatedRowId}`, {
288
+ method: "PUT",
289
+ body: JSON.stringify(formData)
290
+ });
291
+ await this.fetchRows();
292
+ this.hideUpdateForm();
293
+ } catch (error) {
294
+ console.error(error);
295
+ this.showAlert("Update Role Error", error);
296
+ }
297
+ }
298
+
299
+ hideUpdateForm(event = null) {
300
+ if (event) event.preventDefault();
301
+ document.getElementById("crud-update-form-dialog").close();
302
+ }
303
+
304
+ // Delete methods
305
+ async showDeleteForm(id) {
306
+ this.state.deletedRowId = id;
307
+ const deleteDialog = document.getElementById("crud-delete-form-dialog");
308
+ const deleteForm = document.getElementById("crud-delete-form");
309
+ const rawFormData = await UTIL.fetchAPI(`${this.apiUrl}/${id}`, { method: "GET" });
310
+ const { permission_names, ...formData } = rawFormData;
311
+ UTIL.setFormData(deleteForm, formData);
312
+ deleteForm.querySelector('[name="permission_names"]').value = JSON.stringify(permission_names);
313
+ deleteDialog.showModal();
314
+ }
315
+
316
+ async deleteRow(event = null) {
317
+ if (event) event.preventDefault();
318
+ try {
319
+ await UTIL.fetchAPI(`${this.apiUrl}/${this.state.deletedRowId}`, { method: "DELETE" });
320
+ await this.fetchRows();
321
+ this.hideDeleteForm();
322
+ } catch (error) {
323
+ console.error(error);
324
+ this.showAlert("Delete Role Error", error);
325
+ }
326
+ }
327
+
328
+ hideDeleteForm(event = null) {
329
+ if (event) event.preventDefault();
330
+ document.getElementById("crud-delete-form-dialog").close();
331
+ }
332
+
333
+ // Alert methods
334
+ showAlert(title, error) {
335
+ const alertDialog = document.getElementById("crud-alert-dialog");
336
+ document.getElementById("crud-alert-title").textContent = title;
337
+ document.getElementById("crud-alert-message").textContent = error.message || String(error);
338
+ alertDialog.showModal();
339
+ }
340
+
341
+ hideAlert(event = null) {
342
+ if (event) event.preventDefault();
343
+ document.getElementById("crud-alert-dialog").close();
344
+ }
345
+ }
346
+
347
+ // Initialize the CrudApp on DOM ready
348
+ document.addEventListener("DOMContentLoaded", () => {
349
+ const app = document.getElementById("crud-app");
350
+ new CrudApp("/api/v1/roles", {
351
+ pageSize: JSON.parse(app.dataset.pageSize),
352
+ currentPage: JSON.parse(app.dataset.page),
353
+ sort: JSON.parse(app.dataset.sort),
354
+ filter: JSON.parse(app.dataset.filter),
355
+ allowCreate: JSON.parse(app.dataset.allowCreate),
356
+ allowUpdate: JSON.parse(app.dataset.allowUpdate),
357
+ allowDelete: JSON.parse(app.dataset.allowDelete),
358
+ updatedRowId: null,
359
+ deletedRowId: null,
360
+ });
361
+ });
362
+ </script>