@alliance-droid/status-feedback-system 4.0.1 → 4.0.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/dist/api.d.ts CHANGED
@@ -70,6 +70,8 @@ export interface BoardTransitions {
70
70
  publicStatus: PublicStatus;
71
71
  };
72
72
  targets: BoardTransition[];
73
+ /** Whether this user may manage status on this project (drives the UI affordance). */
74
+ canManage: boolean;
73
75
  }
74
76
  /** Result of applying a status change from the board. */
75
77
  export interface BoardStatusUpdate {
@@ -51,7 +51,9 @@
51
51
  let posKey = $derived(`${menuOpen}:${loading}:${pendingTarget?.status ?? ''}:${txns?.targets.length ?? 0}`);
52
52
 
53
53
  onMount(() => {
54
- if (loadOnMount && board.canManageStatus) void board.loadTransitions(item.id);
54
+ // Eagerly load in the detail view: this both readies the menu and lets the
55
+ // board confirm privilege (loadTransitions is a no-op without a token).
56
+ if (loadOnMount) void board.loadTransitions(item.id);
55
57
  });
56
58
 
57
59
  /** Teleport the menu to <body> and keep it positioned under the trigger.
@@ -59,7 +59,12 @@ export interface FeedbackBoardState {
59
59
  pastedImage?: string | null;
60
60
  }): Promise<void>;
61
61
  submitResponse(itemId: string, message: string, pastedImage?: string | null): Promise<void>;
62
- /** Whether a token provider was configured (status controls may be available). */
62
+ /**
63
+ * Whether the current user may manage status — resolved against the backend
64
+ * (valid token AND a qualifying role + project access), not merely whether a
65
+ * token getter was provided. Starts `false` and flips `true` once confirmed,
66
+ * so UI can gate the status control by actual privilege.
67
+ */
63
68
  readonly canManageStatus: boolean;
64
69
  /** Cached allowed transitions per item id, populated by loadTransitions. */
65
70
  readonly transitions: Record<string, BoardTransitions>;
@@ -26,6 +26,9 @@ export function createFeedbackBoard(options) {
26
26
  let voteErrors = $state({});
27
27
  let loadMoreError = $state('');
28
28
  let transitions = $state({});
29
+ // Resolved status-management privilege (one-time backend probe, not token presence).
30
+ let statusPrivilege = $state(false);
31
+ let privilegeProbed = false;
29
32
  // Response cache
30
33
  const responseCache = {};
31
34
  // Track in-flight votes to prevent duplicate requests. A plain Set is correct
@@ -46,6 +49,7 @@ export function createFeedbackBoard(options) {
46
49
  items = data.items;
47
50
  total = data.total;
48
51
  categories = data.categories;
52
+ void ensureStatusPrivilege();
49
53
  }
50
54
  catch (e) {
51
55
  error = e instanceof Error ? e.message : 'Failed to load';
@@ -180,13 +184,32 @@ export function createFeedbackBoard(options) {
180
184
  try {
181
185
  const result = await client.getBoardTransitions(itemId, token);
182
186
  transitions = { ...transitions, [itemId]: result };
187
+ // The backend tells us whether this user may manage status (200 either
188
+ // way — no 403 console noise for ordinary signed-in users).
189
+ statusPrivilege = result.canManage;
183
190
  return result;
184
191
  }
185
192
  catch {
186
- // 401/403 → not signed in or not privileged for this item: no control.
193
+ // 401 (no/invalid token) or network error → no control.
187
194
  return null;
188
195
  }
189
196
  }
197
+ /**
198
+ * One-time probe so the UI can gate the status control by *actual* privilege
199
+ * (valid token + qualifying role + project access), not merely token presence.
200
+ * Loads the first item's transitions once; a non-null result confirms the
201
+ * backend authorized the user. No token / no items / a 403 leaves it `false`.
202
+ */
203
+ async function ensureStatusPrivilege() {
204
+ if (privilegeProbed || !getAuthToken)
205
+ return;
206
+ const sample = items[0];
207
+ if (!sample)
208
+ return;
209
+ privilegeProbed = true;
210
+ // loadTransitions sets statusPrivilege from the result's canManage flag.
211
+ await loadTransitions(sample.id);
212
+ }
190
213
  async function setStatus(itemId, status, reason) {
191
214
  const token = await resolveToken();
192
215
  if (!token)
@@ -226,7 +249,7 @@ export function createFeedbackBoard(options) {
226
249
  getResponses,
227
250
  submitFeedback,
228
251
  submitResponse,
229
- get canManageStatus() { return !!getAuthToken; },
252
+ get canManageStatus() { return statusPrivilege; },
230
253
  get transitions() { return transitions; },
231
254
  loadTransitions,
232
255
  setStatus,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alliance-droid/status-feedback-system",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "type": "module",
5
5
  "description": "Drop-in system status pages and user feedback forms for SvelteKit apps",
6
6
  "svelte": "./dist/index.js",