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
@@ -20,6 +20,10 @@ class UserRepository(ABC):
20
20
  async def get_by_ids(self, id_list: list[str]) -> UserResponse:
21
21
  """Get users by ids"""
22
22
 
23
+ @abstractmethod
24
+ async def validate_role_names(self, role_names: list[str]):
25
+ """Validate Role names"""
26
+
23
27
  @abstractmethod
24
28
  async def add_roles(self, data: dict[str, list[str]], created_by: str):
25
29
  """Add roles to user"""
@@ -143,13 +143,15 @@ class UserService(BaseService):
143
143
  async def create_user_bulk(
144
144
  self, data: list[UserCreateWithRolesAndAudit]
145
145
  ) -> list[UserResponse]:
146
- role_names = [row.get_role_names() for row in data]
147
- data = [row.get_user_create_with_audit() for row in data]
148
- users = await self.user_repository.create_bulk(data)
146
+ bulk_role_names = [row.get_role_names() for row in data]
147
+ for role_names in bulk_role_names:
148
+ await self.user_repository.validate_role_names(role_names)
149
+ bulk_user_data = [row.get_user_create_with_audit() for row in data]
150
+ users = await self.user_repository.create_bulk(bulk_user_data)
149
151
  if len(users) > 0:
150
152
  created_by = users[0].created_by
151
153
  await self.user_repository.add_roles(
152
- data={user.id: role_names[i] for i, user in enumerate(users)},
154
+ data={user.id: bulk_role_names[i] for i, user in enumerate(users)},
153
155
  created_by=created_by,
154
156
  )
155
157
  return await self.user_repository.get_by_ids([user.id for user in users])
@@ -161,8 +163,9 @@ class UserService(BaseService):
161
163
  )
162
164
  async def create_user(self, data: UserCreateWithRolesAndAudit) -> UserResponse:
163
165
  role_names = data.get_role_names()
164
- data = data.get_user_create_with_audit()
165
- user = await self.user_repository.create(data)
166
+ await self.user_repository.validate_role_names(role_names)
167
+ user_data = data.get_user_create_with_audit()
168
+ user = await self.user_repository.create(user_data)
166
169
  await self.user_repository.add_roles(
167
170
  data={user.id: role_names}, created_by=user.created_by
168
171
  )
@@ -176,14 +179,18 @@ class UserService(BaseService):
176
179
  async def update_user_bulk(
177
180
  self, user_ids: list[str], data: UserUpdateWithRolesAndAudit
178
181
  ) -> list[UserResponse]:
179
- role_names = [row.get_role_names() for row in data]
180
- user_data = [row.get_user_create_with_audit() for row in data]
181
- await self.user_repository.update_bulk(user_ids, user_data)
182
+ bulk_role_names = [row.get_role_names() for row in data]
183
+ for role_names in bulk_role_names:
184
+ await self.user_repository.validate_role_names(role_names)
185
+ bulk_user_data = [row.get_user_update_with_audit() for row in data]
186
+ await self.user_repository.update_bulk(user_ids, bulk_user_data)
182
187
  if len(user_ids) > 0:
183
- updated_by = user_data[0].updated_by
188
+ updated_by = bulk_user_data[0].updated_by
184
189
  await self.user_repository.remove_all_roles(user_ids)
185
190
  await self.user_repository.add_roles(
186
- data={user_id: role_names[i] for i, user_id in enumerate(user_ids)},
191
+ data={
192
+ user_id: bulk_role_names[i] for i, user_id in enumerate(user_ids)
193
+ },
187
194
  updated_by=updated_by,
188
195
  )
189
196
  return await self.user_repository.get_by_ids(user_ids)
@@ -197,6 +204,7 @@ class UserService(BaseService):
197
204
  self, user_id: str, data: UserUpdateWithRolesAndAudit
198
205
  ) -> UserResponse:
199
206
  role_names = data.get_role_names()
207
+ await self.user_repository.validate_role_names(role_names)
200
208
  user_data = data.get_user_update_with_audit()
201
209
  await self.user_repository.update(user_id, user_data)
202
210
  await self.user_repository.remove_all_roles([user_id])
@@ -1,14 +1,22 @@
1
1
  <link rel="stylesheet" href="/static/crud/style.css">
2
2
 
3
- <main class="container">
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 }}'>
4
12
  <article>
5
13
  <h1>Permission</h1>
6
14
 
7
15
  <fieldset id="crud-table-fieldset" role="group" class="grid">
8
- <input id="crud-filter" onchange="applySearch()" placeholder="🔍 Filter" aria-label="Search" />
9
- <button onclick="applySearch()">🔍 Search</button>
16
+ <input id="crud-filter-input" placeholder="🔍 Filter" aria-label="Search" />
17
+ <button id="crud-search-button">🔍 Search</button>
10
18
  {% if allow_create %}
11
- <button class="contrast" onclick="showCreateForm(event)">➕ Add</button>
19
+ <button id="crud-show-create-button" class="contrast">➕ Add</button>
12
20
  {% endif %}
13
21
  </fieldset>
14
22
  <div id="crud-table-container">
@@ -18,7 +26,6 @@
18
26
  <th scope="col">ID</th>
19
27
  <th scope="col">Name</th>
20
28
  <th scope="col">Description</th>
21
- <!-- Update this -->
22
29
  {% if allow_update or allow_delete %}
23
30
  <th scope="col">Actions</th>
24
31
  {% endif %}
@@ -42,10 +49,9 @@
42
49
  Description:
43
50
  <input type="text" name="description" required>
44
51
  </label>
45
- <!-- Update this -->
46
52
  <footer>
47
- <button onclick="createRow(event)">➕ Save</button>
48
- <button class="secondary" onclick="hideCreateForm(event)">❌ Cancel</button>
53
+ <button id="crud-create-button">➕ Save</button>
54
+ <button id="crud-cancel-create-button" class="secondary">❌ Cancel</button>
49
55
  </footer>
50
56
  </form>
51
57
  </article>
@@ -65,10 +71,9 @@
65
71
  Description:
66
72
  <input type="text" name="description" required>
67
73
  </label>
68
- <!-- Update this -->
69
74
  <footer>
70
- <button onclick="updateRow(event)">✏️ Save</button>
71
- <button class="secondary" onclick="hideUpdateForm(event)">❌ Cancel</button>
75
+ <button id="crud-update-button">✏️ Save</button>
76
+ <button id="crud-cancel-update-button" class="secondary">❌ Cancel</button>
72
77
  </footer>
73
78
  </form>
74
79
  </article>
@@ -88,10 +93,9 @@
88
93
  Description:
89
94
  <input type="text" name="description" readonly>
90
95
  </label>
91
- <!-- Update this -->
92
96
  <footer>
93
- <button class="secondary" onclick="hideDeleteForm()">❌ Cancel</button>
94
- <button onclick="deleteRow()">🗑️ Delete</button>
97
+ <button id="crud-cancel-delete-button" class="secondary">❌ Cancel</button>
98
+ <button id="crud-delete-button">🗑️ Delete</button>
95
99
  </footer>
96
100
  </form>
97
101
  </article>
@@ -103,209 +107,234 @@
103
107
  <h2 id="crud-alert-title">Error</h2>
104
108
  <pre id="crud-alert-message"></pre>
105
109
  <footer>
106
- <button onclick="hideAlert(event)">Close</button>
110
+ <button id="crud-alert-close-button">Close</button>
107
111
  </footer>
108
112
  </article>
109
113
  </dialog>
110
-
111
114
  </article>
112
115
  </main>
113
116
 
114
117
  <script src="/static/crud/util.js"></script>
115
118
  <script>
116
- const apiUrl = "/api/v1/permissions";
117
- const crudState = {
118
- pageSize: {{page_size | tojson}},
119
- currentPage: {{page | tojson}},
120
- sort: {{sort | tojson}},
121
- filter: {{filter | tojson}},
122
- allowCreate: {{allow_create | tojson}},
123
- allowUpdate: {{allow_update | tojson}},
124
- allowDelete: {{allow_delete | tojson}},
125
- updatedRowId: null,
126
- deletedRowId: null,
127
- };
119
+ class CrudApp {
120
+ constructor(apiUrl, initialState) {
121
+ this.apiUrl = apiUrl;
122
+ this.state = { ...initialState };
123
+ this.init();
124
+ }
128
125
 
129
- async function applySearch() {
130
- const filterInput = document.getElementById("crud-filter");
131
- crudState.filter = filterInput.value;
132
- return await fetchRows(crudState.currentPage);
133
- }
126
+ init() {
127
+ // Cache common elements
128
+ this.filterInput = document.getElementById("crud-filter-input");
129
+ this.searchButton = document.getElementById("crud-search-button");
130
+ this.filterInput.value = this.state.filter;
134
131
 
135
- async function fetchRows(page = null) {
136
- try {
137
- if (typeof page !== 'undefined' && page !== null) {
138
- crudState.currentPage = page;
139
- }
140
- const defaultSearchColumn = "name"
141
- // update address bar
142
- const searchParam = CRUD_UTIL.getSearchParam(crudState, defaultSearchColumn, false);
143
- const newUrl = `${window.location.pathname}?${searchParam}`;
144
- window.history.pushState({ path: newUrl }, "", newUrl);
145
- // update table and pagination
146
- const apiSearchParam = CRUD_UTIL.getSearchParam(crudState, defaultSearchColumn, true);
147
- const result = await UTIL.fetchAPI(
148
- `${apiUrl}?${apiSearchParam}`, { method: "GET" }
149
- );
150
- renderRows(result.data);
151
- const crudPagination = document.getElementById("crud-pagination");
152
- CRUD_UTIL.renderPagination(
153
- crudPagination, crudState, result.count, "fetchRows"
154
- );
155
- } catch (error) {
156
- console.error("Error fetching items:", error);
132
+ // Bind events
133
+ this.filterInput.addEventListener("change", (e) => this.applySearch(e));
134
+ this.searchButton.addEventListener("click", (e) => this.applySearch(e));
135
+
136
+ // Attach optional events
137
+ this.attachEvent("crud-show-create-button", this.showCreateForm.bind(this));
138
+ this.attachEvent("crud-create-button", this.createRow.bind(this));
139
+ this.attachEvent("crud-cancel-create-button", this.hideCreateForm.bind(this));
140
+ this.attachEvent("crud-update-button", this.updateRow.bind(this));
141
+ this.attachEvent("crud-cancel-update-button", this.hideUpdateForm.bind(this));
142
+ this.attachEvent("crud-delete-button", this.deleteRow.bind(this));
143
+ this.attachEvent("crud-cancel-delete-button", this.hideDeleteForm.bind(this));
144
+ this.attachEvent("crud-alert-close-button", this.hideAlert.bind(this));
145
+
146
+ // Initial data fetch
147
+ this.fetchRows(this.state.currentPage);
157
148
  }
158
- }
159
149
 
160
- function renderRows(rows) {
161
- const tableBody = document.querySelector("#crud-table tbody");
162
- tableBody.innerHTML = "";
163
- rows.forEach(row => {
164
- let rowComponent = getRowComponents(row);
165
- actionColumn = "";
166
- if (crudState.allowUpdate) {
167
- actionColumn += `<button class="contrast" onclick="showUpdateForm('${row.id}')">✏️ Edit</button>`;
168
- }
169
- if (crudState.allowDelete) {
170
- actionColumn += `<button class="secondary" onclick="showDeleteForm('${row.id}')">🗑️ Delete</button>`;
171
- }
172
- if (crudState.allowUpdate || crudState.allowDelete) {
173
- actionColumn = `<td><fieldset class="grid" role="group">${actionColumn}</fieldset></td>`;
150
+ attachEvent(elementId, handler) {
151
+ const el = document.getElementById(elementId);
152
+ if (el) el.addEventListener("click", handler);
153
+ }
154
+
155
+ async applySearch(event) {
156
+ if (event) event.preventDefault();
157
+ this.state.filter = this.filterInput.value;
158
+ await this.fetchRows(this.state.currentPage);
159
+ }
160
+
161
+ async fetchRows(page = null) {
162
+ try {
163
+ if (page !== null) {
164
+ this.state.currentPage = page;
165
+ }
166
+ const defaultSearchColumn = "name";
167
+ // Update address bar
168
+ const searchParam = CRUD_UTIL.getSearchParam(this.state, defaultSearchColumn, false);
169
+ const newUrl = `${window.location.pathname}?${searchParam}`;
170
+ window.history.pushState({ path: newUrl }, "", newUrl);
171
+
172
+ // Fetch table data
173
+ const apiSearchParam = CRUD_UTIL.getSearchParam(this.state, defaultSearchColumn, true);
174
+ const result = await UTIL.fetchAPI(`${this.apiUrl}?${apiSearchParam}`, { method: "GET" });
175
+ this.renderRows(result.data);
176
+ const crudPagination = document.getElementById("crud-pagination");
177
+ CRUD_UTIL.renderPagination(crudPagination, this, result.count);
178
+ } catch (error) {
179
+ console.error("Error fetching items:", error);
174
180
  }
175
- tableBody.innerHTML += `<tr>${rowComponent.join('')}${actionColumn}</tr>`;
176
- });
177
- }
181
+ }
178
182
 
179
- function getRowComponents(row) {
180
- let rowComponents = [];
181
- rowComponents.push(`<td>${row.id}</td>`);
182
- rowComponents.push(`<td>${row.name}</td>`);
183
- rowComponents.push(`<td>${row.description}</td>`);
184
- // Update this
185
- return rowComponents;
186
- }
183
+ renderRows(rows) {
184
+ const tableBody = document.querySelector("#crud-table tbody");
185
+ let tableBodyHTML = "";
186
+ rows.forEach(row => {
187
+ const rowComponents = this.getRowComponents(row);
188
+ let actionColumn = "";
189
+ if (this.state.allowUpdate) {
190
+ actionColumn += `<button class="contrast" data-id="${row.id}" data-action="edit">✏️ Edit</button>`;
191
+ }
192
+ if (this.state.allowDelete) {
193
+ actionColumn += `<button class="secondary" data-id="${row.id}" data-action="delete">🗑️ Delete</button>`;
194
+ }
195
+ if (this.state.allowUpdate || this.state.allowDelete) {
196
+ actionColumn = `<td><fieldset class="grid" role="group">${actionColumn}</fieldset></td>`;
197
+ }
198
+ tableBodyHTML += `<tr>${rowComponents.join('')}${actionColumn}</tr>`;
199
+ });
200
+ tableBody.innerHTML = tableBodyHTML;
201
+ this.attachRowActionListeners();
202
+ }
187
203
 
188
- {% if allow_create %}
189
- async function showCreateForm(id) {
190
- const createFormDialog = document.getElementById("crud-create-form-dialog");
191
- const createForm = document.getElementById("crud-create-form");
192
- UTIL.clearFormData(createForm);
193
- createFormDialog.showModal();
194
- }
204
+ attachRowActionListeners() {
205
+ document.querySelectorAll('button[data-action="edit"]').forEach(button => {
206
+ button.addEventListener("click", () => {
207
+ this.showUpdateForm(button.getAttribute("data-id"));
208
+ });
209
+ });
210
+ document.querySelectorAll('button[data-action="delete"]').forEach(button => {
211
+ button.addEventListener("click", () => {
212
+ this.showDeleteForm(button.getAttribute("data-id"));
213
+ });
214
+ });
215
+ }
195
216
 
196
- async function createRow(event = null) {
197
- if (event != null) {
198
- event.preventDefault();
217
+ getRowComponents(row) {
218
+ return [
219
+ `<td>${row.id}</td>`,
220
+ `<td>${row.name}</td>`,
221
+ `<td>${row.description}</td>`
222
+ ];
199
223
  }
200
- try {
224
+
225
+ // Create methods
226
+ showCreateForm(event = null) {
227
+ if (event) event.preventDefault();
228
+ const createDialog = document.getElementById("crud-create-form-dialog");
201
229
  const createForm = document.getElementById("crud-create-form");
202
- const formData = JSON.stringify(UTIL.getFormData(createForm));
203
- await UTIL.fetchAPI(apiUrl, {method: "POST", body: formData});
204
- await fetchRows();
205
- hideCreateForm();
206
- } catch(error) {
207
- showAlert("Create Permission Error", error);
230
+ UTIL.clearFormData(createForm);
231
+ createDialog.showModal();
208
232
  }
209
- }
210
233
 
211
- function hideCreateForm(event = null) {
212
- if (event != null) {
213
- event.preventDefault();
234
+ async createRow(event = null) {
235
+ if (event) event.preventDefault();
236
+ try {
237
+ const createForm = document.getElementById("crud-create-form");
238
+ const formData = UTIL.getFormData(createForm);
239
+ await UTIL.fetchAPI(this.apiUrl, { method: "POST", body: JSON.stringify(formData) });
240
+ await this.fetchRows();
241
+ this.hideCreateForm();
242
+ } catch (error) {
243
+ console.error(error);
244
+ this.showAlert("Create Permission Error", error);
245
+ }
214
246
  }
215
- const createFormDialog = document.getElementById("crud-create-form-dialog");
216
- createFormDialog.close();
217
- }
218
- {% endif %}
219
247
 
220
- {% if allow_update %}
221
- async function showUpdateForm(id) {
222
- crudState.updatedRowId = id;
223
- const updateFormDialog = document.getElementById("crud-update-form-dialog");
224
- const updateForm = document.getElementById("crud-update-form");
225
- result = await UTIL.fetchAPI(`${apiUrl}/${id}`, { method: "GET" });
226
- UTIL.setFormData(updateForm, result);
227
- updateFormDialog.showModal();
228
- }
229
-
230
- async function updateRow(event = null) {
231
- if (event != null) {
232
- event.preventDefault();
248
+ hideCreateForm(event = null) {
249
+ if (event) event.preventDefault();
250
+ document.getElementById("crud-create-form-dialog").close();
233
251
  }
234
- try {
252
+
253
+ // Update methods
254
+ async showUpdateForm(id) {
255
+ this.state.updatedRowId = id;
256
+ const updateDialog = document.getElementById("crud-update-form-dialog");
235
257
  const updateForm = document.getElementById("crud-update-form");
236
- const formData = JSON.stringify(UTIL.getFormData(updateForm));
237
- await UTIL.fetchAPI(
238
- `${apiUrl}/${crudState.updatedRowId}`, {method: "PUT", body: formData},
239
- );
240
- await fetchRows();
241
- hideUpdateForm();
242
- } catch(error) {
243
- showAlert("Update Permission Error", error);
258
+ const formData = await UTIL.fetchAPI(`${this.apiUrl}/${id}`, { method: "GET" });
259
+ UTIL.setFormData(updateForm, formData);
260
+ updateDialog.showModal();
244
261
  }
245
- }
246
262
 
247
- function hideUpdateForm(event = null) {
248
- if (event != null) {
249
- event.preventDefault();
263
+ async updateRow(event = null) {
264
+ if (event) event.preventDefault();
265
+ try {
266
+ const updateForm = document.getElementById("crud-update-form");
267
+ const formData = UTIL.getFormData(updateForm);
268
+ await UTIL.fetchAPI(`${this.apiUrl}/${this.state.updatedRowId}`, {
269
+ method: "PUT",
270
+ body: JSON.stringify(formData)
271
+ });
272
+ await this.fetchRows();
273
+ this.hideUpdateForm();
274
+ } catch (error) {
275
+ console.error(error);
276
+ this.showAlert("Update Permission Error", error);
277
+ }
250
278
  }
251
- const updateFormDialog = document.getElementById("crud-update-form-dialog");
252
- updateFormDialog.close();
253
- }
254
- {% endif %}
255
279
 
256
- {% if allow_delete %}
257
- async function showDeleteForm(id) {
258
- crudState.deletedRowId = id;
259
- const deleteFormDialog = document.getElementById("crud-delete-form-dialog");
260
- const deleteForm = document.getElementById("crud-delete-form");
261
- result = await UTIL.fetchAPI(`${apiUrl}/${id}`, { method: "GET" });
262
- UTIL.setFormData(deleteForm, result);
263
- deleteFormDialog.showModal();
264
- }
280
+ hideUpdateForm(event = null) {
281
+ if (event) event.preventDefault();
282
+ document.getElementById("crud-update-form-dialog").close();
283
+ }
265
284
 
266
- async function deleteRow(event = null) {
267
- if (event != null) {
268
- event.preventDefault();
285
+ // Delete methods
286
+ async showDeleteForm(id) {
287
+ this.state.deletedRowId = id;
288
+ const deleteDialog = document.getElementById("crud-delete-form-dialog");
289
+ const deleteForm = document.getElementById("crud-delete-form");
290
+ const formData = await UTIL.fetchAPI(`${this.apiUrl}/${id}`, { method: "GET" });
291
+ UTIL.setFormData(deleteForm, formData);
292
+ deleteDialog.showModal();
269
293
  }
270
- try {
271
- await UTIL.fetchAPI(`${apiUrl}/${crudState.deletedRowId}`, {method: "DELETE",});
272
- await fetchRows();
273
- hideDeleteForm();
274
- } catch(error) {
275
- showAlert("Delete Permission Error", error);
294
+
295
+ async deleteRow(event = null) {
296
+ if (event) event.preventDefault();
297
+ try {
298
+ await UTIL.fetchAPI(`${this.apiUrl}/${this.state.deletedRowId}`, { method: "DELETE" });
299
+ await this.fetchRows();
300
+ this.hideDeleteForm();
301
+ } catch (error) {
302
+ console.error(error);
303
+ this.showAlert("Delete Permission Error", error);
304
+ }
276
305
  }
277
- }
278
306
 
279
- function hideDeleteForm(event = null) {
280
- if (event != null) {
281
- event.preventDefault();
307
+ hideDeleteForm(event = null) {
308
+ if (event) event.preventDefault();
309
+ document.getElementById("crud-delete-form-dialog").close();
282
310
  }
283
- const deleteFormDialog = document.getElementById("crud-delete-form-dialog");
284
- deleteFormDialog.close();
285
- }
286
- {% endif %}
287
311
 
288
- function showAlert(title, error) {
289
- const alertDialog = document.getElementById("crud-alert-dialog");
290
- const alertTitle = document.getElementById("crud-alert-title");
291
- const alertMessage = document.getElementById("crud-alert-message");
292
- const errorMessage = error.message ? error.message : String(error);
293
- alertTitle.textContent = title;
294
- alertMessage.textContent = errorMessage;
295
- alertDialog.showModal();
296
- }
312
+ // Alert methods
313
+ showAlert(title, error) {
314
+ const alertDialog = document.getElementById("crud-alert-dialog");
315
+ document.getElementById("crud-alert-title").textContent = title;
316
+ document.getElementById("crud-alert-message").textContent = error.message || String(error);
317
+ alertDialog.showModal();
318
+ }
297
319
 
298
- function hideAlert(event = null) {
299
- if (event != null) {
300
- event.preventDefault();
320
+ hideAlert(event = null) {
321
+ if (event) event.preventDefault();
322
+ document.getElementById("crud-alert-dialog").close();
301
323
  }
302
- const alertDialog = document.getElementById("crud-alert-dialog");
303
- alertDialog.close();
304
324
  }
305
325
 
306
326
  document.addEventListener("DOMContentLoaded", () => {
307
- const filterInput = document.getElementById("crud-filter");
308
- filterInput.value = crudState.filter;
309
- fetchRows(crudState.currentPage);
327
+ const app = document.getElementById("crud-app");
328
+ new CrudApp("/api/v1/permissions", {
329
+ pageSize: JSON.parse(app.dataset.pageSize),
330
+ currentPage: JSON.parse(app.dataset.page),
331
+ sort: JSON.parse(app.dataset.sort),
332
+ filter: JSON.parse(app.dataset.filter),
333
+ allowCreate: JSON.parse(app.dataset.allowCreate),
334
+ allowUpdate: JSON.parse(app.dataset.allowUpdate),
335
+ allowDelete: JSON.parse(app.dataset.allowDelete),
336
+ updatedRowId: null,
337
+ deletedRowId: null,
338
+ });
310
339
  });
311
- </script>
340
+ </script>