jat-feedback 3.4.2 → 3.5.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jat-feedback",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Embeddable feedback widget for bug reports and feature requests. Captures screenshots, console logs, and user context as a web component.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/jat-feedback.js",
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget comments API — consumer route template.
|
|
3
|
+
* Copy to: src/routes/api/tasks/[id]/comments/+server.ts
|
|
4
|
+
*
|
|
5
|
+
* Requires jat-feedback v3.5.0 migration (external column on project_tasks_comments).
|
|
6
|
+
*
|
|
7
|
+
* GET ?external=true → external comments for a task (for widget thread view)
|
|
8
|
+
* POST → reporter posts a reply comment (always external=true)
|
|
9
|
+
*/
|
|
10
|
+
import { json } from '@sveltejs/kit'
|
|
11
|
+
import type { RequestHandler } from './$types'
|
|
12
|
+
|
|
13
|
+
export const GET: RequestHandler = async ({ params, locals, url }) => {
|
|
14
|
+
const { id } = params
|
|
15
|
+
const externalOnly = url.searchParams.get('external') === 'true'
|
|
16
|
+
|
|
17
|
+
// Verify task exists
|
|
18
|
+
const { data: task, error: taskErr } = await locals.supabaseServiceRole
|
|
19
|
+
.from('project_tasks')
|
|
20
|
+
.select('id')
|
|
21
|
+
.eq('id', id)
|
|
22
|
+
.single()
|
|
23
|
+
|
|
24
|
+
if (taskErr || !task) {
|
|
25
|
+
return json({ error: 'Task not found' }, { status: 404 })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let query = locals.supabaseServiceRole
|
|
29
|
+
.from('project_tasks_comments')
|
|
30
|
+
.select('id, text, author, author_type, comment_type, external, created_at, metadata')
|
|
31
|
+
.eq('task_id', id)
|
|
32
|
+
.order('created_at', { ascending: true })
|
|
33
|
+
|
|
34
|
+
if (externalOnly) {
|
|
35
|
+
query = (query as any).eq('external', true)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { data, error } = await query
|
|
39
|
+
|
|
40
|
+
if (error) {
|
|
41
|
+
return json({ error: error.message }, { status: 500 })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const comments = (data || []).map((c: any) => ({
|
|
45
|
+
...c,
|
|
46
|
+
metadata: c.metadata ? safeParse(c.metadata) : null,
|
|
47
|
+
external: c.external !== false,
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
return json({ comments })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const POST: RequestHandler = async ({ params, request, locals }) => {
|
|
54
|
+
const { id } = params
|
|
55
|
+
|
|
56
|
+
let body: Record<string, unknown>
|
|
57
|
+
try {
|
|
58
|
+
body = await request.json()
|
|
59
|
+
} catch {
|
|
60
|
+
return json({ error: 'Invalid JSON body' }, { status: 400 })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { text, author, author_email, author_type, comment_type, metadata } = body
|
|
64
|
+
|
|
65
|
+
if (!text || typeof text !== 'string' || !text.trim()) {
|
|
66
|
+
return json({ error: 'text is required' }, { status: 400 })
|
|
67
|
+
}
|
|
68
|
+
if (!author || typeof author !== 'string') {
|
|
69
|
+
return json({ error: 'author is required' }, { status: 400 })
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Verify task exists
|
|
73
|
+
const { data: task, error: taskErr } = await locals.supabaseServiceRole
|
|
74
|
+
.from('project_tasks')
|
|
75
|
+
.select('id')
|
|
76
|
+
.eq('id', id)
|
|
77
|
+
.single()
|
|
78
|
+
|
|
79
|
+
if (taskErr || !task) {
|
|
80
|
+
return json({ error: 'Task not found' }, { status: 404 })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const { data: comment, error } = await locals.supabaseServiceRole
|
|
84
|
+
.from('project_tasks_comments')
|
|
85
|
+
.insert({
|
|
86
|
+
task_id: id,
|
|
87
|
+
text: (text as string).trim(),
|
|
88
|
+
author,
|
|
89
|
+
author_type: author_type ?? 'user',
|
|
90
|
+
comment_type: comment_type ?? 'note',
|
|
91
|
+
external: true,
|
|
92
|
+
metadata: metadata != null ? JSON.stringify(metadata) : null,
|
|
93
|
+
})
|
|
94
|
+
.select('id, text, author, author_type, comment_type, external, created_at, metadata')
|
|
95
|
+
.single()
|
|
96
|
+
|
|
97
|
+
if (error) {
|
|
98
|
+
return json({ error: error.message }, { status: 500 })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return json({ comment: { ...comment, external: true } }, { status: 201 })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function safeParse(s: unknown) {
|
|
105
|
+
if (typeof s !== 'string') return s
|
|
106
|
+
try { return JSON.parse(s) } catch { return s }
|
|
107
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- jat-feedback v3.5.0 — Extended comments schema
|
|
2
|
+
--
|
|
3
|
+
-- Adds first-class fields for agent question/answer threads:
|
|
4
|
+
-- author_type TEXT -- 'agent' | 'user' | 'system'
|
|
5
|
+
-- comment_type TEXT -- 'question' | 'answer' | 'note' | 'event'
|
|
6
|
+
-- session_id TEXT -- agent session to resume on answer (NULL = human thread)
|
|
7
|
+
-- metadata TEXT -- JSON blob, extensible
|
|
8
|
+
-- external BOOLEAN -- true = visible to client; false = internal agent/team thread
|
|
9
|
+
--
|
|
10
|
+
-- All columns are additive and nullable-safe (IF NOT EXISTS guards).
|
|
11
|
+
-- Existing rows get external=true, preserving current behaviour where every
|
|
12
|
+
-- comment was visible everywhere.
|
|
13
|
+
--
|
|
14
|
+
-- Apply to each consuming project:
|
|
15
|
+
-- cp node_modules/jat-feedback/supabase/migrations/3.5.0_extend_comments_schema.sql \
|
|
16
|
+
-- supabase/migrations/$(date +%Y%m%d%H%M%S)_feedback_3_5_0.sql
|
|
17
|
+
-- supabase db push
|
|
18
|
+
|
|
19
|
+
ALTER TABLE project_tasks_comments
|
|
20
|
+
ADD COLUMN IF NOT EXISTS author_type TEXT,
|
|
21
|
+
ADD COLUMN IF NOT EXISTS comment_type TEXT,
|
|
22
|
+
ADD COLUMN IF NOT EXISTS session_id TEXT,
|
|
23
|
+
ADD COLUMN IF NOT EXISTS metadata TEXT,
|
|
24
|
+
ADD COLUMN IF NOT EXISTS external BOOLEAN NOT NULL DEFAULT true;
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_project_tasks_comments_type
|
|
27
|
+
ON project_tasks_comments(task_id, comment_type);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_project_tasks_comments_external
|
|
30
|
+
ON project_tasks_comments(task_id, external);
|