@ansiversa/components 0.0.143 → 0.0.145
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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import AvButton from "../../AvButton.astro";
|
|
3
3
|
import AvCard from "../../AvCard.astro";
|
|
4
4
|
import AvDrawer from "../../AvDrawer.astro";
|
|
5
|
+
import AvEmptyState from "../../AvEmptyState.astro";
|
|
6
|
+
import AvIcon from "../../AvIcon.astro";
|
|
5
7
|
import AvInput from "../../AvInput.astro";
|
|
6
8
|
import AvLoading from "../../AvLoading.astro";
|
|
7
9
|
import AvSelect from "../../AvSelect.astro";
|
|
@@ -50,29 +52,43 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
50
52
|
<p class="av-table-toolbar__subtitle av-m-0">Manage frequently asked questions.</p>
|
|
51
53
|
<div class="av-admin-toolbar-actions">
|
|
52
54
|
<AvButton
|
|
53
|
-
size="
|
|
55
|
+
size="md"
|
|
54
56
|
variant="ghost"
|
|
55
57
|
type="button"
|
|
58
|
+
title="Refresh FAQs"
|
|
59
|
+
aria-label="Refresh FAQs"
|
|
56
60
|
@click.prevent="fetchFaqs()"
|
|
57
61
|
:disabled="loading || saving"
|
|
58
62
|
>
|
|
59
|
-
|
|
63
|
+
<AvIcon name="refresh" size="md" />
|
|
60
64
|
</AvButton>
|
|
61
65
|
<AvButton
|
|
62
|
-
size="
|
|
66
|
+
size="md"
|
|
63
67
|
variant="primary"
|
|
64
68
|
type="button"
|
|
69
|
+
title="Add FAQ"
|
|
70
|
+
aria-label="Add FAQ"
|
|
65
71
|
@click.prevent="openCreate()"
|
|
66
72
|
:disabled="loading || saving"
|
|
67
73
|
>
|
|
68
|
-
|
|
74
|
+
<AvIcon name="plus" size="md" />
|
|
69
75
|
</AvButton>
|
|
70
76
|
</div>
|
|
71
77
|
</div>
|
|
72
78
|
|
|
73
|
-
<div class="av-admin-filters"
|
|
74
|
-
<div class="av-admin-filter av-admin-filter--
|
|
75
|
-
<
|
|
79
|
+
<div class="av-admin-filters">
|
|
80
|
+
<div class="av-admin-filter av-admin-filter--search">
|
|
81
|
+
<AvInput
|
|
82
|
+
name="faq-search"
|
|
83
|
+
type="search"
|
|
84
|
+
placeholder="Search by question, category, or audience..."
|
|
85
|
+
x-model="q"
|
|
86
|
+
@input="setQuery($event.target.value)"
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="av-admin-filter av-admin-filter--dropdown" x-show="showAudienceToggle" x-cloak>
|
|
91
|
+
<AvSelect name="faq-audience" aria-label="Filter by audience" x-model="audience" @change="setAudience($event.target.value)">
|
|
76
92
|
<option value="user">User</option>
|
|
77
93
|
<option value="admin">Admin</option>
|
|
78
94
|
</AvSelect>
|
|
@@ -92,9 +108,13 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
92
108
|
</AvCard>
|
|
93
109
|
|
|
94
110
|
<template x-if="!loading && !faqs.length">
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
111
|
+
<AvEmptyState
|
|
112
|
+
headline="No FAQs match your search"
|
|
113
|
+
description="Try another search term or adjust filters."
|
|
114
|
+
ctaLabel="Add FAQ"
|
|
115
|
+
x-bind:ctaDisabled="loading || saving"
|
|
116
|
+
@cta="openCreate()"
|
|
117
|
+
/>
|
|
98
118
|
</template>
|
|
99
119
|
|
|
100
120
|
<template x-if="faqs.length">
|
|
@@ -102,11 +122,10 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
102
122
|
<AvTable stickyHeader>
|
|
103
123
|
<thead>
|
|
104
124
|
<tr>
|
|
105
|
-
<th
|
|
125
|
+
<th>Reorder</th>
|
|
106
126
|
<th x-show="showAudienceColumn" x-cloak>Audience</th>
|
|
107
127
|
<th x-show="showCategoryColumn" x-cloak>Category</th>
|
|
108
128
|
<th>Question</th>
|
|
109
|
-
<th>Answer</th>
|
|
110
129
|
<th>Published</th>
|
|
111
130
|
<th x-show="showUpdatedColumn" x-cloak>Updated</th>
|
|
112
131
|
<th class="av-table__th-actions">Actions</th>
|
|
@@ -118,7 +137,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
118
137
|
<tr>
|
|
119
138
|
<td>
|
|
120
139
|
<div class="av-faq-manager__order-cell">
|
|
121
|
-
<span class="av-faq-manager__order-pill" x-text="faq.sort_order"></span>
|
|
122
140
|
<AvButton
|
|
123
141
|
size="sm"
|
|
124
142
|
variant="ghost"
|
|
@@ -126,6 +144,7 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
126
144
|
@click.prevent="moveFaq(index, -1)"
|
|
127
145
|
:disabled="loading || saving || index === 0 || !faq.id || !faqs[index - 1]?.id"
|
|
128
146
|
aria-label="Move up"
|
|
147
|
+
title="Move up"
|
|
129
148
|
>
|
|
130
149
|
↑
|
|
131
150
|
</AvButton>
|
|
@@ -136,6 +155,7 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
136
155
|
@click.prevent="moveFaq(index, 1)"
|
|
137
156
|
:disabled="loading || saving || index === faqs.length - 1 || !faq.id || !faqs[index + 1]?.id"
|
|
138
157
|
aria-label="Move down"
|
|
158
|
+
title="Move down"
|
|
139
159
|
>
|
|
140
160
|
↓
|
|
141
161
|
</AvButton>
|
|
@@ -150,9 +170,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
150
170
|
<td>
|
|
151
171
|
<div class="av-table__cell-strong" x-text="faq.question || '-'" ></div>
|
|
152
172
|
</td>
|
|
153
|
-
<td class="av-table__cell-muted">
|
|
154
|
-
<span x-text="previewAnswer(faq.answer_md)"></span>
|
|
155
|
-
</td>
|
|
156
173
|
<td>
|
|
157
174
|
<AvButton
|
|
158
175
|
size="sm"
|
|
@@ -246,17 +263,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
246
263
|
<p class="av-faq-manager__field-error" role="status" x-text="validationErrors.category"></p>
|
|
247
264
|
</template>
|
|
248
265
|
|
|
249
|
-
<AvInput
|
|
250
|
-
label="Sort order"
|
|
251
|
-
type="number"
|
|
252
|
-
min="1"
|
|
253
|
-
name="sort_order"
|
|
254
|
-
x-model.number="draftSortOrder"
|
|
255
|
-
/>
|
|
256
|
-
<template x-if="validationErrors.sort_order">
|
|
257
|
-
<p class="av-faq-manager__field-error" role="status" x-text="validationErrors.sort_order"></p>
|
|
258
|
-
</template>
|
|
259
|
-
|
|
260
266
|
<div class="av-faq-manager__publish-toggle">
|
|
261
267
|
<label class="av-label" for="faq-draft-published">Published</label>
|
|
262
268
|
<input
|
|
@@ -303,11 +309,12 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
303
309
|
drawerError: "",
|
|
304
310
|
draftId: null,
|
|
305
311
|
draftAudience: "user",
|
|
312
|
+
q: "",
|
|
306
313
|
draftCategory: "",
|
|
307
314
|
draftQuestion: "",
|
|
308
315
|
draftAnswerMd: "",
|
|
309
|
-
draftSortOrder: 0,
|
|
310
316
|
draftIsPublished: true,
|
|
317
|
+
_queryTimer: null,
|
|
311
318
|
limits: {
|
|
312
319
|
questionMin: 3,
|
|
313
320
|
questionMax: 160,
|
|
@@ -354,7 +361,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
354
361
|
const questionLen = this.draftQuestionCount;
|
|
355
362
|
const answerLen = this.draftAnswerCount;
|
|
356
363
|
const categoryLen = this.draftCategoryCount;
|
|
357
|
-
const sortOrder = Number(this.draftSortOrder);
|
|
358
364
|
|
|
359
365
|
if (questionLen < this.limits.questionMin || questionLen > this.limits.questionMax) {
|
|
360
366
|
errors.question = `Question must be ${this.limits.questionMin}-${this.limits.questionMax} characters`;
|
|
@@ -368,10 +374,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
368
374
|
errors.category = `Category must be ≤ ${this.limits.categoryMax} characters`;
|
|
369
375
|
}
|
|
370
376
|
|
|
371
|
-
if (!Number.isInteger(sortOrder) || sortOrder < 1) {
|
|
372
|
-
errors.sort_order = "Sort order must be 1 or greater";
|
|
373
|
-
}
|
|
374
|
-
|
|
375
377
|
if (this.draftAudience !== "user" && this.draftAudience !== "admin") {
|
|
376
378
|
errors.audience = "Audience must be user or admin";
|
|
377
379
|
}
|
|
@@ -439,12 +441,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
439
441
|
};
|
|
440
442
|
},
|
|
441
443
|
|
|
442
|
-
previewAnswer(value) {
|
|
443
|
-
const text = typeof value === "string" ? value.trim() : "";
|
|
444
|
-
if (!text) return "-";
|
|
445
|
-
return text.length > 120 ? `${text.slice(0, 120)}...` : text;
|
|
446
|
-
},
|
|
447
|
-
|
|
448
444
|
formatUpdatedAt(value) {
|
|
449
445
|
if (!value) return "-";
|
|
450
446
|
const date = new Date(value);
|
|
@@ -457,6 +453,19 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
457
453
|
this.fetchFaqs();
|
|
458
454
|
},
|
|
459
455
|
|
|
456
|
+
setQuery(value) {
|
|
457
|
+
this.q = typeof value === "string" ? value : "";
|
|
458
|
+
if (this._queryTimer) {
|
|
459
|
+
window.clearTimeout(this._queryTimer);
|
|
460
|
+
this._queryTimer = null;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
this._queryTimer = window.setTimeout(() => {
|
|
464
|
+
this.fetchFaqs();
|
|
465
|
+
this._queryTimer = null;
|
|
466
|
+
}, 250);
|
|
467
|
+
},
|
|
468
|
+
|
|
460
469
|
clearMessages() {
|
|
461
470
|
this.error = "";
|
|
462
471
|
this.notice = "";
|
|
@@ -467,8 +476,13 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
467
476
|
this.clearMessages();
|
|
468
477
|
|
|
469
478
|
try {
|
|
479
|
+
const searchParams = new URLSearchParams();
|
|
480
|
+
searchParams.set("audience", this.audience);
|
|
481
|
+
const trimmedQuery = this.trimValue(this.q);
|
|
482
|
+
if (trimmedQuery) searchParams.set("q", trimmedQuery);
|
|
483
|
+
|
|
470
484
|
const response = await fetch(
|
|
471
|
-
this.endpoint(`/api/admin/faqs.json
|
|
485
|
+
this.endpoint(`/api/admin/faqs.json?${searchParams.toString()}`),
|
|
472
486
|
{
|
|
473
487
|
method: "GET",
|
|
474
488
|
credentials: "include",
|
|
@@ -503,7 +517,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
503
517
|
this.draftCategory = "";
|
|
504
518
|
this.draftQuestion = "";
|
|
505
519
|
this.draftAnswerMd = "";
|
|
506
|
-
this.draftSortOrder = this.faqs.length + 1;
|
|
507
520
|
this.draftIsPublished = true;
|
|
508
521
|
this.drawerError = "";
|
|
509
522
|
},
|
|
@@ -523,7 +536,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
523
536
|
this.draftCategory = faq?.category ?? "";
|
|
524
537
|
this.draftQuestion = faq?.question ?? "";
|
|
525
538
|
this.draftAnswerMd = faq?.answer_md ?? "";
|
|
526
|
-
this.draftSortOrder = Number.isFinite(Number(faq?.sort_order)) ? Number(faq.sort_order) : 0;
|
|
527
539
|
this.draftIsPublished = faq?.is_published !== false;
|
|
528
540
|
this.drawerError = "";
|
|
529
541
|
this.drawerOpen = true;
|
|
@@ -536,13 +548,11 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
536
548
|
},
|
|
537
549
|
|
|
538
550
|
buildPayload() {
|
|
539
|
-
const parsedSortOrder = Number.parseInt(String(this.draftSortOrder ?? ""), 10);
|
|
540
551
|
return {
|
|
541
552
|
audience: this.draftAudience === "admin" ? "admin" : "user",
|
|
542
553
|
category: String(this.draftCategory || "").trim(),
|
|
543
554
|
question: String(this.draftQuestion || "").trim(),
|
|
544
555
|
answer_md: String(this.draftAnswerMd || "").trim(),
|
|
545
|
-
sort_order: Number.isInteger(parsedSortOrder) ? parsedSortOrder : 0,
|
|
546
556
|
is_published: Boolean(this.draftIsPublished),
|
|
547
557
|
};
|
|
548
558
|
},
|
|
@@ -742,20 +752,6 @@ const initialAudience = defaultAudience === "admin" ? "admin" : "user";
|
|
|
742
752
|
align-items: center;
|
|
743
753
|
}
|
|
744
754
|
|
|
745
|
-
.av-faq-manager__order-pill {
|
|
746
|
-
min-width: 2rem;
|
|
747
|
-
height: 2rem;
|
|
748
|
-
display: inline-flex;
|
|
749
|
-
align-items: center;
|
|
750
|
-
justify-content: center;
|
|
751
|
-
border-radius: 9999px;
|
|
752
|
-
border: 1px solid rgba(148, 163, 184, 0.35);
|
|
753
|
-
color: rgba(226, 232, 240, 0.95);
|
|
754
|
-
font-size: 0.78rem;
|
|
755
|
-
line-height: 1;
|
|
756
|
-
padding: 0 0.5rem;
|
|
757
|
-
}
|
|
758
|
-
|
|
759
755
|
.av-faq-manager__publish-toggle {
|
|
760
756
|
display: flex;
|
|
761
757
|
align-items: center;
|
|
@@ -15,6 +15,7 @@ const normalizedAppKey = typeof appKey === "string" ? appKey.trim() : "";
|
|
|
15
15
|
const meta = normalizedAppKey ? MINI_APP_REGISTRY[normalizedAppKey] : undefined;
|
|
16
16
|
const appName = meta?.name ?? (normalizedAppKey || "App");
|
|
17
17
|
const showLogo = normalizedAppKey.length > 0;
|
|
18
|
+
const homeHref = meta?.links?.[0]?.href ?? "/";
|
|
18
19
|
|
|
19
20
|
let menuLinks = links ?? meta?.links ?? [];
|
|
20
21
|
if (!menuLinks.length) {
|
|
@@ -34,11 +35,18 @@ if (normalizedBookmarksHref.length > 0) {
|
|
|
34
35
|
|
|
35
36
|
<div class="av-mini-app-bar">
|
|
36
37
|
<div class="av-mini-app-bar__inner">
|
|
37
|
-
<div class="av-mini-app-bar__title"
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
<div class="av-mini-app-bar__title">
|
|
39
|
+
<a
|
|
40
|
+
href={homeHref}
|
|
41
|
+
class="av-mini-app-bar__title-link"
|
|
42
|
+
title={appName}
|
|
43
|
+
aria-label={`Go to ${appName} home`}
|
|
44
|
+
>
|
|
45
|
+
<span class="av-mini-app-bar__name flex items-center gap-2">
|
|
46
|
+
{showLogo && <AppLogo appId={normalizedAppKey} size="sm" />}
|
|
47
|
+
<span class="truncate">{appName}</span>
|
|
48
|
+
</span>
|
|
49
|
+
</a>
|
|
42
50
|
</div>
|
|
43
51
|
|
|
44
52
|
<div class="av-mini-app-bar__menu" x-data="{ open: false }">
|
package/src/styles/global.css
CHANGED
|
@@ -1432,6 +1432,11 @@
|
|
|
1432
1432
|
min-width: 0;
|
|
1433
1433
|
}
|
|
1434
1434
|
|
|
1435
|
+
.av-mini-app-bar__title-link {
|
|
1436
|
+
@apply inline-flex items-center rounded-md text-slate-100 transition-colors hover:text-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400/70 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-950;
|
|
1437
|
+
min-width: 0;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1435
1440
|
.av-mini-app-bar__name {
|
|
1436
1441
|
@apply block truncate;
|
|
1437
1442
|
}
|