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.
- zrb/builtin/llm/llm_chat.py +42 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +28 -6
- 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
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +3 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +18 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +20 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +17 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +19 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +209 -180
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +362 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +377 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +68 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +50 -29
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +3 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +6 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -3
- zrb/input/any_input.py +5 -0
- zrb/input/base_input.py +6 -0
- zrb/input/bool_input.py +2 -0
- zrb/input/float_input.py +2 -0
- zrb/input/int_input.py +2 -0
- zrb/input/option_input.py +2 -0
- zrb/input/password_input.py +2 -0
- zrb/input/text_input.py +2 -0
- zrb/runner/cli.py +1 -1
- zrb/runner/common_util.py +3 -3
- zrb/runner/web_route/task_input_api_route.py +1 -1
- {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/METADATA +84 -17
- {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/RECORD +33 -33
- {zrb-1.2.2.dist-info → zrb-1.3.0.dist-info}/WHEEL +0 -0
- {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>
|