jat-feedback 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -11,25 +11,11 @@ User clicks "Report Bug" → Widget captures context → POST /api/feedback/repo
11
11
 
12
12
  ## Install
13
13
 
14
- ### Option 1: CDN (recommended)
15
-
16
- ```html
17
- <script src="https://unpkg.com/jat-feedback@^1/dist/jat-feedback.js"></script>
18
- <jat-feedback project="my-app"></jat-feedback>
19
- <script>
20
- document.querySelector('jat-feedback').setAttribute('endpoint', location.origin);
21
- </script>
22
- ```
23
-
24
- ### Option 2: npm
25
-
26
14
  ```bash
27
15
  npm install jat-feedback
28
16
  ```
29
17
 
30
- ```js
31
- import 'jat-feedback';
32
- ```
18
+ The package includes the widget bundle, Supabase migration, and edge function — all three are needed for the full pipeline.
33
19
 
34
20
  ## Widget Attributes
35
21
 
@@ -82,9 +68,32 @@ Full setup for any SvelteKit app with Supabase. Creates the feedback pipeline fr
82
68
 
83
69
  ### Step 1: Add Widget to app.html
84
70
 
71
+ The widget is a web component bundled at `dist/jat-feedback.js`. Use `vite-plugin-static-copy` to copy it into your build output, then load it from your own server.
72
+
73
+ **vite.config.ts:**
74
+ ```typescript
75
+ import { sveltekit } from '@sveltejs/vite-plugin-svelte';
76
+ import { viteStaticCopy } from 'vite-plugin-static-copy';
77
+
78
+ export default {
79
+ plugins: [
80
+ sveltekit(),
81
+ viteStaticCopy({
82
+ targets: [
83
+ {
84
+ src: 'node_modules/jat-feedback/dist/jat-feedback.js',
85
+ dest: '.'
86
+ }
87
+ ]
88
+ })
89
+ ]
90
+ };
91
+ ```
92
+
93
+ **src/app.html:**
85
94
  ```html
86
- <!-- src/app.html — before closing </body> tag -->
87
- <script src="https://unpkg.com/jat-feedback@^1/dist/jat-feedback.js"></script>
95
+ <!-- before closing </body> tag -->
96
+ <script src="/jat-feedback.js"></script>
88
97
  <jat-feedback project="YOUR_PROJECT"></jat-feedback>
89
98
  <script>
90
99
  (function() {
@@ -164,7 +173,6 @@ export const POST: RequestHandler = async ({ request, locals }) => {
164
173
  }
165
174
 
166
175
  const reporter = body.metadata?.reporter || {};
167
- const org = body.metadata?.organization || {};
168
176
 
169
177
  const { data: row, error: insertError } = await supabase
170
178
  .from('feedback_reports')
@@ -179,8 +187,6 @@ export const POST: RequestHandler = async ({ request, locals }) => {
179
187
  reporter_email: reporter.email || null,
180
188
  reporter_name: reporter.name || null,
181
189
  reporter_role: reporter.role || null,
182
- organization_id: org.id || null,
183
- organization_name: org.name || null,
184
190
  console_logs: body.console_logs || null,
185
191
  selected_elements: body.selected_elements || null,
186
192
  screenshot_paths: screenshotPaths.length > 0 ? screenshotPaths : null,
@@ -208,104 +214,21 @@ export const POST: RequestHandler = async ({ request, locals }) => {
208
214
  };
209
215
  ```
210
216
 
211
- **Note:** If your app doesn't have organizations, remove the `organization_id` and `organization_name` fields from the insert and from the migration (Step 3).
212
-
213
- ### Step 3: Supabase Migration
214
-
215
- Create a migration file (e.g., `supabase/migrations/YYYYMMDD000000_feedback_reports.sql`):
216
-
217
- ```sql
218
- -- Feedback reports table for jat-feedback widget
219
- -- Widget POSTs to /api/feedback/report → this table
220
- -- JAT ingest daemon polls for jat_status = 'new'
221
-
222
- CREATE TABLE IF NOT EXISTS feedback_reports (
223
- id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
224
- title TEXT NOT NULL,
225
- description TEXT DEFAULT '',
226
- type TEXT DEFAULT 'bug' CHECK (type IN ('bug', 'enhancement', 'other')),
227
- priority TEXT DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'critical')),
228
- page_url TEXT,
229
- user_agent TEXT,
230
-
231
- -- Reporter identity
232
- reporter_user_id UUID REFERENCES auth.users(id),
233
- reporter_email TEXT,
234
- reporter_name TEXT,
235
- reporter_role TEXT,
236
-
237
- -- Organization context (remove if not applicable)
238
- organization_id UUID,
239
- organization_name TEXT,
240
-
241
- -- Structured data
242
- console_logs JSONB,
243
- selected_elements JSONB,
244
- screenshot_paths TEXT[],
245
- metadata JSONB,
246
-
247
- -- JAT ingest tracking
248
- jat_status TEXT DEFAULT 'new' CHECK (jat_status IN ('new', 'ingested', 'failed', 'rejected')),
249
- jat_task_id TEXT,
250
-
251
- -- User-facing status tracking
252
- status TEXT DEFAULT 'submitted'
253
- CHECK (status IN ('submitted', 'in_progress', 'completed', 'wontfix', 'closed')),
254
- dev_notes TEXT,
255
- user_response TEXT CHECK (user_response IN ('accepted', 'rejected')),
256
- user_response_at TIMESTAMPTZ,
257
- rejection_reason TEXT,
258
-
259
- created_at TIMESTAMPTZ DEFAULT now()
260
- );
261
-
262
- -- Indexes
263
- CREATE INDEX idx_feedback_reports_jat_status ON feedback_reports(jat_status) WHERE jat_status = 'new';
264
- CREATE INDEX idx_feedback_reports_jat_rejected ON feedback_reports(jat_status) WHERE jat_status = 'rejected';
265
- CREATE INDEX idx_feedback_reports_reporter ON feedback_reports(reporter_user_id, created_at DESC);
266
-
267
- -- Storage bucket for screenshots
268
- INSERT INTO storage.buckets (id, name, public) VALUES ('feedback-screenshots', 'feedback-screenshots', false)
269
- ON CONFLICT (id) DO NOTHING;
270
-
271
- -- RLS policies
272
- ALTER TABLE feedback_reports ENABLE ROW LEVEL SECURITY;
273
-
274
- CREATE POLICY "Authenticated users can insert feedback"
275
- ON feedback_reports FOR INSERT TO authenticated
276
- WITH CHECK (true);
277
-
278
- CREATE POLICY "Users can read own feedback reports"
279
- ON feedback_reports FOR SELECT TO authenticated
280
- USING (reporter_user_id = auth.uid());
281
-
282
- CREATE POLICY "Users can respond to own feedback"
283
- ON feedback_reports FOR UPDATE TO authenticated
284
- USING (reporter_user_id = auth.uid())
285
- WITH CHECK (reporter_user_id = auth.uid());
286
-
287
- CREATE POLICY "Service role full access to feedback"
288
- ON feedback_reports FOR ALL TO service_role
289
- USING (true) WITH CHECK (true);
290
-
291
- -- Storage policies
292
- CREATE POLICY "Authenticated users can upload feedback screenshots"
293
- ON storage.objects FOR INSERT TO authenticated
294
- WITH CHECK (bucket_id = 'feedback-screenshots');
295
-
296
- CREATE POLICY "Service role can read feedback screenshots"
297
- ON storage.objects FOR SELECT TO service_role
298
- USING (bucket_id = 'feedback-screenshots');
299
- ```
217
+ ### Step 3: Run the Supabase Migration
300
218
 
301
- Push the migration:
219
+ The `feedback_reports` table schema is included in this package. Copy it into your migrations folder and push:
302
220
 
303
221
  ```bash
222
+ # Copy the migration (rename to match your timestamp convention)
223
+ cp node_modules/jat-feedback/supabase/migrations/1.0.0_feedback_reports.sql \
224
+ supabase/migrations/$(date +%Y%m%d%H%M%S)_feedback_reports.sql
225
+
226
+ # Push to Supabase
304
227
  supabase db push
305
- # or
306
- supabase migration up
307
228
  ```
308
229
 
230
+ **When upgrading jat-feedback:** check `node_modules/jat-feedback/supabase/migrations/` for new versioned files (e.g. `1.1.0_*.sql`) and copy+apply any you haven't run yet.
231
+
309
232
  ### Step 4: Wire User Context
310
233
 
311
234
  In your authenticated layout, set widget attributes after login so reports include user identity.
@@ -323,8 +246,6 @@ onMount(() => {
323
246
  // Adjust based on where your app stores display name:
324
247
  if (user.user_metadata?.full_name) el.setAttribute('user-name', user.user_metadata.full_name);
325
248
  if (user.user_metadata?.role) el.setAttribute('user-role', user.user_metadata.role);
326
- // If your app has organizations:
327
- // if (user.organization_id) el.setAttribute('org-id', user.organization_id);
328
249
  }
329
250
  }
330
251
  });
@@ -372,12 +293,11 @@ Add this entry to the `sources` array.
372
293
  "labels": ["widget", "feedback"]
373
294
  },
374
295
  "table": "feedback_reports",
375
- "statusColumn": "jat_status",
376
- "statusNew": "new",
377
- "statusDone": "ingested",
296
+ "statusColumn": "status",
297
+ "statusNew": "submitted",
378
298
  "taskIdColumn": "jat_task_id",
379
299
  "titleColumn": "title",
380
- "descriptionTemplate": "**Reporter:** {reporter_name} ({reporter_email})\n**Page:** {page_url}\n**Browser:** {user_agent}\n\n{description}",
300
+ "descriptionTemplate": "**Reporter:** {reporter_name} ({reporter_email}) — {reporter_role}\n**Page:** {page_url}\n**Browser:** {user_agent}\n\n{description}",
381
301
  "authorColumn": "reporter_email",
382
302
  "timestampColumn": "created_at",
383
303
  "attachmentColumn": "screenshot_paths",
@@ -404,12 +324,11 @@ Add this entry to the `sources` array.
404
324
  "projectUrl": "https://YOUR_SUPABASE_PROJECT_ID.supabase.co",
405
325
  "secretName": "YOUR_PROJECT-supabase-service-role",
406
326
  "table": "feedback_reports",
407
- "statusColumn": "jat_status",
408
- "statusNew": "new",
409
- "statusDone": "ingested",
327
+ "statusColumn": "status",
328
+ "statusNew": "submitted",
410
329
  "taskIdColumn": "jat_task_id",
411
330
  "titleColumn": "title",
412
- "descriptionTemplate": "**Reporter:** {reporter_name} ({reporter_email})\n**Page:** {page_url}\n**Browser:** {user_agent}\n\n{description}",
331
+ "descriptionTemplate": "**Reporter:** {reporter_name} ({reporter_email}) — {reporter_role}\n**Page:** {page_url}\n**Browser:** {user_agent}\n\n{description}",
413
332
  "authorColumn": "reporter_email",
414
333
  "timestampColumn": "created_at",
415
334
  "attachmentColumn": "screenshot_paths",
@@ -437,16 +356,53 @@ Add this entry to the `sources` array.
437
356
  },
438
357
  "referenceTable": "feedback_reports",
439
358
  "referenceIdFrom": "item_id"
440
- }
359
+ },
360
+ "actions": [
361
+ {
362
+ "id": "sync_status",
363
+ "label": "Sync Status",
364
+ "description": "Push current task status to Supabase",
365
+ "type": "callback",
366
+ "event": "status_changed",
367
+ "icon": "refresh"
368
+ },
369
+ {
370
+ "id": "open_record",
371
+ "label": "View in Supabase",
372
+ "description": "Open the original feedback report in Supabase dashboard",
373
+ "type": "link",
374
+ "urlTemplate": "{projectUrl}/project/default/editor/feedback_reports?filter=id%3Deq.{referenceId}",
375
+ "icon": "external-link"
376
+ }
377
+ ]
441
378
  }
442
379
  ```
443
380
 
444
381
  - **automation**: Auto-spawn an agent when a report is ingested
445
- - **callback**: Push JAT task status changes back to Supabase (requires a Supabase Edge Function to receive the webhook)
382
+ - **callback**: Push JAT task status changes back to Supabase requires deploying the `jat-webhook` edge function (see Step 6)
383
+ - **actions**: Adds "Sync Status" and "View in Supabase" buttons in the JAT IDE task panel
446
384
 
447
385
  The ingest daemon picks up config changes automatically (no restart needed).
448
386
 
449
- ### Step 6: Verify
387
+ ### Step 6: Deploy the JAT Webhook Edge Function (for callbacks)
388
+
389
+ The `jat-webhook` Supabase Edge Function receives status-change callbacks from JAT and updates your `feedback_reports` rows. It's included in this package — copy it into your project and deploy it.
390
+
391
+ ```bash
392
+ # Copy the function into your project
393
+ mkdir -p supabase/functions/jat-webhook
394
+ cp node_modules/jat-feedback/supabase/functions/jat-webhook/index.ts \
395
+ supabase/functions/jat-webhook/index.ts
396
+
397
+ # Deploy to Supabase
398
+ supabase functions deploy jat-webhook
399
+ ```
400
+
401
+ The function uses `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY` — both are injected automatically by Supabase, no extra configuration needed.
402
+
403
+ **Skip this step** if you don't need bidirectional status sync (i.e., you only want JAT to ingest reports, not push status back to Supabase).
404
+
405
+ ### Step 7: Verify
450
406
 
451
407
  ```bash
452
408
  # Check the API endpoint is working
@@ -471,14 +427,14 @@ jat ingest status
471
427
  │ jat-feedback │────►│ Your App │────►│ Supabase │────►│ JAT Ingest │
472
428
  │ (widget) │ │ /api/report │ │ feedback_ │ │ Daemon │
473
429
  │ │ │ │ │ reports │ │ │
474
- └──────────────┘ └──────────────┘ └──────────────┘ └──────┬───────┘
475
-
476
-
477
- ┌──────────────┐
478
- │ JAT Task │
479
- │ (auto-
480
- created)
481
- └──────────────┘
430
+ └──────────────┘ └──────────────┘ └──────┬───────┘ └──────┬───────┘
431
+
432
+
433
+ ┌──────┴───────┐ ┌──────────────┐
434
+ jat-webhook │◄──│ JAT Task │
435
+ │ (edge fn) │ │ status
436
+ status sync │ changes │
437
+ └──────────────┘ └──────────────┘
482
438
  ```
483
439
 
484
440
  ## Column Reference
@@ -487,11 +443,72 @@ The `feedback_reports` table columns used by the pipeline:
487
443
 
488
444
  | Column | Type | Purpose |
489
445
  |--------|------|---------|
490
- | `jat_status` | TEXT | Ingest state: `new` → `ingested` (or `failed`, `rejected`) |
446
+ | `status` | TEXT | Lifecycle: `submitted` → `in_progress` `completed` `accepted` \| `rejected` |
491
447
  | `jat_task_id` | TEXT | JAT task ID written back after ingest (e.g., `myapp-abc`) |
492
- | `status` | TEXT | User-facing status synced from JAT task lifecycle |
448
+ | `rejection_reason` | TEXT | User-provided reason when rejecting a completed report |
493
449
  | `dev_notes` | TEXT | Developer notes pushed back via callback |
494
- | `user_response` | TEXT | User accept/reject of completed fix |
450
+
451
+ ## Upgrading
452
+
453
+ npm never auto-updates — you need to explicitly upgrade and then handle each type of change manually.
454
+
455
+ ```bash
456
+ # 1. Update the package
457
+ npm install jat-feedback@latest
458
+ ```
459
+
460
+ Then handle whatever changed in the release notes:
461
+
462
+ **Widget JS changes** (UI, behavior, new attributes):
463
+ ```bash
464
+ # Nothing extra — the updated bundle is copied to your build output automatically
465
+ # Just redeploy your app
466
+ ```
467
+
468
+ **Schema changes** (new columns, indexes):
469
+ ```bash
470
+ # Check for new migration files
471
+ ls node_modules/jat-feedback/supabase/migrations/
472
+
473
+ # Copy any new ones into your project and run them
474
+ cp node_modules/jat-feedback/supabase/migrations/1.1.0_*.sql \
475
+ supabase/migrations/$(date +%Y%m%d%H%M%S)_feedback_1_1_0.sql
476
+
477
+ supabase db push
478
+ ```
479
+
480
+ **Edge function changes** (webhook behavior):
481
+ ```bash
482
+ # Re-copy and redeploy
483
+ cp node_modules/jat-feedback/supabase/functions/jat-webhook/index.ts \
484
+ supabase/functions/jat-webhook/index.ts
485
+
486
+ supabase functions deploy jat-webhook
487
+ ```
488
+
489
+ ## Versioning
490
+
491
+ This package follows semver. The `^` range in consuming projects (`"jat-feedback": "^1.1.0"`) means:
492
+
493
+ - **Patch and minor** (1.1.x, 1.2.0) — auto-accepted by `npm install`
494
+ - **Major** (2.0.0) — requires manual version bump in `package.json`
495
+
496
+ ### What triggers a major version
497
+
498
+ | Change | Version |
499
+ |--------|---------|
500
+ | New nullable column (additive) | patch/minor |
501
+ | New widget attribute (optional) | minor |
502
+ | Removing or renaming a column | **major** |
503
+ | Changing a column's type | **major** |
504
+ | Renaming `status` values (e.g. `submitted` → `new`) | **major** |
505
+ | Required integrations.json config field added/renamed | **major** |
506
+
507
+ ### Rule for additive schema changes
508
+
509
+ Any column added in a `1.x` release **must** be nullable with no required default. This ensures consuming projects don't break even if they haven't run the migration yet — the insert just omits the column and it lands as `NULL`.
510
+
511
+ If a new column is required (non-nullable, no default), that's a breaking change and belongs in a major version.
495
512
 
496
513
  ## License
497
514