feat(groups): add drag-and-drop reorder for group list#398
Conversation
- Add PUT /api/groups/reorder endpoint with transactional batch update - Implement drag-and-drop UI in GroupList.vue with visual feedback - Support touch device detection and search mode restrictions - Add i18n messages for zh-CN, en-US, ja-JP
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughAdds backend and frontend support for reordering groups: new PUT API endpoint and handler, service method that validates and transactionally updates group sort orders with row locks and cache invalidation, localization strings, client API call, and drag-and-drop UI in GroupList.vue. Changes
Sequence DiagramsequenceDiagram
actor User
participant Vue as GroupList Component
participant API as keysApi.reorderGroups()
participant Handler as ReorderGroups Handler
participant Service as GroupService.ReorderGroups()
participant DB as Database
participant Cache as Group Cache Manager
User->>Vue: Drag & drop groups
Vue->>Vue: Update displayGroups locally
User->>Vue: Drop to finalize
Vue->>API: PUT /groups/reorder (items)
API->>Handler: HTTP request -> bind & validate
alt Validation fails
Handler-->>API: Return I18n validation error
API-->>Vue: Error response
Vue->>Vue: Restore previous order & show error
else
Handler->>Service: ReorderGroups(ctx, items)
Service->>DB: Begin transaction
Service->>DB: SELECT COUNT(id) WHERE id IN (items) FOR UPDATE
alt Count mismatch
Service-->>Handler: Return I18n not-found error
Handler-->>Vue: Error response
Vue->>Vue: Restore previous order & show error
else
Service->>DB: UPDATE groups SET sort = ? WHERE id = ? (FOR UPDATE)
Service->>DB: Commit
Service->>Cache: Invalidate()
Service-->>Handler: success
Handler-->>Vue: success.groups_reordered
Vue->>Vue: Show success & sync displayGroups
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/components/keys/GroupList.vue (1)
384-417:⚠️ Potential issue | 🟠 MajorThe drag handle is still unreachable for keyboard users.
This
<div role="button">is not focusable and has no keyboard move interaction, so the new reorder flow is effectively mouse-only. The hint at Lines 415-417 also never helps keyboard users if the control cannot receive focus. Please add a real button plus keyboard reordering, or a separate non-drag reorder control.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/keys/GroupList.vue` around lines 384 - 417, The drag handle (div.group-icon) is not keyboard-accessible; make it a real focusable control and add keyboard reorder support by replacing the non-focusable <div class="group-icon"> with a <button> (or add tabindex="0") and wire keydown handlers that call the existing drag/reorder logic (use handleDragStart(group.id)/handleDragEnd or delegate to new methods like handleReorderUp(group.id) and handleReorderDown(group.id)); ensure the aria-describedby still references `drag-hint-${group.id}` (dragDisabledHint) and update the handlers referenced in the template (group-icon, handleDragStart, handleDragEnd, dragDisabledHint) so keyboard users can focus the control and use ArrowUp/ArrowDown (or Space/Enter to pick up + Esc to cancel) to reorder items.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/handler/group_handler.go`:
- Around line 137-166: Change GroupReorderItemRequest.Sort from int to *int so
you can distinguish omitted vs explicit 0 (update the struct
GroupReorderItemRequest). In validateGroupReorderItems, add a nil check for
item.Sort and return the "validation.reorder_sort_negative" (or a new
"validation.reorder_sort_required") error when item.Sort == nil, and keep the
negative-value check against *item.Sort. Finally, update the code that maps
request items into the internal model/DB (the block that reads Items and assigns
Sort) to dereference the pointer (*item.Sort) when constructing the target
objects.
- Around line 27-30: The type assertion for services.I18nError currently returns
false when svcErr is a typed-nil, which incorrectly signals the error wasn't
handled; change the logic in the block that checks "if svcErr, ok :=
err.(*services.I18nError); ok {" so that if svcErr == nil you treat it as an
internal error (handle/log/respond as other I18nError cases) and return true
instead of false; ensure the branch for nil svcErr follows the same
error-response/handling pathway as the non-nil svcErr cases so callers see the
error was handled.
In `@internal/services/group_service.go`:
- Around line 315-325: The COUNT-based existence check is racy; after counting
you must ensure each Update actually modified a row or lock the target rows
beforehand. Fix by either selecting and locking the groups before the loop
(e.g., use tx.Clauses(clause.Locking{Strength:"UPDATE"}).Where("id IN ?",
ids).Find(&groups)) then perform updates, or after each
tx.Model(&models.Group{}).Where("id = ?", item.ID).Update("sort", item.Sort)
check the result.RowsAffected and if RowsAffected != 1 return
NewI18nError(app_errors.ErrValidation, "validation.reorder_group_not_found",
nil); ensure you use the tx result variable (e.g., res :=
tx.Model(...).Update(...)) to inspect res.Error and res.RowsAffected and return
parsed DB errors when res.Error != nil.
In `@web/src/components/keys/GroupList.vue`:
- Around line 233-239: On failed reorder (inside the catch block around
reorderGroups) don't just roll back displayGroups to previousOrder; also
re-fetch the authoritative list from the server to avoid leaving a stale local
state. Update the catch path that references reorderGroups, displayGroups,
previousOrder, savingOrder and suspendAutoScroll to call the same refresh/fetch
routine you use in Keys.vue (the view-level fetch or refreshGroups function) or
emit the existing refresh event so the component reloads groups from the server,
then keep the existing error toast.
---
Outside diff comments:
In `@web/src/components/keys/GroupList.vue`:
- Around line 384-417: The drag handle (div.group-icon) is not
keyboard-accessible; make it a real focusable control and add keyboard reorder
support by replacing the non-focusable <div class="group-icon"> with a <button>
(or add tabindex="0") and wire keydown handlers that call the existing
drag/reorder logic (use handleDragStart(group.id)/handleDragEnd or delegate to
new methods like handleReorderUp(group.id) and handleReorderDown(group.id));
ensure the aria-describedby still references `drag-hint-${group.id}`
(dragDisabledHint) and update the handlers referenced in the template
(group-icon, handleDragStart, handleDragEnd, dragDisabledHint) so keyboard users
can focus the control and use ArrowUp/ArrowDown (or Space/Enter to pick up + Esc
to cancel) to reorder items.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b7cc4250-8485-4199-9c76-e1fd64ec5cef
📒 Files selected for processing (11)
internal/handler/group_handler.gointernal/i18n/locales/en-US.gointernal/i18n/locales/ja-JP.gointernal/i18n/locales/zh-CN.gointernal/router/router.gointernal/services/group_service.goweb/src/api/keys.tsweb/src/components/keys/GroupList.vueweb/src/locales/en-US.tsweb/src/locales/ja-JP.tsweb/src/locales/zh-CN.ts
|
我核对后处理如下:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/components/keys/GroupList.vue`:
- Around line 379-383: The ref access inside the template ref callback is using
groupItemRefs.set(...) on a Ref<Map<...>> which causes a runtime error; update
the callback to call groupItemRefs.value.set(group.id, el as HTMLElement) (and
ensure null checks for el and group.id remain) so you operate on the underlying
Map; reference the existing symbol groupItemRefs and the callback that checks
group.id to locate and fix the code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: eb785a89-99bd-4623-9a4d-b0a11af88a79
📒 Files selected for processing (2)
internal/services/group_service.goweb/src/components/keys/GroupList.vue
|
@QLHazyCoder 🎉 感谢您的贡献,此PR已合并,并在内部进行测试。 |
变更内容 / Change Content
Bug fix
New feature
Other changes
Add
PUT /api/groups/reorderendpoint with transactional batch update for group sort order.Implement drag-and-drop reordering in
GroupList.vuewith visual drop indicators and rollback on save failure.Disable drag sorting while searching and on touch devices to avoid ambiguous interactions.
Add i18n messages for reorder validation and UI feedback in
zh-CN,en-US, andja-JP.自查清单 / Checklist
Summary by CodeRabbit
New Features
Bug Fixes
Localization
Accessibility