@hotstaq/admin-panel 0.4.0 → 0.4.1

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hotstaq/admin-panel",
3
3
  "description": "",
4
- "version": "0.4.0",
4
+ "version": "0.4.1",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
7
7
  "start": "hotstaq --hotsite ./HotSite.json --env-file ./.env run --server-type web-api",
@@ -1,42 +1,43 @@
1
1
  import { HotStaq, Hot, HotAPI, HotComponent, HotComponentOutput } from "hotstaq";
2
2
 
3
3
  /**
4
- * Inline collapsible "Add" panel. Replaces the modal opened by
5
- * <admin-edit hot-type="add">. Built on Bootstrap's collapse so it
6
- * slides down below a trigger button without overlaying the page.
4
+ * Self-contained inline add form. Replaces the modal opened by
5
+ * <admin-edit hot-type="add"> with a Bootstrap-collapse card that
6
+ * lives directly on the page header carries the "+ Add" toggle,
7
+ * body holds the form fields. No cross-component DOM injection.
7
8
  *
8
- * Pairs with <admin-card-table>: the table's "+ Add" button toggles
9
- * this panel via Bootstrap's data-bs-toggle="collapse" data-bs-target.
10
- * Slot for form fields lives at `hot-place-here name="panelBody"`.
9
+ * Place this above an <admin-card-table>; when the user clicks the
10
+ * header toggle, the form panel slides down. On Save, the paired
11
+ * table is asked to refresh via `attached_list`.
11
12
  *
12
13
  * Usage:
13
- * <admin-add-panel name="bankAccountsAdd" hot-title="Add bank account"
14
- * hot-onsave="<(values) => {
15
- * const r = await Hot.jsonRequest(`${config.baseUrl}/v1/bank_accounts/create`,
16
- * { bankAccount: values }, '${jwtToken}');
17
- * if (r && r.error) { alertError(r.error); return false; }
18
- * }Ra>"
19
- * hot-attached_list="bankAccountsList">
14
+ * <admin-add-panel name="bankAccountsAdd"
15
+ * hot-title="Add a bank account"
16
+ * hot-attached_list="bankAccountsList"
17
+ * hot-add_text="+ Add bank account"
18
+ * hot-button_title="Create"
19
+ * hot-onsave="<(values) => {...}Ra>">
20
20
  * <admin-form-field hot-field="name" hot-label="Name" hot-required="1"
21
21
  * hot-col="col-md-6"></admin-form-field>
22
- * ...
22
+ * <admin-form-field hot-field="bankSyncAPIType" hot-label="Sync type"
23
+ * hot-control="select"
24
+ * hot-options="paypal_webhooks:PayPal Webhooks"
25
+ * hot-col="col-md-6"></admin-form-field>
23
26
  * </admin-add-panel>
24
27
  */
25
28
  export class AdminAddPanel extends HotComponent
26
29
  {
27
- /** Title shown at the top of the panel. */
30
+ /** Title shown in the card header (next to the toggle button). */
28
31
  title: string;
29
32
  /** Submit button label. */
30
33
  button_title: string;
31
- /** Cancel button label. Empty disables the cancel button. */
34
+ /** Cancel button label. Empty hides the cancel button. */
32
35
  cancel_text: string;
33
- /** Optional id of the related <admin-card-table>; the toggle button gets injected into its header. */
36
+ /** Optional id of the partner <admin-card-table>; refreshList() is called after a successful save. */
34
37
  attached_list: string;
35
- /** Where to render the toggle button (a hot-place-here name on the page). Leave blank when attached_list is set. */
36
- add_place_here: string;
37
- /** Text shown on the toggle button. */
38
+ /** Text shown on the header toggle button. */
38
39
  add_text: string;
39
- /** When set to "1" / "true", panel starts expanded. */
40
+ /** "1" / "true" panel starts expanded. */
40
41
  start_open: string;
41
42
  /** What to run when the user clicks Save. Receives a values object built from hot-field inputs. Return false to keep the panel open. */
42
43
  onsave: (values: any) => Promise<boolean | void>;
@@ -53,33 +54,31 @@ export class AdminAddPanel extends HotComponent
53
54
  this.button_title = "Save";
54
55
  this.cancel_text = "Cancel";
55
56
  this.attached_list = "";
56
- this.add_place_here = "";
57
57
  this.add_text = "+ Add";
58
58
  this.start_open = "0";
59
59
  this.onsave = null;
60
60
  }
61
61
 
62
62
  /**
63
- * Wires the submit + cancel handlers after the DOM is in place.
64
- * Browsers handle the collapse open/close via Bootstrap data-attrs
65
- * on the toggle button — we don't need to manage that ourselves.
63
+ * Wire submit + cancel handlers after the DOM is in place.
64
+ * Bootstrap's data-bs-toggle handles open/close automatically.
66
65
  */
67
66
  onPostPlace (parentHtmlElement: HTMLElement, htmlElement: HTMLElement): HTMLElement
68
67
  {
69
68
  const self = this;
70
- const panel = document.getElementById (this.panelId);
71
- if (panel == null)
69
+ const root = document.getElementById (this.name);
70
+ if (root == null)
72
71
  return (null);
73
72
 
74
- const submitBtn = panel.querySelector (`.fl-add-panel-submit`) as HTMLButtonElement | null;
75
- const cancelBtn = panel.querySelector (`.fl-add-panel-cancel`) as HTMLButtonElement | null;
73
+ const submitBtn = root.querySelector (".fl-add-panel-submit") as HTMLButtonElement | null;
74
+ const cancelBtn = root.querySelector (".fl-add-panel-cancel") as HTMLButtonElement | null;
76
75
 
77
76
  if (submitBtn != null)
78
77
  {
79
78
  submitBtn.addEventListener ("click", async (e) =>
80
79
  {
81
80
  e.preventDefault ();
82
- const values = self.collectFieldValues (panel);
81
+ const values = self.collectFieldValues (root);
83
82
  submitBtn.disabled = true;
84
83
  try
85
84
  {
@@ -89,7 +88,7 @@ export class AdminAddPanel extends HotComponent
89
88
 
90
89
  if (keepOpen === false || keepOpen == null)
91
90
  {
92
- self.resetFields (panel);
91
+ self.resetFields (root);
93
92
  self.collapsePanel ();
94
93
  self.refreshAttachedList ();
95
94
  }
@@ -110,7 +109,7 @@ export class AdminAddPanel extends HotComponent
110
109
  cancelBtn.addEventListener ("click", (e) =>
111
110
  {
112
111
  e.preventDefault ();
113
- self.resetFields (panel);
112
+ self.resetFields (root);
114
113
  self.collapsePanel ();
115
114
  });
116
115
  }
@@ -118,11 +117,10 @@ export class AdminAddPanel extends HotComponent
118
117
  return (null);
119
118
  }
120
119
 
121
- /** Read every hot-field-marked input inside the panel into a plain object. */
122
- protected collectFieldValues (panel: HTMLElement): any
120
+ protected collectFieldValues (root: HTMLElement): any
123
121
  {
124
122
  const out: any = {};
125
- const nodes = panel.querySelectorAll ("[hot-field]");
123
+ const nodes = root.querySelectorAll ("[hot-field]");
126
124
  for (let i = 0; i < nodes.length; i++)
127
125
  {
128
126
  const el = nodes[i] as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
@@ -139,9 +137,9 @@ export class AdminAddPanel extends HotComponent
139
137
  return (out);
140
138
  }
141
139
 
142
- protected resetFields (panel: HTMLElement): void
140
+ protected resetFields (root: HTMLElement): void
143
141
  {
144
- const nodes = panel.querySelectorAll ("[hot-field]");
142
+ const nodes = root.querySelectorAll ("[hot-field]");
145
143
  for (let i = 0; i < nodes.length; i++)
146
144
  {
147
145
  const el = nodes[i] as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
@@ -155,19 +153,11 @@ export class AdminAddPanel extends HotComponent
155
153
  {
156
154
  const panel = document.getElementById (this.panelId);
157
155
  if (panel == null) return;
158
- // Bootstrap collapse hide — works without importing Bootstrap JS
159
- // directly by toggling the .show class and aria-expanded on any
160
- // trigger pointed at us.
161
156
  panel.classList.remove ("show");
162
157
  const triggers = document.querySelectorAll (`[data-bs-target="#${this.panelId}"]`);
163
158
  triggers.forEach (t => t.setAttribute ("aria-expanded", "false"));
164
159
  }
165
160
 
166
- /**
167
- * Best-effort refresh of the paired <admin-card-table>. The table
168
- * exposes a `refreshList` method on the rendered element when its
169
- * data is loaded; calling it re-fetches without a page reload.
170
- */
171
161
  protected refreshAttachedList (): void
172
162
  {
173
163
  if (this.attached_list === "") return;
@@ -185,48 +175,36 @@ export class AdminAddPanel extends HotComponent
185
175
  this.formId = `${this.name}Form`;
186
176
 
187
177
  const showClass = (this.start_open === "1" || this.start_open === "true") ? " show" : "";
188
- const titleHtml = this.title ? `<h2 class="h6 fl-add-panel-title mb-3">${this.title}</h2>` : "";
178
+ const ariaExp = showClass ? "true" : "false";
179
+ const titleHtml = this.title ? `<strong class="fl-add-panel-title">${this.title}</strong>` : `<span></span>`;
189
180
  const cancelHtml = this.cancel_text
190
181
  ? `<button type="button" class="btn btn-sm btn-link text-muted fl-add-panel-cancel">${this.cancel_text}</button>`
191
182
  : "";
192
183
 
193
- const panelHtml = `
194
- <div id="${this.panelId}" class="collapse fl-add-panel${showClass}">
195
- <div class="card-body border-top bg-body-tertiary fl-add-panel-body">
184
+ // Single self-contained card. Header has the toggle, body is the
185
+ // collapse panel containing the form. Bootstrap's data-bs-toggle
186
+ // drives the open/close — no JS wiring needed for that.
187
+ return (`
188
+ <div id="${this.name}" class="card fl-add-panel mb-3">
189
+ <div class="card-header d-flex justify-content-between align-items-center">
196
190
  ${titleHtml}
197
- <form id="${this.formId}" class="fl-add-panel-form">
198
- <div class="row g-2 align-items-end">
199
- <hot-place-here name="panelBody"></hot-place-here>
200
- </div>
201
- <div class="d-flex justify-content-end gap-2 mt-3">
202
- ${cancelHtml}
203
- <button type="submit" class="btn btn-sm btn-success fl-add-panel-submit">${this.button_title}</button>
204
- </div>
205
- </form>
191
+ <button type="button" class="btn btn-sm btn-primary fl-add-panel-toggle"
192
+ data-bs-toggle="collapse" data-bs-target="#${this.panelId}"
193
+ aria-expanded="${ariaExp}" aria-controls="${this.panelId}">${this.add_text}</button>
206
194
  </div>
207
- </div>`;
208
-
209
- const outputs: HotComponentOutput[] = [{ html: panelHtml, documentSelector: "body" }];
210
-
211
- // If a partner card-table is named, inject the toggle button into
212
- // its header slot. Otherwise honour an explicit add_place_here.
213
- const toggleBtn = `<button type="button" class="btn btn-sm btn-primary fl-add-panel-toggle" data-bs-toggle="collapse" data-bs-target="#${this.panelId}" aria-expanded="${showClass ? "true" : "false"}" aria-controls="${this.panelId}">${this.add_text}</button>`;
214
-
215
- if (this.attached_list !== "")
216
- {
217
- outputs.push ({
218
- html: toggleBtn,
219
- documentSelector: `[data-card-table-add-slot="${this.attached_list}"]`
220
- });
221
- }
222
- else if (this.add_place_here !== "")
223
- {
224
- outputs.push ({
225
- html: toggleBtn,
226
- documentSelector: `hot-place-here[name="${this.add_place_here}"]`
227
- });
228
- }
229
-
230
- return (outputs);
195
+ <div id="${this.panelId}" class="collapse fl-add-panel-body${showClass}">
196
+ <div class="card-body border-top">
197
+ <form id="${this.formId}" class="fl-add-panel-form">
198
+ <div class="row g-2 align-items-end">
199
+ <hot-place-here name="panelBody"></hot-place-here>
200
+ </div>
201
+ <div class="d-flex justify-content-end gap-2 mt-3">
202
+ ${cancelHtml}
203
+ <button type="submit" class="btn btn-sm btn-success fl-add-panel-submit">${this.button_title}</button>
204
+ </div>
205
+ </form>
206
+ </div>
207
+ </div>
208
+ </div>`);
231
209
  }
232
210
  }