@alliance-droid/status-feedback-system 4.3.1 → 4.3.3
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/CHANGELOG.md +30 -17
- package/README.md +457 -457
- package/dist/api.d.ts +2 -0
- package/dist/components/board/FeedbackDetail.svelte +37 -22
- package/dist/components/board/FeedbackItem.svelte +19 -26
- package/dist/components/board/FeedbackList.svelte +2 -2
- package/dist/components/board/FeedbackSidebar.svelte +11 -11
- package/dist/components/board/StatusBadgeMenu.svelte +32 -32
- package/dist/components/board/screenshot-src.d.ts +16 -0
- package/dist/components/board/screenshot-src.js +24 -0
- package/dist/components/board/state.svelte.d.ts +1 -1
- package/package.json +70 -70
package/dist/api.d.ts
CHANGED
|
@@ -47,6 +47,8 @@ export interface BoardItem {
|
|
|
47
47
|
adminResponse: string | null;
|
|
48
48
|
responseCount: number;
|
|
49
49
|
createdAt: number;
|
|
50
|
+
/** Safe, own-origin URL of the reporter's pasted screenshot (server-validated), or null. */
|
|
51
|
+
screenshotUrl?: string | null;
|
|
50
52
|
/** Server-authorized consumer-board status control for this item. */
|
|
51
53
|
canChangeStatus?: boolean;
|
|
52
54
|
/** This item is awaiting a product decision or reply from a board manager. */
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { Spinner, Button, Textarea, Divider } from '@alliance-droid/svelte-component-library';
|
|
2
|
+
import { Spinner, Button, Textarea, Divider } from '@alliance-droid/svelte-component-library';
|
|
3
3
|
import type { BoardItem } from '../../api.js';
|
|
4
4
|
import type { FeedbackResponse } from '../../types.js';
|
|
5
5
|
import type { FeedbackBoardState } from './state.svelte.js';
|
|
6
6
|
import { timeAgo } from './utils.js';
|
|
7
|
+
import { safeScreenshotSrc } from './screenshot-src.js';
|
|
7
8
|
import StatusBadgeMenu from './StatusBadgeMenu.svelte';
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
@@ -13,6 +14,10 @@
|
|
|
13
14
|
|
|
14
15
|
let { board, item }: Props = $props();
|
|
15
16
|
|
|
17
|
+
// Client-side scheme allowlist over the server-provided URL (defense-in-depth
|
|
18
|
+
// for a reusable widget that puts this in an <a href>).
|
|
19
|
+
let screenshotSrc = $derived(safeScreenshotSrc(item.screenshotUrl));
|
|
20
|
+
|
|
16
21
|
let responses = $state<FeedbackResponse[]>([]);
|
|
17
22
|
let loadingResponses = $state(true);
|
|
18
23
|
|
|
@@ -101,27 +106,27 @@
|
|
|
101
106
|
|
|
102
107
|
let isAuthenticated = $derived(!!board.userEmail);
|
|
103
108
|
|
|
104
|
-
$effect(() => {
|
|
105
|
-
const itemId = item.id;
|
|
106
|
-
let cancelled = false;
|
|
107
|
-
|
|
108
|
-
// Consumers commonly keep the detail rail mounted and replace its item prop.
|
|
109
|
-
// Clear the old thread immediately so it is never shown under the new item.
|
|
110
|
-
responses = [];
|
|
111
|
-
loadingResponses = true;
|
|
112
|
-
|
|
113
|
-
void board.getResponses(itemId).then((nextResponses) => {
|
|
114
|
-
// A slower request for the previous selection must not overwrite the
|
|
115
|
-
// currently selected item's responses.
|
|
116
|
-
if (cancelled) return;
|
|
117
|
-
responses = nextResponses;
|
|
118
|
-
loadingResponses = false;
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
return () => {
|
|
122
|
-
cancelled = true;
|
|
123
|
-
};
|
|
124
|
-
});
|
|
109
|
+
$effect(() => {
|
|
110
|
+
const itemId = item.id;
|
|
111
|
+
let cancelled = false;
|
|
112
|
+
|
|
113
|
+
// Consumers commonly keep the detail rail mounted and replace its item prop.
|
|
114
|
+
// Clear the old thread immediately so it is never shown under the new item.
|
|
115
|
+
responses = [];
|
|
116
|
+
loadingResponses = true;
|
|
117
|
+
|
|
118
|
+
void board.getResponses(itemId).then((nextResponses) => {
|
|
119
|
+
// A slower request for the previous selection must not overwrite the
|
|
120
|
+
// currently selected item's responses.
|
|
121
|
+
if (cancelled) return;
|
|
122
|
+
responses = nextResponses;
|
|
123
|
+
loadingResponses = false;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return () => {
|
|
127
|
+
cancelled = true;
|
|
128
|
+
};
|
|
129
|
+
});
|
|
125
130
|
|
|
126
131
|
async function handleSubmitReply() {
|
|
127
132
|
if (!replyMessage.trim() || !isAuthenticated) return;
|
|
@@ -187,6 +192,16 @@
|
|
|
187
192
|
<h3 class="text-base font-semibold text-foreground">{item.title}</h3>
|
|
188
193
|
{/if}
|
|
189
194
|
<p class="text-sm leading-relaxed text-foreground whitespace-pre-wrap">{item.message}</p>
|
|
195
|
+
{#if screenshotSrc}
|
|
196
|
+
<a href={screenshotSrc} target="_blank" rel="noopener noreferrer" class="inline-block" title="Open full screenshot">
|
|
197
|
+
<img
|
|
198
|
+
src={screenshotSrc}
|
|
199
|
+
alt="Attached screenshot"
|
|
200
|
+
loading="lazy"
|
|
201
|
+
class="max-h-72 w-auto rounded-md border border-border object-contain"
|
|
202
|
+
/>
|
|
203
|
+
</a>
|
|
204
|
+
{/if}
|
|
190
205
|
<div class="flex items-center gap-1.5 pt-1 text-xs text-foreground-tertiary" aria-label="{item.votes} votes">
|
|
191
206
|
<i class="fa-solid fa-arrow-up" aria-hidden="true"></i>
|
|
192
207
|
<span class="font-medium text-foreground-secondary">{item.votes}</span>
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import type { BoardItem } from '../../api.js';
|
|
4
4
|
import type { FeedbackBoardState } from './state.svelte.js';
|
|
5
5
|
import { timeAgo } from './utils.js';
|
|
6
|
-
import StatusBadgeMenu from './StatusBadgeMenu.svelte';
|
|
7
|
-
import { canChangeItemStatus } from './capabilities.js';
|
|
6
|
+
import StatusBadgeMenu from './StatusBadgeMenu.svelte';
|
|
7
|
+
import { canChangeItemStatus } from './capabilities.js';
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
board: FeedbackBoardState;
|
|
@@ -14,13 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
let { board, item, onclick }: Props = $props();
|
|
16
16
|
|
|
17
|
-
let isClickable = $derived(!!onclick);
|
|
18
|
-
let canChangeStatus = $derived(canChangeItemStatus(board, item));
|
|
17
|
+
let isClickable = $derived(!!onclick);
|
|
18
|
+
let canChangeStatus = $derived(canChangeItemStatus(board, item));
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
22
|
-
<article
|
|
23
|
-
class:sf-editable={canChangeStatus}
|
|
22
|
+
<article
|
|
24
23
|
class="rounded-xl border border-border bg-surface p-4 transition-all {isClickable ? 'cursor-pointer hover:border-border-strong hover:bg-surface-hover' : ''}"
|
|
25
24
|
role={isClickable ? 'button' : undefined}
|
|
26
25
|
tabindex={isClickable ? 0 : undefined}
|
|
@@ -78,12 +77,10 @@
|
|
|
78
77
|
<StatusBadgeMenu {board} {item} />
|
|
79
78
|
</div>
|
|
80
79
|
|
|
81
|
-
<!-- Meta -->
|
|
82
|
-
{#if canChangeStatus}
|
|
83
|
-
<p class="sf-action-hint">
|
|
84
|
-
|
|
85
|
-
</p>
|
|
86
|
-
{/if}
|
|
80
|
+
<!-- Meta -->
|
|
81
|
+
{#if canChangeStatus && item.needsInput}
|
|
82
|
+
<p class="sf-action-hint">Needs your input</p>
|
|
83
|
+
{/if}
|
|
87
84
|
<div class="mt-3 flex items-center gap-3 text-xs text-foreground-tertiary">
|
|
88
85
|
{#if item.email}
|
|
89
86
|
<span class="inline-flex min-w-0 flex-1 items-center gap-1.5">
|
|
@@ -111,20 +108,16 @@
|
|
|
111
108
|
</div>
|
|
112
109
|
</article>
|
|
113
110
|
|
|
114
|
-
<style>
|
|
115
|
-
.sf-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
margin-top: 0.75rem;
|
|
125
|
-
font-size: 0.75rem;
|
|
126
|
-
width: fit-content;
|
|
127
|
-
}
|
|
111
|
+
<style>
|
|
112
|
+
.sf-action-hint {
|
|
113
|
+
color: #6d28d9;
|
|
114
|
+
background: #f3e8ff;
|
|
115
|
+
border-radius: 0.25rem;
|
|
116
|
+
padding: 0.15rem 0.4rem;
|
|
117
|
+
margin-top: 0.75rem;
|
|
118
|
+
font-size: 0.75rem;
|
|
119
|
+
width: fit-content;
|
|
120
|
+
}
|
|
128
121
|
.line-clamp-1,
|
|
129
122
|
.line-clamp-2 {
|
|
130
123
|
display: -webkit-box;
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
{:else if board.items.length === 0}
|
|
33
33
|
<div class="text-center py-12 text-foreground-tertiary">
|
|
34
34
|
<i class="fa-solid fa-inbox text-3xl"></i>
|
|
35
|
-
<p class="mt-2">{board.statusFilter === 'needs_input' ? 'No feedback needs your input' : 'No feedback yet'}</p>
|
|
36
|
-
<p class="text-sm">{board.statusFilter === 'needs_input' ? 'You’re all caught up. Choose another status to see more feedback.' : 'Be the first to submit!'}</p>
|
|
35
|
+
<p class="mt-2">{board.statusFilter === 'needs_input' ? 'No feedback needs your input' : 'No feedback yet'}</p>
|
|
36
|
+
<p class="text-sm">{board.statusFilter === 'needs_input' ? 'You’re all caught up. Choose another status to see more feedback.' : 'Be the first to submit!'}</p>
|
|
37
37
|
</div>
|
|
38
38
|
{:else}
|
|
39
39
|
<div class="flex flex-col gap-2">
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
|
|
9
9
|
let { board }: Props = $props();
|
|
10
10
|
|
|
11
|
-
const statusOptions = $derived([
|
|
11
|
+
const statusOptions = $derived([
|
|
12
12
|
{ value: "open", label: "Open" },
|
|
13
13
|
{ value: "resolved", label: "Done" },
|
|
14
|
-
{ value: "all", label: "All" },
|
|
15
|
-
...(board.canManageStatus ? [{ value: "needs_input", label: "Needs my input" }] : []),
|
|
16
|
-
]);
|
|
14
|
+
{ value: "all", label: "All" },
|
|
15
|
+
...(board.canManageStatus ? [{ value: "needs_input", label: "Needs my input" }] : []),
|
|
16
|
+
]);
|
|
17
17
|
|
|
18
18
|
const sortOptions = [
|
|
19
19
|
{ value: "votes", label: "Most Votes" },
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
|
|
62
62
|
<Divider class="my-2" />
|
|
63
63
|
|
|
64
|
-
<div class="sf-filter-group">
|
|
65
|
-
{#if board.canManageStatus}
|
|
66
|
-
<p class="sf-editable-legend"><span aria-hidden="true"></span>Purple
|
|
67
|
-
{/if}
|
|
64
|
+
<div class="sf-filter-group">
|
|
65
|
+
{#if board.canManageStatus}
|
|
66
|
+
<p class="sf-editable-legend"><span aria-hidden="true"></span>Purple status controls mark statuses you can change.</p>
|
|
67
|
+
{/if}
|
|
68
68
|
<Select
|
|
69
69
|
label="Status"
|
|
70
70
|
class="rounded-lg border-0 bg-surface-hover"
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
</div>
|
|
81
81
|
</aside>
|
|
82
82
|
|
|
83
|
-
<style>
|
|
84
|
-
.sf-editable-legend { font-size: 0.75rem; display: flex; gap: 0.5rem; align-items: center; }
|
|
85
|
-
.sf-editable-legend span { width: 0.6rem; height: 0.6rem; background: #8b5cf6; border-radius: 50%; flex-shrink: 0; }
|
|
83
|
+
<style>
|
|
84
|
+
.sf-editable-legend { font-size: 0.75rem; display: flex; gap: 0.5rem; align-items: center; }
|
|
85
|
+
.sf-editable-legend span { width: 0.6rem; height: 0.6rem; background: #8b5cf6; border-radius: 50%; flex-shrink: 0; }
|
|
86
86
|
/* Spacing/padding is bundled in the component (not Tailwind utilities) so it
|
|
87
87
|
applies regardless of whether the consuming app's Tailwind generates these
|
|
88
88
|
classes. gap → space between the Status and Sort By selects; the 1.25rem
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Status badge that doubles as a status-change control for privileged users.
|
|
4
4
|
*
|
|
5
|
-
* Items without an authorized board decision have a read-only badge.
|
|
6
|
-
* Editable items have a purple button: clicking it
|
|
5
|
+
* Items without an authorized board decision have a read-only badge.
|
|
6
|
+
* Editable items have a purple button: clicking it
|
|
7
7
|
* loads the transitions they may make and shows them in a menu; picking one
|
|
8
8
|
* applies it (prompting for a reason first when the transition requires one).
|
|
9
9
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* library's Select uses.
|
|
14
14
|
*
|
|
15
15
|
* Used in both the detail panel and the list row, so a ProductOwner can change
|
|
16
|
-
* status without opening the item. Transitions refresh on demand (on each open)
|
|
16
|
+
* status without opening the item. Transitions refresh on demand (on each open)
|
|
17
17
|
* unless `loadOnMount` is set — the list stays lazy so it doesn't fire a
|
|
18
18
|
* request per row.
|
|
19
19
|
*/
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
import { onMount } from 'svelte';
|
|
22
22
|
import type { BoardItem, BoardTransition } from '../../api.js';
|
|
23
23
|
import type { FeedbackBoardState } from './state.svelte.js';
|
|
24
|
-
import { getStatusBadgeVariant, internalStatusLabel } from './utils.js';
|
|
25
|
-
import { canChangeItemStatus } from './capabilities.js';
|
|
24
|
+
import { getStatusBadgeVariant, internalStatusLabel } from './utils.js';
|
|
25
|
+
import { canChangeItemStatus } from './capabilities.js';
|
|
26
26
|
|
|
27
27
|
interface Props {
|
|
28
28
|
board: FeedbackBoardState;
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
let menuEl: HTMLDivElement | undefined = $state();
|
|
46
46
|
|
|
47
47
|
// Read from the board's shared cache so detail + list reuse the same fetch.
|
|
48
|
-
let txns = $derived(board.transitions[item.id]?.current.status === item.status ? board.transitions[item.id] : null);
|
|
49
|
-
let canChange = $derived(canChangeItemStatus(board, item));
|
|
48
|
+
let txns = $derived(board.transitions[item.id]?.current.status === item.status ? board.transitions[item.id] : null);
|
|
49
|
+
let canChange = $derived(canChangeItemStatus(board, item));
|
|
50
50
|
// Everyone sees the item's real granular status — `item.status` is present on
|
|
51
51
|
// every board item (the board endpoint returns it), so all rows render their
|
|
52
52
|
// actual status on first paint, no privilege or transitions fetch required.
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
reason = '';
|
|
134
134
|
// Open immediately so the spinner is visible while transitions load.
|
|
135
135
|
menuOpen = true;
|
|
136
|
-
await reload();
|
|
136
|
+
await reload();
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
async function reload() {
|
|
@@ -187,20 +187,20 @@
|
|
|
187
187
|
}
|
|
188
188
|
</script>
|
|
189
189
|
|
|
190
|
-
{#if canChange}
|
|
190
|
+
{#if canChange}
|
|
191
191
|
<button
|
|
192
192
|
bind:this={triggerEl}
|
|
193
193
|
type="button"
|
|
194
|
-
class="sf-status-editable inline-flex items-center gap-1.5 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:opacity-60"
|
|
195
|
-
onclick={toggleMenu}
|
|
196
|
-
onkeydown={(e) => e.stopPropagation()}
|
|
194
|
+
class="sf-status-editable inline-flex items-center gap-1.5 rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40 disabled:opacity-60"
|
|
195
|
+
onclick={toggleMenu}
|
|
196
|
+
onkeydown={(e) => e.stopPropagation()}
|
|
197
197
|
aria-haspopup="listbox"
|
|
198
198
|
aria-expanded={menuOpen}
|
|
199
199
|
disabled={applying}
|
|
200
|
-
title="You can change this status"
|
|
201
|
-
aria-label="Change status: {badgeLabel}"
|
|
202
|
-
>
|
|
203
|
-
<span>{badgeLabel}</span>
|
|
200
|
+
title="You can change this status"
|
|
201
|
+
aria-label="Change status: {badgeLabel}"
|
|
202
|
+
>
|
|
203
|
+
<span>{badgeLabel}</span>
|
|
204
204
|
<i class="fa-solid fa-chevron-down text-[0.625rem] text-foreground-tertiary" aria-hidden="true"></i>
|
|
205
205
|
</button>
|
|
206
206
|
|
|
@@ -237,7 +237,7 @@
|
|
|
237
237
|
</Button>
|
|
238
238
|
</div>
|
|
239
239
|
</div>
|
|
240
|
-
{:else if txns && txns.targets.length > 0 && !loadFailed}
|
|
240
|
+
{:else if txns && txns.targets.length > 0 && !loadFailed}
|
|
241
241
|
{#each txns.targets as target (target.status)}
|
|
242
242
|
<button
|
|
243
243
|
type="button"
|
|
@@ -272,18 +272,18 @@
|
|
|
272
272
|
<Badge variant={getStatusBadgeVariant(item.publicStatus)} size="sm">
|
|
273
273
|
{badgeLabel}
|
|
274
274
|
</Badge>
|
|
275
|
-
{/if}
|
|
276
|
-
|
|
277
|
-
<style>
|
|
278
|
-
.sf-status-editable {
|
|
279
|
-
flex-shrink: 0;
|
|
280
|
-
white-space: nowrap;
|
|
281
|
-
color: #6d28d9;
|
|
282
|
-
background: #f3e8ff;
|
|
283
|
-
border: 1px solid #a78bfa;
|
|
284
|
-
padding: 0.2rem 0.5rem;
|
|
285
|
-
font-size: 0.75rem;
|
|
286
|
-
font-weight: 600;
|
|
287
|
-
}
|
|
288
|
-
.sf-status-editable:hover { background: #e9d5ff; }
|
|
289
|
-
</style>
|
|
275
|
+
{/if}
|
|
276
|
+
|
|
277
|
+
<style>
|
|
278
|
+
.sf-status-editable {
|
|
279
|
+
flex-shrink: 0;
|
|
280
|
+
white-space: nowrap;
|
|
281
|
+
color: #6d28d9;
|
|
282
|
+
background: #f3e8ff;
|
|
283
|
+
border: 1px solid #a78bfa;
|
|
284
|
+
padding: 0.2rem 0.5rem;
|
|
285
|
+
font-size: 0.75rem;
|
|
286
|
+
font-weight: 600;
|
|
287
|
+
}
|
|
288
|
+
.sf-status-editable:hover { background: #e9d5ff; }
|
|
289
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side allowlist for a board item's screenshot URL.
|
|
3
|
+
*
|
|
4
|
+
* Defense-in-depth: `screenshotUrl` is validated server-side (status-feedback-
|
|
5
|
+
* admin), but this is a reusable package consumed by multiple apps, and
|
|
6
|
+
* `<FeedbackDetail>` puts the value in an `<a href>` — a script-execution
|
|
7
|
+
* surface. A shared widget must not be the single layer that trusts an upstream
|
|
8
|
+
* it can't guarantee, so validate the scheme here too: allow only a safe image
|
|
9
|
+
* source (an `https` URL, or a base64 raster-image data URL) and reject
|
|
10
|
+
* `javascript:`, `data:text/html`, `data:image/svg+xml` (SVG can execute
|
|
11
|
+
* script), plain `http:`, and non-strings.
|
|
12
|
+
*
|
|
13
|
+
* Origin restriction (limiting `https` to the blob container) is enforced
|
|
14
|
+
* server-side, where the configured origin is known — the client can't see it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function safeScreenshotSrc(value: unknown): string | null;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side allowlist for a board item's screenshot URL.
|
|
3
|
+
*
|
|
4
|
+
* Defense-in-depth: `screenshotUrl` is validated server-side (status-feedback-
|
|
5
|
+
* admin), but this is a reusable package consumed by multiple apps, and
|
|
6
|
+
* `<FeedbackDetail>` puts the value in an `<a href>` — a script-execution
|
|
7
|
+
* surface. A shared widget must not be the single layer that trusts an upstream
|
|
8
|
+
* it can't guarantee, so validate the scheme here too: allow only a safe image
|
|
9
|
+
* source (an `https` URL, or a base64 raster-image data URL) and reject
|
|
10
|
+
* `javascript:`, `data:text/html`, `data:image/svg+xml` (SVG can execute
|
|
11
|
+
* script), plain `http:`, and non-strings.
|
|
12
|
+
*
|
|
13
|
+
* Origin restriction (limiting `https` to the blob container) is enforced
|
|
14
|
+
* server-side, where the configured origin is known — the client can't see it.
|
|
15
|
+
*/
|
|
16
|
+
export function safeScreenshotSrc(value) {
|
|
17
|
+
if (typeof value !== 'string')
|
|
18
|
+
return null;
|
|
19
|
+
if (/^data:image\/(?:png|jpeg|gif|webp);base64,/i.test(value))
|
|
20
|
+
return value;
|
|
21
|
+
if (/^https:\/\//i.test(value))
|
|
22
|
+
return value;
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
@@ -24,7 +24,7 @@ export interface FeedbackBoardOptions {
|
|
|
24
24
|
* Returns the current user's Alliance OIDC **access token** — whose audience
|
|
25
25
|
* covers `status-feedback-admin` (an id token is rejected by the backend) — or
|
|
26
26
|
* null if they aren't signed in / not privileged. When provided, privileged
|
|
27
|
-
* users (Admin / ProductOwner) can accept, reopen, or decline feedback. Called
|
|
27
|
+
* users (Admin / ProductOwner) can accept, approve QA/UAT, reopen, or decline feedback. Called
|
|
28
28
|
* on demand so the host controls token refresh.
|
|
29
29
|
*/
|
|
30
30
|
getAuthToken?: () => string | null | Promise<string | null>;
|
package/package.json
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@alliance-droid/status-feedback-system",
|
|
3
|
-
"version": "4.3.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Drop-in system status pages and user feedback forms for SvelteKit apps",
|
|
6
|
-
"svelte": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"svelte": "./dist/index.js"
|
|
12
|
-
},
|
|
13
|
-
"./sveltekit/server": {
|
|
14
|
-
"types": "./dist/sveltekit/server.d.ts",
|
|
15
|
-
"default": "./dist/sveltekit/server.js"
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"!dist/**/*.test.*",
|
|
21
|
-
"CHANGELOG.md"
|
|
22
|
-
],
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "svelte-package -o dist && publint",
|
|
25
|
-
"lint": "eslint src",
|
|
26
|
-
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
27
|
-
"test": "vitest run",
|
|
28
|
-
"test:watch": "vitest",
|
|
29
|
-
"prepublishOnly": "npm run build"
|
|
30
|
-
},
|
|
31
|
-
"peerDependencies": {
|
|
32
|
-
"@alliance-droid/svelte-component-library": ">=1.3.2",
|
|
33
|
-
"svelte": "^5.55.7"
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@alliance-droid/svelte-component-library": "^1.20.2",
|
|
37
|
-
"@eslint/js": "^10.0.1",
|
|
38
|
-
"@sveltejs/package": "^2.5.8",
|
|
39
|
-
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
40
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
41
|
-
"@testing-library/svelte": "^5.4.2",
|
|
42
|
-
"eslint": "^10.10.0",
|
|
43
|
-
"eslint-plugin-svelte": "^3.23.0",
|
|
44
|
-
"globals": "^17.12.0",
|
|
45
|
-
"jsdom": "^29.1.1",
|
|
46
|
-
"publint": "^0.3.24",
|
|
47
|
-
"svelte": "^5.57.0",
|
|
48
|
-
"svelte-check": "^4.7.6",
|
|
49
|
-
"typescript": "^5.9.3",
|
|
50
|
-
"typescript-eslint": "^8.70.0",
|
|
51
|
-
"vite": "^7.3.6",
|
|
52
|
-
"vitest": "^4.1.11"
|
|
53
|
-
},
|
|
54
|
-
"keywords": [
|
|
55
|
-
"svelte",
|
|
56
|
-
"sveltekit",
|
|
57
|
-
"status-page",
|
|
58
|
-
"feedback",
|
|
59
|
-
"uptime"
|
|
60
|
-
],
|
|
61
|
-
"author": "Alliance Technical Group",
|
|
62
|
-
"license": "UNLICENSED",
|
|
63
|
-
"publishConfig": {
|
|
64
|
-
"access": "restricted"
|
|
65
|
-
},
|
|
66
|
-
"repository": {
|
|
67
|
-
"type": "git",
|
|
68
|
-
"url": "git+https://alliance.ghe.com/alliance/status-feedback-system.git"
|
|
69
|
-
}
|
|
70
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@alliance-droid/status-feedback-system",
|
|
3
|
+
"version": "4.3.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Drop-in system status pages and user feedback forms for SvelteKit apps",
|
|
6
|
+
"svelte": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"svelte": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./sveltekit/server": {
|
|
14
|
+
"types": "./dist/sveltekit/server.d.ts",
|
|
15
|
+
"default": "./dist/sveltekit/server.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"!dist/**/*.test.*",
|
|
21
|
+
"CHANGELOG.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "svelte-package -o dist && publint",
|
|
25
|
+
"lint": "eslint src",
|
|
26
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@alliance-droid/svelte-component-library": ">=1.3.2",
|
|
33
|
+
"svelte": "^5.55.7"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@alliance-droid/svelte-component-library": "^1.20.2",
|
|
37
|
+
"@eslint/js": "^10.0.1",
|
|
38
|
+
"@sveltejs/package": "^2.5.8",
|
|
39
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
40
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
41
|
+
"@testing-library/svelte": "^5.4.2",
|
|
42
|
+
"eslint": "^10.10.0",
|
|
43
|
+
"eslint-plugin-svelte": "^3.23.0",
|
|
44
|
+
"globals": "^17.12.0",
|
|
45
|
+
"jsdom": "^29.1.1",
|
|
46
|
+
"publint": "^0.3.24",
|
|
47
|
+
"svelte": "^5.57.0",
|
|
48
|
+
"svelte-check": "^4.7.6",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"typescript-eslint": "^8.70.0",
|
|
51
|
+
"vite": "^7.3.6",
|
|
52
|
+
"vitest": "^4.1.11"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"svelte",
|
|
56
|
+
"sveltekit",
|
|
57
|
+
"status-page",
|
|
58
|
+
"feedback",
|
|
59
|
+
"uptime"
|
|
60
|
+
],
|
|
61
|
+
"author": "Alliance Technical Group",
|
|
62
|
+
"license": "UNLICENSED",
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "restricted"
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://alliance.ghe.com/alliance/status-feedback-system.git"
|
|
69
|
+
}
|
|
70
|
+
}
|