@designfever/web-review-kit 0.1.0 → 0.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 +59 -184
- package/dist/{chunk-U5K2YGGL.js → chunk-EJDROXJM.js} +2583 -3085
- package/dist/chunk-EJDROXJM.js.map +1 -0
- package/dist/index.cjs +2615 -2900
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.js +220 -7
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +8364 -6651
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +7 -3
- package/dist/react-shell.d.ts +7 -3
- package/dist/react-shell.js +5163 -3425
- package/dist/react-shell.js.map +1 -1
- package/dist/{types-D_mNjOHx.d.cts → types-NiCp9JJQ.d.cts} +6 -14
- package/dist/{types-D_mNjOHx.d.ts → types-NiCp9JJQ.d.ts} +6 -14
- package/docs/README.md +21 -30
- package/docs/adaptor.sample.ts +182 -0
- package/docs/architecture.md +125 -0
- package/docs/db-setup.md +253 -0
- package/docs/figma-overlay.md +52 -0
- package/docs/grid-overlay.md +38 -0
- package/docs/installation.md +75 -40
- package/package.json +8 -3
- package/dist/chunk-U5K2YGGL.js.map +0 -1
- package/docs/adapter-handoff.md +0 -146
- package/docs/concept.md +0 -102
- package/docs/df-sheet-adapter.md +0 -336
- package/docs/df-sheet-next.md +0 -222
- package/docs/initial-plan.md +0 -226
- package/docs/package-split-checkpoint.md +0 -79
- package/docs/presence-handoff.md +0 -138
- package/docs/review-feedback-2026-06-20.md +0 -267
- package/docs/smoke-baseline-2026-06-20.md +0 -41
- package/docs/stabilize-ui-work-guide.md +0 -243
- package/docs/supabase-presence.md +0 -198
- package/docs/supabase-review-items.md +0 -365
- package/docs/supabase.md +0 -205
package/docs/concept.md
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# Concept
|
|
2
|
-
|
|
3
|
-
`df-web-review-kit`는 웹 프로젝트 안에 넣는 review shell이다. QA 전용 SaaS나 Chrome extension이 아니라, 프로젝트가 가진 route, viewport, DOM, style context를 그대로 사용해 검수 이슈를 남기는 toolkit이다.
|
|
4
|
-
|
|
5
|
-
## 해결하려는 문제
|
|
6
|
-
|
|
7
|
-
웹 QA는 보통 다음 정보가 빠져서 다시 확인하는 시간이 길어진다.
|
|
8
|
-
|
|
9
|
-
- 정확한 page route
|
|
10
|
-
- viewport size
|
|
11
|
-
- scroll position
|
|
12
|
-
- DOM anchor
|
|
13
|
-
- 깨진 영역의 좌표
|
|
14
|
-
- 사람이 남긴 comment
|
|
15
|
-
- 누가 같은 page를 보고 있는지
|
|
16
|
-
|
|
17
|
-
review-kit은 이 정보를 review item 하나에 묶어서 저장하고, `/review?...` link로 다시 복원한다.
|
|
18
|
-
|
|
19
|
-
## 기본 UX
|
|
20
|
-
|
|
21
|
-
1. `/review`를 연다.
|
|
22
|
-
2. sitemap에서 target page를 고른다.
|
|
23
|
-
3. mobile/tablet/desktop/wide viewport를 고른다.
|
|
24
|
-
4. iframe 안에서 note, DOM note, area item을 만든다.
|
|
25
|
-
5. 개인이 처리할 것은 `local` draft에 둔다.
|
|
26
|
-
6. 팀 공유가 필요한 것만 remote source에 등록한다.
|
|
27
|
-
7. remote item link를 공유하거나 AI prompt로 복사한다.
|
|
28
|
-
|
|
29
|
-
## Source 개념
|
|
30
|
-
|
|
31
|
-
### local
|
|
32
|
-
|
|
33
|
-
개인 draft 저장소다. `localStorage` 기반이라 빠르고, 실험/개인 처리용 QA를 remote에 섞지 않는다.
|
|
34
|
-
|
|
35
|
-
local item의 `#id`는 개인 브라우저 안에서만 의미가 있다. 여러 사람이 같은 번호를 가질 수 있다.
|
|
36
|
-
|
|
37
|
-
### remote
|
|
38
|
-
|
|
39
|
-
팀이 같이 보는 source of record다. 현재 검증 source는 `supabase`다.
|
|
40
|
-
|
|
41
|
-
local item을 remote에 등록하면 local id/number를 보존하지 않고 remote source가 새 id와 canonical `reviewNumber`를 발급한다. 등록 성공 후 local draft는 삭제한다.
|
|
42
|
-
|
|
43
|
-
### df-sheet
|
|
44
|
-
|
|
45
|
-
df-sheet는 issue workflow와 연결되는 remote destination 후보다. review-kit이 df-sheet issue editor가 되면 안 되고, local QA를 issue로 등록하고 restore link를 제공하는 역할이 맞다.
|
|
46
|
-
|
|
47
|
-
## Item 종류
|
|
48
|
-
|
|
49
|
-
- `note`: 화면 특정 지점에 남기는 comment.
|
|
50
|
-
- `dom`: DOM element에 anchor를 둔 note.
|
|
51
|
-
- `area`: 사용자가 드래그한 영역.
|
|
52
|
-
|
|
53
|
-
표시 범위는 mobile/tablet/desktop/wide와 연결된다. DOM item은 viewport가 달라도 anchor를 찾아 복원하는 것을 목표로 한다.
|
|
54
|
-
|
|
55
|
-
## Presence
|
|
56
|
-
|
|
57
|
-
Presence는 저장소가 아니다. 현재 접속한 사용자의 page/source/viewport 상태만 공유한다.
|
|
58
|
-
|
|
59
|
-
현재 UI는 같은 page에 들어온 사용자 id만 page header 쪽에 보여준다. sitemap 같은 상위 화면에서는 page별 접속자 표시로 확장할 수 있다.
|
|
60
|
-
|
|
61
|
-
## 패키지 경계
|
|
62
|
-
|
|
63
|
-
Package가 담당하는 것:
|
|
64
|
-
|
|
65
|
-
- review shell UI
|
|
66
|
-
- iframe target loading
|
|
67
|
-
- marker 생성과 restore
|
|
68
|
-
- prompt generation
|
|
69
|
-
- adapter contract
|
|
70
|
-
- local adapter
|
|
71
|
-
- Supabase adapter
|
|
72
|
-
- presence adapter contract
|
|
73
|
-
|
|
74
|
-
Host project가 담당하는 것:
|
|
75
|
-
|
|
76
|
-
- `/review` route 생성
|
|
77
|
-
- page glob 전달
|
|
78
|
-
- viewport preset 전달
|
|
79
|
-
- project id와 storage key 결정
|
|
80
|
-
- remote adapter env와 auth 결정
|
|
81
|
-
- Supabase client 생성
|
|
82
|
-
|
|
83
|
-
## 0.1 목표
|
|
84
|
-
|
|
85
|
-
0.1은 완전한 review platform이 아니라, 프로젝트별로 붙여 쓸 수 있는 최소 안정 package다.
|
|
86
|
-
|
|
87
|
-
포함:
|
|
88
|
-
|
|
89
|
-
- local draft
|
|
90
|
-
- Supabase remote CRUD
|
|
91
|
-
- Supabase Presence
|
|
92
|
-
- stable remote review number
|
|
93
|
-
- prompt copy
|
|
94
|
-
- route/viewport/scroll/marker restore
|
|
95
|
-
|
|
96
|
-
보류:
|
|
97
|
-
|
|
98
|
-
- screenshot upload
|
|
99
|
-
- full df-sheet workflow
|
|
100
|
-
- auth/member 기반 production RLS
|
|
101
|
-
- sitemap presence dashboard
|
|
102
|
-
- Chrome extension
|
package/docs/df-sheet-adapter.md
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
# df-sheet adapter implementation plan
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
`dfSheetAdapter` connects df-web-review-kit QA items to df-sheet issues.
|
|
6
|
-
|
|
7
|
-
The first production pilot is Lexus. Keep the adapter local and project-tested
|
|
8
|
-
before exposing it as a package export.
|
|
9
|
-
|
|
10
|
-
## Current decision
|
|
11
|
-
|
|
12
|
-
- Use `issues.id` as the canonical QA item id.
|
|
13
|
-
- Add only one df-sheet field: `issues.review_metadata jsonb null`.
|
|
14
|
-
- Store human-readable QA content in `description`.
|
|
15
|
-
- Store restore data in `review_metadata`.
|
|
16
|
-
- Store screenshots through df-sheet `/api/upload`, then attach the returned URL
|
|
17
|
-
to `issues.attachments`.
|
|
18
|
-
- Use a project/page scoped integration token, not an open unauthenticated API.
|
|
19
|
-
- Publish the adapter only after the Lexus pilot validates create, list, update,
|
|
20
|
-
delete, upload, and deep-link restore.
|
|
21
|
-
|
|
22
|
-
## Required df-sheet changes
|
|
23
|
-
|
|
24
|
-
### Database
|
|
25
|
-
|
|
26
|
-
Add a nullable JSON field to `issues`.
|
|
27
|
-
|
|
28
|
-
```sql
|
|
29
|
-
alter table issues
|
|
30
|
-
add column if not exists review_metadata jsonb null;
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### API
|
|
34
|
-
|
|
35
|
-
Pass `review_metadata` through the existing issue APIs.
|
|
36
|
-
|
|
37
|
-
- `POST /api/issues`
|
|
38
|
-
- accept `body.review_metadata`
|
|
39
|
-
- insert it into `issues.review_metadata`
|
|
40
|
-
- `PATCH /api/issues/:id`
|
|
41
|
-
- accept `body.review_metadata`
|
|
42
|
-
- update `issues.review_metadata`
|
|
43
|
-
- `GET /api/issues`
|
|
44
|
-
- include `review_metadata` in list responses
|
|
45
|
-
- `GET /api/issues/:id`
|
|
46
|
-
- include `review_metadata` in detail responses
|
|
47
|
-
|
|
48
|
-
Do not hide adapter metadata in `description`. df-sheet uses Tiptap JSON there,
|
|
49
|
-
so a hidden JSON block would be fragile.
|
|
50
|
-
|
|
51
|
-
### Auth
|
|
52
|
-
|
|
53
|
-
Add an integration token path for adapter calls.
|
|
54
|
-
|
|
55
|
-
Recommended rules:
|
|
56
|
-
|
|
57
|
-
- token is sent with `Authorization: Bearer <token>`
|
|
58
|
-
- token is scoped to one `project_id`
|
|
59
|
-
- token is scoped to one `page_id`
|
|
60
|
-
- token can only create/list/read/update/delete issues in that scope
|
|
61
|
-
- token can upload attachments for those issues
|
|
62
|
-
|
|
63
|
-
Do not expose issue CRUD without auth.
|
|
64
|
-
|
|
65
|
-
## Adapter config
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
type DfSheetAdapterOptions = {
|
|
69
|
-
baseUrl: string;
|
|
70
|
-
projectId: string;
|
|
71
|
-
pageId: string;
|
|
72
|
-
token: string;
|
|
73
|
-
issueType?: string;
|
|
74
|
-
issuePriority?: "low" | "medium" | "high" | "urgent";
|
|
75
|
-
};
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Example usage during the Lexus pilot:
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
createWebReviewKit({
|
|
82
|
-
projectId: "lexus-official-v2026",
|
|
83
|
-
adapter: createDfSheetAdapter({
|
|
84
|
-
baseUrl: "https://df-sheet.vercel.app",
|
|
85
|
-
projectId: "<df-sheet-project-id>",
|
|
86
|
-
pageId: "<df-sheet-page-id>",
|
|
87
|
-
token: "<integration-token>",
|
|
88
|
-
issueType: "task",
|
|
89
|
-
issuePriority: "medium",
|
|
90
|
-
}),
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
If the target project has no backend proxy, assume the browser can see this
|
|
95
|
-
token. Keep the token project/page scoped so leakage impact is limited.
|
|
96
|
-
|
|
97
|
-
## Adapter contract changes
|
|
98
|
-
|
|
99
|
-
Current adapter contract:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
interface WebReviewKitAdapter {
|
|
103
|
-
list(query: ReviewItemQuery): Promise<ReviewItem[]>;
|
|
104
|
-
create(item: ReviewItem): Promise<ReviewItem>;
|
|
105
|
-
update(id: string, patch: Partial<Omit<ReviewItem, "id" | "createdAt">>): Promise<ReviewItem>;
|
|
106
|
-
remove(id: string): Promise<void>;
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Add `get(id)` for deep-link restore:
|
|
111
|
-
|
|
112
|
-
```ts
|
|
113
|
-
interface WebReviewKitAdapter {
|
|
114
|
-
get(id: string): Promise<ReviewItem | null>;
|
|
115
|
-
list(query: ReviewItemQuery): Promise<ReviewItem[]>;
|
|
116
|
-
create(item: ReviewItem): Promise<ReviewItem>;
|
|
117
|
-
update(id: string, patch: Partial<Omit<ReviewItem, "id" | "createdAt">>): Promise<ReviewItem>;
|
|
118
|
-
remove(id: string): Promise<void>;
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`localAdapter` should implement `get(id)` from local storage.
|
|
123
|
-
|
|
124
|
-
## Status mapping
|
|
125
|
-
|
|
126
|
-
df-web-review-kit:
|
|
127
|
-
|
|
128
|
-
- `todo`
|
|
129
|
-
- `doing`
|
|
130
|
-
- `review`
|
|
131
|
-
- `hold`
|
|
132
|
-
- `done`
|
|
133
|
-
|
|
134
|
-
df-sheet:
|
|
135
|
-
|
|
136
|
-
- `todo`
|
|
137
|
-
- `in_progress`
|
|
138
|
-
- `review`
|
|
139
|
-
- `on_hold`
|
|
140
|
-
- `done`
|
|
141
|
-
|
|
142
|
-
Mapping:
|
|
143
|
-
|
|
144
|
-
```ts
|
|
145
|
-
const toDfSheetStatus = {
|
|
146
|
-
todo: "todo",
|
|
147
|
-
doing: "in_progress",
|
|
148
|
-
review: "review",
|
|
149
|
-
hold: "on_hold",
|
|
150
|
-
done: "done",
|
|
151
|
-
} as const;
|
|
152
|
-
|
|
153
|
-
const fromDfSheetStatus = {
|
|
154
|
-
todo: "todo",
|
|
155
|
-
in_progress: "doing",
|
|
156
|
-
review: "review",
|
|
157
|
-
on_hold: "hold",
|
|
158
|
-
done: "done",
|
|
159
|
-
} as const;
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Review metadata shape
|
|
163
|
-
|
|
164
|
-
The metadata must be enough to rebuild a `ReviewItem` from a df-sheet issue.
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
|
-
type DfSheetReviewMetadata = {
|
|
168
|
-
schema: "df-web-review-kit";
|
|
169
|
-
version: 1;
|
|
170
|
-
reviewProjectId: string;
|
|
171
|
-
routeKey: string;
|
|
172
|
-
pageUrl: string;
|
|
173
|
-
originalUrl?: string;
|
|
174
|
-
normalizedPath: string;
|
|
175
|
-
scope?: ReviewItemScope;
|
|
176
|
-
kind: ReviewItemKind;
|
|
177
|
-
viewport: ViewportSize;
|
|
178
|
-
devicePixelRatio?: number;
|
|
179
|
-
scroll?: {
|
|
180
|
-
x: number;
|
|
181
|
-
y: number;
|
|
182
|
-
};
|
|
183
|
-
anchor?: DomAnchor;
|
|
184
|
-
marker?: ReviewMarker;
|
|
185
|
-
selection?: ReviewSelection;
|
|
186
|
-
reviewUrl?: string;
|
|
187
|
-
};
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Avoid storing screenshot data URLs in metadata. Use `attachments`.
|
|
191
|
-
|
|
192
|
-
## Issue mapping
|
|
193
|
-
|
|
194
|
-
### ReviewItem to Issue
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
{
|
|
198
|
-
project_id: options.projectId,
|
|
199
|
-
page_id: options.pageId,
|
|
200
|
-
title: item.title || makeIssueTitle(item),
|
|
201
|
-
description: makeIssueDescription(item),
|
|
202
|
-
status: toDfSheetStatus[normalizeReviewItemStatus(item.status)],
|
|
203
|
-
priority: options.issuePriority ?? "medium",
|
|
204
|
-
type: options.issueType ?? "task",
|
|
205
|
-
attachments: item.screenshot ? [uploadedScreenshot] : [],
|
|
206
|
-
links: makeReviewUrl(item),
|
|
207
|
-
review_metadata: makeReviewMetadata(item),
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Issue to ReviewItem
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
{
|
|
215
|
-
id: issue.id,
|
|
216
|
-
externalIssueId: issue.id,
|
|
217
|
-
projectId: metadata.reviewProjectId,
|
|
218
|
-
routeKey: metadata.routeKey,
|
|
219
|
-
pageUrl: metadata.pageUrl,
|
|
220
|
-
originalUrl: metadata.originalUrl,
|
|
221
|
-
normalizedPath: metadata.normalizedPath,
|
|
222
|
-
scope: metadata.scope,
|
|
223
|
-
kind: metadata.kind,
|
|
224
|
-
title: issue.title,
|
|
225
|
-
comment: extractComment(issue.description),
|
|
226
|
-
status: fromDfSheetStatus[issue.status],
|
|
227
|
-
viewport: metadata.viewport,
|
|
228
|
-
devicePixelRatio: metadata.devicePixelRatio,
|
|
229
|
-
scroll: metadata.scroll,
|
|
230
|
-
anchor: metadata.anchor,
|
|
231
|
-
marker: metadata.marker,
|
|
232
|
-
selection: metadata.selection,
|
|
233
|
-
screenshot: undefined,
|
|
234
|
-
createdAt: issue.created_at,
|
|
235
|
-
updatedAt: issue.updated_at ?? issue.created_at,
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
## Deep link flow
|
|
240
|
-
|
|
241
|
-
Target URL:
|
|
242
|
-
|
|
243
|
-
```txt
|
|
244
|
-
/review?target=/path&w=390&h=720&item=<issue.id>
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
Flow:
|
|
248
|
-
|
|
249
|
-
1. `ReviewShell` reads `item` from the query string.
|
|
250
|
-
2. It calls `adapter.get(itemId)`.
|
|
251
|
-
3. The adapter fetches `GET /api/issues/:id`.
|
|
252
|
-
4. It maps the df-sheet issue to `ReviewItem`.
|
|
253
|
-
5. `ReviewShell` restores target route and viewport from the item metadata.
|
|
254
|
-
6. It reloads the target iframe.
|
|
255
|
-
7. It highlights the restored item after the target document is ready.
|
|
256
|
-
|
|
257
|
-
The query params `target`, `w`, and `h` are hints for early shell rendering.
|
|
258
|
-
`adapter.get(item)` is the source of truth.
|
|
259
|
-
|
|
260
|
-
## List sync
|
|
261
|
-
|
|
262
|
-
MVP behavior:
|
|
263
|
-
|
|
264
|
-
- load list on review shell mount
|
|
265
|
-
- reload after create/update/delete
|
|
266
|
-
- reload when target route changes
|
|
267
|
-
- reload on manual refresh
|
|
268
|
-
- optionally reload when the window regains focus
|
|
269
|
-
|
|
270
|
-
Do not start with realtime sync. Add Supabase realtime later only if the Lexus
|
|
271
|
-
pilot shows that multiple reviewers need live updates.
|
|
272
|
-
|
|
273
|
-
## Delete behavior
|
|
274
|
-
|
|
275
|
-
Use the existing df-sheet delete API for the pilot:
|
|
276
|
-
|
|
277
|
-
```txt
|
|
278
|
-
DELETE /api/issues/:id
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
If teams want QA history preserved later, add an archive/hidden status in
|
|
282
|
-
df-sheet instead of hard delete.
|
|
283
|
-
|
|
284
|
-
## Implementation phases
|
|
285
|
-
|
|
286
|
-
### Phase 1: contract
|
|
287
|
-
|
|
288
|
-
- add `adapter.get(id)`
|
|
289
|
-
- update `localAdapter`
|
|
290
|
-
- update review shell deep-link restore to use adapter data instead of local
|
|
291
|
-
storage only
|
|
292
|
-
|
|
293
|
-
### Phase 2: df-sheet adapter
|
|
294
|
-
|
|
295
|
-
- add `src/adapters/df-sheet.ts`
|
|
296
|
-
- implement `createDfSheetAdapter(options)`
|
|
297
|
-
- implement issue/status/metadata mapping helpers
|
|
298
|
-
- implement screenshot upload helper
|
|
299
|
-
- keep the adapter unexported or internally linked during Lexus pilot
|
|
300
|
-
|
|
301
|
-
### Phase 3: Lexus pilot
|
|
302
|
-
|
|
303
|
-
- configure Lexus review shell with df-sheet adapter options
|
|
304
|
-
- test create/list/update/delete
|
|
305
|
-
- test screenshot attachment upload
|
|
306
|
-
- test `/review?...&item=<issue.id>` restore
|
|
307
|
-
- test status changes from both review-kit and df-sheet
|
|
308
|
-
|
|
309
|
-
### Phase 4: package export
|
|
310
|
-
|
|
311
|
-
After the Lexus pilot is stable:
|
|
312
|
-
|
|
313
|
-
- export as `@designfever/web-review-kit/adapters/df-sheet`
|
|
314
|
-
- include adapter files in package build
|
|
315
|
-
- update README with installation/config examples
|
|
316
|
-
- publish npm package
|
|
317
|
-
|
|
318
|
-
## Verification checklist
|
|
319
|
-
|
|
320
|
-
- `pnpm typecheck`
|
|
321
|
-
- `pnpm build`
|
|
322
|
-
- create text QA from Lexus review page
|
|
323
|
-
- create capture QA with screenshot
|
|
324
|
-
- list reload shows created issues
|
|
325
|
-
- status update maps both directions
|
|
326
|
-
- delete removes item from list
|
|
327
|
-
- deep link restores route, viewport, and highlighted QA
|
|
328
|
-
- df-sheet issue detail shows title, description, status, attachment, and link
|
|
329
|
-
|
|
330
|
-
## Open questions
|
|
331
|
-
|
|
332
|
-
- Lexus df-sheet `project_id`
|
|
333
|
-
- Lexus df-sheet `page_id`
|
|
334
|
-
- integration token creation and rotation flow
|
|
335
|
-
- whether df-sheet should expose an issue type dedicated to QA
|
|
336
|
-
- whether delete should become archive after the pilot
|
package/docs/df-sheet-next.md
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
# df-sheet adapter next plan
|
|
2
|
-
|
|
3
|
-
## 목적
|
|
4
|
-
|
|
5
|
-
df-sheet를 `df-web-review-kit`의 remote destination으로 계속 사용할 때의 역할과 필요한 df-sheet 쪽 변경을 분리해서 정리한다.
|
|
6
|
-
|
|
7
|
-
Supabase 실험 문서는 adapter 구조 검증용이고, 이 문서는 df-sheet 제품/운영 흐름 기준이다.
|
|
8
|
-
|
|
9
|
-
## 현재 위치
|
|
10
|
-
|
|
11
|
-
현재 `dfSheetAdapter`는 review-kit item을 df-sheet issue로 보낸다.
|
|
12
|
-
|
|
13
|
-
현재 동작:
|
|
14
|
-
|
|
15
|
-
- `create`: local QA item을 df-sheet issue로 등록
|
|
16
|
-
- `list`: df-sheet issue 목록을 review item으로 변환
|
|
17
|
-
- `get`: issue detail을 review item으로 변환
|
|
18
|
-
- `update/remove`: review-kit 안에서는 read-only로 막음
|
|
19
|
-
- restore data: `issues.review_metadata`
|
|
20
|
-
- review link: issue metadata/link에 `/review?...&item=<issueId>` 저장
|
|
21
|
-
|
|
22
|
-
현재 shell adapter에서는 df-sheet config에 `updateStatus`와 `remove`를 넣지 않는다. 따라서 df-sheet source에서는 상태 변경 dropdown은 readonly badge로만 둘 수 있고, 삭제 버튼도 숨긴다.
|
|
23
|
-
|
|
24
|
-
## 권장 역할
|
|
25
|
-
|
|
26
|
-
df-sheet는 source of record가 되어야 한다. review-kit은 다음 역할까지만 한다.
|
|
27
|
-
|
|
28
|
-
- local에서 만든 QA를 df-sheet issue로 등록
|
|
29
|
-
- df-sheet issue를 현재 page/viewport에서 restore
|
|
30
|
-
- issue status를 표시
|
|
31
|
-
- 필요 시 status update만 좁게 허용
|
|
32
|
-
|
|
33
|
-
review-kit이 df-sheet의 전체 issue editor가 되면 안 된다.
|
|
34
|
-
|
|
35
|
-
## 상태 변경 정책
|
|
36
|
-
|
|
37
|
-
generic `update`는 넣지 않는다.
|
|
38
|
-
|
|
39
|
-
df-sheet 상태를 review-kit에서 바꾸고 싶다면 `updateStatus`만 추가한다.
|
|
40
|
-
|
|
41
|
-
```ts
|
|
42
|
-
{
|
|
43
|
-
label: 'df-sheet',
|
|
44
|
-
get,
|
|
45
|
-
list,
|
|
46
|
-
create,
|
|
47
|
-
statusOptions: DF_SHEET_STATUS_OPTIONS,
|
|
48
|
-
updateStatus: async ({ id, status, statusOption, statusIndex }) => {
|
|
49
|
-
return dfSheet.updateIssueStatus(id, {
|
|
50
|
-
status,
|
|
51
|
-
statusIndex,
|
|
52
|
-
statusLabel: statusOption.label,
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
상태 기준:
|
|
59
|
-
|
|
60
|
-
- 저장 identity: `statusOption.value`
|
|
61
|
-
- 표시명: `statusOption.label`
|
|
62
|
-
- 외부 매핑 보조값: `statusIndex`
|
|
63
|
-
|
|
64
|
-
df-sheet의 실제 status vocabulary가 다르면 adapter에서 mapping한다.
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
const DF_SHEET_STATUS_OPTIONS = [
|
|
68
|
-
{ value: 'todo', label: '작업전' },
|
|
69
|
-
{ value: 'doing', label: '작업중' },
|
|
70
|
-
{ value: 'review', label: '검토 필요' },
|
|
71
|
-
{ value: 'hold', label: '보류' },
|
|
72
|
-
{ value: 'done', label: '완료' },
|
|
73
|
-
] as const;
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## df-sheet API 제안
|
|
77
|
-
|
|
78
|
-
현재 issue CRUD API를 그대로 넓히기보다 review-kit 전용 endpoint를 두는 쪽이 안전하다.
|
|
79
|
-
|
|
80
|
-
추천 endpoint:
|
|
81
|
-
|
|
82
|
-
```txt
|
|
83
|
-
GET /api/review-kit/issues
|
|
84
|
-
GET /api/review-kit/issues/:id
|
|
85
|
-
POST /api/review-kit/issues
|
|
86
|
-
PATCH /api/review-kit/issues/:id/status
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
선택 endpoint:
|
|
90
|
-
|
|
91
|
-
```txt
|
|
92
|
-
DELETE /api/review-kit/issues/:id
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
요청은 integration token 기준으로 project/page scope를 제한한다.
|
|
96
|
-
|
|
97
|
-
```http
|
|
98
|
-
Authorization: Bearer <review-kit-token>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Token scope:
|
|
102
|
-
|
|
103
|
-
- `project_id`
|
|
104
|
-
- `page_id`
|
|
105
|
-
- allowed actions: `create`, `read`, optional `update_status`, optional `delete`
|
|
106
|
-
|
|
107
|
-
브라우저에서 직접 token을 쓰는 경우 누출을 전제로 작게 scope를 잡는다. production에서는 backend proxy 또는 df-sheet domain cookie auth를 우선 검토한다.
|
|
108
|
-
|
|
109
|
-
## DB 필드
|
|
110
|
-
|
|
111
|
-
`issues.review_metadata jsonb`는 유지한다.
|
|
112
|
-
|
|
113
|
-
```sql
|
|
114
|
-
alter table issues
|
|
115
|
-
add column if not exists review_metadata jsonb null;
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
`review_metadata`에 들어가는 값:
|
|
119
|
-
|
|
120
|
-
- review item id
|
|
121
|
-
- review number
|
|
122
|
-
- route/normalized path
|
|
123
|
-
- viewport
|
|
124
|
-
- scroll
|
|
125
|
-
- anchor
|
|
126
|
-
- marker
|
|
127
|
-
- selection
|
|
128
|
-
- original comment
|
|
129
|
-
- review link
|
|
130
|
-
- schema/source/version
|
|
131
|
-
|
|
132
|
-
사람이 읽어야 하는 내용은 `title`/`description`에 둔다. restore에 필요한 구조 데이터는 `review_metadata`에 둔다.
|
|
133
|
-
|
|
134
|
-
## create flow
|
|
135
|
-
|
|
136
|
-
1. local item 선택
|
|
137
|
-
2. review-kit이 df-sheet `POST /api/review-kit/issues` 호출
|
|
138
|
-
3. df-sheet가 issue 생성
|
|
139
|
-
4. df-sheet가 issue id/url 반환
|
|
140
|
-
5. remote 등록 성공 시 local item은 삭제하고, 실패 시에만 `syncSubmission`으로 실패 상태를 기록
|
|
141
|
-
|
|
142
|
-
remote issue id를 local id로 덮어쓰지 않는다. 대신 remote 등록 payload에서는 사람이 볼 번호로 `reviewNumber`를 보낸다.
|
|
143
|
-
|
|
144
|
-
## list/get flow
|
|
145
|
-
|
|
146
|
-
`list`는 route 기준으로 좁게 가져온다.
|
|
147
|
-
|
|
148
|
-
```txt
|
|
149
|
-
project_id=<df-sheet-project-id>
|
|
150
|
-
page_id=<df-sheet-page-id>
|
|
151
|
-
review_source=df-web-review-kit
|
|
152
|
-
review_route_key=<target route>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
응답에는 `review_metadata`가 반드시 포함되어야 한다. metadata가 없거나 source/schema가 맞지 않으면 adapter는 review item으로 변환하지 않는다.
|
|
156
|
-
|
|
157
|
-
## status update flow
|
|
158
|
-
|
|
159
|
-
df-sheet에서 status 변경을 허용하려면 issue 전체 patch가 아니라 status-only endpoint를 쓴다.
|
|
160
|
-
|
|
161
|
-
```txt
|
|
162
|
-
PATCH /api/review-kit/issues/:id/status
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
Body:
|
|
166
|
-
|
|
167
|
-
```json
|
|
168
|
-
{
|
|
169
|
-
"status": "doing"
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Response는 updated issue와 `review_metadata`를 포함한다.
|
|
174
|
-
|
|
175
|
-
review-kit adapter는 이 응답을 `ReviewItem`으로 변환한다.
|
|
176
|
-
|
|
177
|
-
## delete policy
|
|
178
|
-
|
|
179
|
-
초기에는 df-sheet delete를 review-kit에서 열지 않는 편이 낫다.
|
|
180
|
-
|
|
181
|
-
이유:
|
|
182
|
-
|
|
183
|
-
- remote issue 삭제는 팀 공유 데이터에 직접 영향이 크다.
|
|
184
|
-
- review-kit list에서 실수 클릭으로 삭제될 수 있다.
|
|
185
|
-
- df-sheet 내부 권한/감사 로그와 맞춰야 한다.
|
|
186
|
-
|
|
187
|
-
필요하면 `remove`를 adapter에 넣고, UI는 확인 modal을 추가한다. 현재 shell의 local 삭제 UX와 같은 즉시 삭제 방식으로 remote delete를 열면 안 된다.
|
|
188
|
-
|
|
189
|
-
## package 관점
|
|
190
|
-
|
|
191
|
-
df-sheet adapter를 package에 포함할 때도 df-sheet 전용 타입/설정은 adapter 내부에 묶는다.
|
|
192
|
-
|
|
193
|
-
`ReviewShellAdapter`는 계속 storage-agnostic이어야 한다.
|
|
194
|
-
|
|
195
|
-
좋은 방향:
|
|
196
|
-
|
|
197
|
-
- `dfSheetAdapter(options)`는 core `WebReviewKitAdapter` 구현
|
|
198
|
-
- `dfSheetShellAdapter(options)` 또는 helper는 shell-facing adapter config 생성
|
|
199
|
-
- page/review에서는 helper를 조립해서 넘김
|
|
200
|
-
|
|
201
|
-
예시:
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
const dfSheet = dfSheetAdapter(options);
|
|
205
|
-
|
|
206
|
-
const REVIEW_ADAPTERS = [
|
|
207
|
-
localReviewShellAdapter(local),
|
|
208
|
-
dfSheetReviewShellAdapter(dfSheet, {
|
|
209
|
-
pageId: options.pageId,
|
|
210
|
-
statusOptions: DF_SHEET_STATUS_OPTIONS,
|
|
211
|
-
canUpdateStatus: false,
|
|
212
|
-
}),
|
|
213
|
-
];
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
## 남은 결정
|
|
217
|
-
|
|
218
|
-
- df-sheet에서 review-kit 전용 endpoint를 새로 만들지, 기존 `/api/issues`를 확장할지.
|
|
219
|
-
- status update를 review-kit에서 허용할지.
|
|
220
|
-
- integration token을 cookie auth와 병행할지.
|
|
221
|
-
- df-sheet issue status vocabulary와 review-kit status value를 1:1로 맞출지.
|
|
222
|
-
- remote delete를 허용할지.
|