@alliance-droid/status-feedback-system 2.3.2 → 3.0.0

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.
@@ -1,8 +1,13 @@
1
1
  interface Props {
2
2
  apiUrl: string;
3
3
  apiKey: string;
4
- /** Explicit enable/disable — overrides auto-detection */
5
- enabled?: boolean | undefined;
4
+ /**
5
+ * The sole gate for the widget — wire it to your role/permission check
6
+ * (e.g. `enabled={userHasFeedbackRole}`). When false, nothing renders and
7
+ * no listeners attach. A non-empty `userEmail` is also required. There is
8
+ * no environment auto-detection.
9
+ */
10
+ enabled: boolean;
6
11
  /** Max console errors to capture */
7
12
  maxConsoleErrors?: number;
8
13
  /** Show onboarding toast on first visit */
@@ -1,110 +1,110 @@
1
- <script lang="ts">
2
- /**
3
- * All-in-one feedback board component.
4
- * Composes the board sub-components (FeedbackSidebar, FeedbackList,
5
- * FeedbackSubmitForm) with a shared FeedbackBoardState.
6
- */
7
- import { untrack } from 'svelte';
8
- import { Button } from '@alliance-droid/svelte-component-library';
9
- import type { BoardItem } from '../api.js';
10
- import { createFeedbackBoard, type CategoryConfig, type FeedbackBoardState } from './board/index.js';
11
- import FeedbackSidebar from './board/FeedbackSidebar.svelte';
12
- import FeedbackList from './board/FeedbackList.svelte';
13
- import FeedbackDetail from './board/FeedbackDetail.svelte';
14
- import FeedbackSubmitForm from './board/FeedbackSubmitForm.svelte';
15
-
16
- interface Props {
17
- apiUrl: string;
18
- apiKey: string;
19
- /** Authenticated user email. Required for submitting feedback and replies. */
20
- userEmail?: string;
21
- /** Category labels and icons — order determines sidebar order */
22
- categoryConfig?: Record<string, CategoryConfig>;
23
- /** Title shown at the top */
24
- title?: string;
25
- /** Show the submit form inline */
26
- showSubmit?: boolean;
27
- /** Available categories for the submit form */
28
- submitCategories?: string[];
29
- }
30
-
31
- let {
32
- apiUrl,
33
- apiKey,
34
- userEmail,
35
- categoryConfig = {},
36
- title = 'Feedback Board',
37
- showSubmit = true,
38
- submitCategories = [],
39
- }: Props = $props();
40
-
41
- let board: FeedbackBoardState = $derived(
42
- createFeedbackBoard({ apiUrl, apiKey, userEmail, categoryConfig })
43
- );
44
-
45
- let showForm = $state(false);
46
- let selectedItem = $state<BoardItem | null>(null);
47
-
48
- function selectItem(item: BoardItem) {
49
- selectedItem = item;
50
- }
51
-
52
- function clearSelection() {
53
- selectedItem = null;
54
- }
55
- </script>
56
-
57
- <div class="flex gap-6 min-h-[400px]">
58
- <!-- Sidebar -->
59
- <aside class="w-56 flex-shrink-0">
60
- <FeedbackSidebar {board} />
61
- </aside>
62
-
63
- <!-- Main -->
64
- <main class="flex-1 min-w-0">
65
- <div class="flex items-start justify-between mb-4">
66
- <div>
67
- {#if board.activeCategory}
68
- <h2 class="text-xl font-bold flex items-center gap-2">
69
- <i class={board.getCategoryIcon(board.activeCategory)}></i>
70
- {board.getCategoryLabel(board.activeCategory)}
71
- </h2>
72
- {#if board.getCategoryDescription(board.activeCategory)}
73
- <p class="text-sm text-foreground-secondary mt-1">{board.getCategoryDescription(board.activeCategory)}</p>
74
- {/if}
75
- {:else if title}
76
- <h2 class="text-xl font-bold">{title}</h2>
77
- {/if}
78
- </div>
79
- {#if showSubmit}
80
- <Button variant="primary" onclick={() => (showForm = !showForm)}>
81
- <i class="fa-solid fa-plus mr-1.5"></i> Submit
82
- </Button>
83
- {/if}
84
- </div>
85
-
86
- <!-- Submit form -->
87
- {#if showForm}
88
- <div class="mb-4">
89
- <FeedbackSubmitForm {board} categories={submitCategories} />
90
- </div>
91
- {/if}
92
-
93
- <!-- Items / Detail -->
94
- {#if selectedItem}
95
- <div class="mb-4">
96
- <button
97
- onclick={clearSelection}
98
- class="text-sm text-foreground-secondary hover:text-foreground transition-colors mb-3"
99
- >
100
- <i class="fa-solid fa-arrow-left mr-1" aria-hidden="true"></i> Back to list
101
- </button>
102
- <div class="rounded-xl border border-border bg-surface p-5">
103
- <FeedbackDetail {board} item={selectedItem} />
104
- </div>
105
- </div>
106
- {:else}
107
- <FeedbackList {board} onSelect={selectItem} />
108
- {/if}
109
- </main>
110
- </div>
1
+ <script lang="ts">
2
+ /**
3
+ * All-in-one feedback board component.
4
+ * Composes the board sub-components (FeedbackSidebar, FeedbackList,
5
+ * FeedbackSubmitForm) with a shared FeedbackBoardState.
6
+ */
7
+ import { untrack } from 'svelte';
8
+ import { Button } from '@alliance-droid/svelte-component-library';
9
+ import type { BoardItem } from '../api.js';
10
+ import { createFeedbackBoard, type CategoryConfig, type FeedbackBoardState } from './board/index.js';
11
+ import FeedbackSidebar from './board/FeedbackSidebar.svelte';
12
+ import FeedbackList from './board/FeedbackList.svelte';
13
+ import FeedbackDetail from './board/FeedbackDetail.svelte';
14
+ import FeedbackSubmitForm from './board/FeedbackSubmitForm.svelte';
15
+
16
+ interface Props {
17
+ apiUrl: string;
18
+ apiKey: string;
19
+ /** Authenticated user email. Required for submitting feedback and replies. */
20
+ userEmail?: string;
21
+ /** Category labels and icons — order determines sidebar order */
22
+ categoryConfig?: Record<string, CategoryConfig>;
23
+ /** Title shown at the top */
24
+ title?: string;
25
+ /** Show the submit form inline */
26
+ showSubmit?: boolean;
27
+ /** Available categories for the submit form */
28
+ submitCategories?: string[];
29
+ }
30
+
31
+ let {
32
+ apiUrl,
33
+ apiKey,
34
+ userEmail,
35
+ categoryConfig = {},
36
+ title = 'Feedback Board',
37
+ showSubmit = true,
38
+ submitCategories = [],
39
+ }: Props = $props();
40
+
41
+ let board: FeedbackBoardState = $derived(
42
+ createFeedbackBoard({ apiUrl, apiKey, userEmail, categoryConfig })
43
+ );
44
+
45
+ let showForm = $state(false);
46
+ let selectedItem = $state<BoardItem | null>(null);
47
+
48
+ function selectItem(item: BoardItem) {
49
+ selectedItem = item;
50
+ }
51
+
52
+ function clearSelection() {
53
+ selectedItem = null;
54
+ }
55
+ </script>
56
+
57
+ <div class="flex gap-6 min-h-[400px]">
58
+ <!-- Sidebar -->
59
+ <aside class="w-56 flex-shrink-0">
60
+ <FeedbackSidebar {board} />
61
+ </aside>
62
+
63
+ <!-- Main -->
64
+ <main class="flex-1 min-w-0">
65
+ <div class="flex items-start justify-between mb-4">
66
+ <div>
67
+ {#if board.activeCategory}
68
+ <h2 class="text-xl font-bold flex items-center gap-2">
69
+ <i class={board.getCategoryIcon(board.activeCategory)}></i>
70
+ {board.getCategoryLabel(board.activeCategory)}
71
+ </h2>
72
+ {#if board.getCategoryDescription(board.activeCategory)}
73
+ <p class="text-sm text-foreground-secondary mt-1">{board.getCategoryDescription(board.activeCategory)}</p>
74
+ {/if}
75
+ {:else if title}
76
+ <h2 class="text-xl font-bold">{title}</h2>
77
+ {/if}
78
+ </div>
79
+ {#if showSubmit}
80
+ <Button variant="primary" onclick={() => (showForm = !showForm)}>
81
+ <i class="fa-solid fa-plus mr-1.5"></i> Submit
82
+ </Button>
83
+ {/if}
84
+ </div>
85
+
86
+ <!-- Submit form -->
87
+ {#if showForm}
88
+ <div class="mb-4">
89
+ <FeedbackSubmitForm {board} categories={submitCategories} />
90
+ </div>
91
+ {/if}
92
+
93
+ <!-- Items / Detail -->
94
+ {#if selectedItem}
95
+ <div class="mb-4">
96
+ <button
97
+ onclick={clearSelection}
98
+ class="text-sm text-foreground-secondary hover:text-foreground transition-colors mb-3"
99
+ >
100
+ <i class="fa-solid fa-arrow-left mr-1" aria-hidden="true"></i> Back to list
101
+ </button>
102
+ <div class="rounded-xl border border-border bg-surface p-5">
103
+ <FeedbackDetail {board} item={selectedItem} />
104
+ </div>
105
+ </div>
106
+ {:else}
107
+ <FeedbackList {board} onSelect={selectItem} />
108
+ {/if}
109
+ </main>
110
+ </div>
@@ -1,72 +1,72 @@
1
- <script lang="ts">
2
- import FeedbackForm from './FeedbackForm.svelte';
3
-
4
- interface Props {
5
- apiKey: string;
6
- apiUrl: string;
7
- categories?: string[];
8
- userEmail?: string;
9
- position?: 'bottom-right' | 'bottom-left';
10
- }
11
-
12
- let {
13
- apiKey,
14
- apiUrl,
15
- categories,
16
- userEmail,
17
- position = 'bottom-right',
18
- }: Props = $props();
19
-
20
- let open = $state(false);
21
-
22
- let positionStyles = $derived(
23
- position === 'bottom-right'
24
- ? 'right: 1rem; bottom: 1rem;'
25
- : 'left: 1rem; bottom: 1rem;'
26
- );
27
-
28
- let panelStyles = $derived(
29
- position === 'bottom-right'
30
- ? 'right: 0; bottom: 3.5rem;'
31
- : 'left: 0; bottom: 3.5rem;'
32
- );
33
- </script>
34
-
35
- <div class="fixed z-50" style={positionStyles}>
36
- {#if open}
37
- <div class="absolute w-80 rounded-lg border border-border bg-background shadow-lg" style={panelStyles}>
38
- <div class="flex items-center justify-between border-b border-border px-4 py-3">
39
- <h3 class="text-sm font-semibold text-foreground">
40
- <i class="fa-solid fa-comment mr-1.5 text-primary"></i>
41
- Send Feedback
42
- </h3>
43
- <button
44
- onclick={() => (open = false)}
45
- class="text-foreground-tertiary transition-colors hover:text-foreground"
46
- aria-label="Close feedback"
47
- >
48
- <i class="fa-solid fa-xmark"></i>
49
- </button>
50
- </div>
51
- <div class="p-4">
52
- <FeedbackForm
53
- {apiKey}
54
- {apiUrl}
55
- {categories}
56
- {userEmail}
57
- onSuccess={() => {
58
- setTimeout(() => (open = false), 2000);
59
- }}
60
- />
61
- </div>
62
- </div>
63
- {/if}
64
-
65
- <button
66
- onclick={() => (open = !open)}
67
- class="flex h-12 w-12 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-transform hover:scale-105"
68
- aria-label="Send feedback"
69
- >
70
- <i class="fa-solid {open ? 'fa-xmark' : 'fa-comment'} text-lg"></i>
71
- </button>
72
- </div>
1
+ <script lang="ts">
2
+ import FeedbackForm from './FeedbackForm.svelte';
3
+
4
+ interface Props {
5
+ apiKey: string;
6
+ apiUrl: string;
7
+ categories?: string[];
8
+ userEmail?: string;
9
+ position?: 'bottom-right' | 'bottom-left';
10
+ }
11
+
12
+ let {
13
+ apiKey,
14
+ apiUrl,
15
+ categories,
16
+ userEmail,
17
+ position = 'bottom-right',
18
+ }: Props = $props();
19
+
20
+ let open = $state(false);
21
+
22
+ let positionStyles = $derived(
23
+ position === 'bottom-right'
24
+ ? 'right: 1rem; bottom: 1rem;'
25
+ : 'left: 1rem; bottom: 1rem;'
26
+ );
27
+
28
+ let panelStyles = $derived(
29
+ position === 'bottom-right'
30
+ ? 'right: 0; bottom: 3.5rem;'
31
+ : 'left: 0; bottom: 3.5rem;'
32
+ );
33
+ </script>
34
+
35
+ <div class="fixed z-50" style={positionStyles}>
36
+ {#if open}
37
+ <div class="absolute w-80 rounded-lg border border-border bg-background shadow-lg" style={panelStyles}>
38
+ <div class="flex items-center justify-between border-b border-border px-4 py-3">
39
+ <h3 class="text-sm font-semibold text-foreground">
40
+ <i class="fa-solid fa-comment mr-1.5 text-primary"></i>
41
+ Send Feedback
42
+ </h3>
43
+ <button
44
+ onclick={() => (open = false)}
45
+ class="text-foreground-tertiary transition-colors hover:text-foreground"
46
+ aria-label="Close feedback"
47
+ >
48
+ <i class="fa-solid fa-xmark"></i>
49
+ </button>
50
+ </div>
51
+ <div class="p-4">
52
+ <FeedbackForm
53
+ {apiKey}
54
+ {apiUrl}
55
+ {categories}
56
+ {userEmail}
57
+ onSuccess={() => {
58
+ setTimeout(() => (open = false), 2000);
59
+ }}
60
+ />
61
+ </div>
62
+ </div>
63
+ {/if}
64
+
65
+ <button
66
+ onclick={() => (open = !open)}
67
+ class="flex h-12 w-12 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-transform hover:scale-105"
68
+ aria-label="Send feedback"
69
+ >
70
+ <i class="fa-solid {open ? 'fa-xmark' : 'fa-comment'} text-lg"></i>
71
+ </button>
72
+ </div>