@allbluecn/web-app 0.19.5 → 0.21.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.
Files changed (235) hide show
  1. package/package.json +3 -3
  2. package/src/components/bug/__tests__/board-subgroups.test.tsx +1 -1
  3. package/src/components/bug/__tests__/board-view.test.tsx +1 -1
  4. package/src/components/bug/__tests__/comment-reactions.test.tsx +1 -1
  5. package/src/components/bug/__tests__/create-bug-dialog.test.tsx +2 -2
  6. package/src/components/bug/__tests__/filters-compat.test.tsx +1 -1
  7. package/src/components/bug/__tests__/fix-request-race.test.tsx +1 -1
  8. package/src/components/bug/__tests__/peek-modes.test.tsx +1 -1
  9. package/src/components/bug/__tests__/use-paged-bug-groups.test.tsx +1 -1
  10. package/src/components/bug/__tests__/view-type.test.tsx +1 -1
  11. package/src/components/bug/bug-list-view.tsx +2 -2
  12. package/src/components/bug/bug-toolbar.tsx +1 -1
  13. package/src/components/bug/create-bug-dialog.tsx +3 -3
  14. package/src/components/bug/detail/__tests__/activity-timeline.test.tsx +1 -1
  15. package/src/components/bug/detail/__tests__/bug-detail-collab.test.tsx +1 -1
  16. package/src/components/bug/detail/__tests__/repro-report-card.test.tsx +1 -1
  17. package/src/components/bug/detail/activity-tabs.tsx +34 -3
  18. package/src/components/bug/detail/activity-timeline.tsx +1 -1
  19. package/src/components/bug/detail/bug-detail-body.tsx +3 -3
  20. package/src/components/bug/detail/comment-input.tsx +1 -1
  21. package/src/components/bug/detail/comment-item.tsx +1 -1
  22. package/src/components/bug/detail/comment-reactions.tsx +1 -1
  23. package/src/components/bug/detail/comment-thread.tsx +1 -1
  24. package/src/components/bug/detail/props-section.tsx +2 -2
  25. package/src/components/bug/detail/repro-report-card.tsx +1 -1
  26. package/src/components/bug/detail/reproduce-steps-view.tsx +1 -1
  27. package/src/components/bug/display/use-paged-bug-groups.ts +2 -2
  28. package/src/components/bug/filters/field-configs.tsx +1 -1
  29. package/src/components/bug/inline-editors/assignee-editor.tsx +1 -1
  30. package/src/components/bug/peek-panel.tsx +2 -2
  31. package/src/components/bug/reproduce-steps-editor.tsx +1 -1
  32. package/src/components/bug/story-bugs-panel.tsx +10 -6
  33. package/src/components/bug/views/board-view.tsx +2 -2
  34. package/src/components/bug/views/bug-list/batch-toolbar.tsx +1 -1
  35. package/src/components/bug/views/bug-list/group.tsx +1 -1
  36. package/src/components/bug/views/bug-list/index.tsx +1 -1
  37. package/src/components/bug/views/bug-list/inline-bug-creator.tsx +1 -1
  38. package/src/components/bug/views/bug-list/row.tsx +2 -2
  39. package/src/components/bug/views/create-view-dialog.tsx +1 -1
  40. package/src/components/design/editor/versions/hooks/use-design-versions.ts +11 -4
  41. package/src/components/design/editor/versions/page-version-panel.tsx +30 -11
  42. package/src/components/harness/code/workspace-context.tsx +5 -1
  43. package/src/components/harness/code/workspace-picker.tsx +19 -6
  44. package/src/components/harness/composer/harness-composer.tsx +25 -3
  45. package/src/components/harness/composer/mention/mention-resolvers.ts +2 -0
  46. package/src/components/harness/composer/mention/mention-suggestion-menu.tsx +6 -3
  47. package/src/components/harness/composer/mention/use-mention-sources.ts +1 -1
  48. package/src/components/iteration/iteration-progress-bar.tsx +2 -2
  49. package/src/components/knowledge/public/legal-compliance-editor.tsx +1 -1
  50. package/src/components/layout/sidebar-03/nav-knowledge.tsx +1 -1
  51. package/src/components/layout/sidebar-03/nav-workspace.tsx +1 -1
  52. package/src/components/project/invite-member-dialog.tsx +2 -2
  53. package/src/components/project/project-member-manager.tsx +0 -3
  54. package/src/components/shared-kit/list-kit/kanban/__tests__/instance-key.test.ts +1 -1
  55. package/src/components/story/__tests__/constants.test.ts +1 -1
  56. package/src/components/story/__tests__/dependency-type-badge.test.ts +1 -1
  57. package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +1 -1
  58. package/src/components/story/__tests__/story-list-view-peek-sync.test.tsx +1 -1
  59. package/src/components/story/ai/story-ai-panel.tsx +1 -1
  60. package/src/components/story/ai/story-ai-preview-list.tsx +2 -2
  61. package/src/components/story/ai/use-story-ai-model.ts +17 -4
  62. package/src/components/story/columns.tsx +2 -2
  63. package/src/components/story/create-story-dialog.tsx +4 -4
  64. package/src/components/story/detail/__tests__/activity-timeline.test.tsx +1 -1
  65. package/src/components/story/detail/__tests__/associated-attachments-panel.test.tsx +1 -1
  66. package/src/components/story/detail/__tests__/story-collab.test.tsx +1 -1
  67. package/src/components/story/detail/activity-tabs.tsx +1 -1
  68. package/src/components/story/detail/activity-timeline.tsx +36 -17
  69. package/src/components/story/detail/comment-input.tsx +1 -1
  70. package/src/components/story/detail/comment-item.tsx +1 -1
  71. package/src/components/story/detail/comment-thread.tsx +1 -1
  72. package/src/components/story/detail/reaction-bar.tsx +1 -1
  73. package/src/components/story/detail/story-detail-body.tsx +4 -4
  74. package/src/components/story/detail/story-detail-toolbar/__tests__/attachment-cache.test.ts +83 -0
  75. package/src/components/story/detail/story-detail-toolbar/associated-attachments-panel.tsx +50 -9
  76. package/src/components/story/detail/story-detail-toolbar/children.tsx +3 -3
  77. package/src/components/story/detail/story-detail-toolbar/dependency-panel.tsx +9 -8
  78. package/src/components/story/detail/story-detail-toolbar/index.tsx +8 -8
  79. package/src/components/story/detail/story-detail-toolbar/related-test-cases.tsx +2 -2
  80. package/src/components/story/detail/story-properties.tsx +2 -2
  81. package/src/components/story/display/__tests__/flatten-story-groups.test.ts +1 -1
  82. package/src/components/story/display/__tests__/group-fields.test.ts +1 -1
  83. package/src/components/story/display/__tests__/group-resolver.test.ts +2 -2
  84. package/src/components/story/display/__tests__/order-resolver.test.ts +1 -1
  85. package/src/components/story/display/__tests__/use-grouped-stories.test.ts +1 -1
  86. package/src/components/story/display/__tests__/use-paged-story-groups.test.ts +1 -1
  87. package/src/components/story/display/flatten-story-groups.ts +1 -1
  88. package/src/components/story/display/group-fields.ts +2 -2
  89. package/src/components/story/display/group-resolver.tsx +4 -4
  90. package/src/components/story/display/order-resolver.ts +3 -3
  91. package/src/components/story/display/types.ts +5 -4
  92. package/src/components/story/display/use-grouped-stories.ts +5 -5
  93. package/src/components/story/display/use-paged-story-groups.ts +11 -4
  94. package/src/components/story/entity-overlay.ts +24 -0
  95. package/src/components/story/filters/field-configs.tsx +2 -2
  96. package/src/components/story/inline-editors/assignee-editor.tsx +1 -1
  97. package/src/components/story/inline-editors/dependency-type-select.tsx +1 -1
  98. package/src/components/story/inline-editors/parent-story-editor.tsx +2 -2
  99. package/src/components/story/inline-editors/priority-editor.tsx +2 -2
  100. package/src/components/story/inline-editors/state-editor.tsx +2 -2
  101. package/src/components/story/inline-editors/story-select-dialog.tsx +2 -2
  102. package/src/components/story/peek-panel.tsx +6 -5
  103. package/src/components/story/priority-icon.tsx +2 -2
  104. package/src/components/story/relations/__tests__/attachment-manage-dialog.test.tsx +1 -1
  105. package/src/components/story/relations/__tests__/design-select-dialog.test.tsx +1 -1
  106. package/src/components/story/relations/__tests__/knowledge-select-dialog.test.tsx +1 -1
  107. package/src/components/story/relations/__tests__/link-manage-dialog.test.tsx +1 -1
  108. package/src/components/story/relations/attachment-manage-dialog.tsx +4 -4
  109. package/src/components/story/relations/design-select-dialog.tsx +2 -2
  110. package/src/components/story/relations/knowledge-select-dialog.tsx +2 -2
  111. package/src/components/story/relations/link-manage-dialog.tsx +2 -2
  112. package/src/components/story/relations/story-ref-row.tsx +3 -3
  113. package/src/components/story/story-list-table.tsx +1 -1
  114. package/src/components/story/story-list-view.tsx +1 -1
  115. package/src/components/story/story-toolbar.tsx +1 -1
  116. package/src/components/story/transfer-story-popover.tsx +3 -2
  117. package/src/components/story/views/__tests__/calendar-view.test.tsx +1 -1
  118. package/src/components/story/views/__tests__/kanban-drop-subgroup.test.ts +1 -1
  119. package/src/components/story/views/__tests__/kanban-peek-sync.test.tsx +2 -2
  120. package/src/components/story/views/__tests__/kanban-view-membership-sync.test.tsx +1 -1
  121. package/src/components/story/views/__tests__/timeline-view.test.tsx +1 -1
  122. package/src/components/story/views/calendar/__tests__/mappers.test.ts +1 -1
  123. package/src/components/story/views/calendar/mappers.ts +1 -1
  124. package/src/components/story/views/calendar-view.tsx +1 -1
  125. package/src/components/story/views/create-view-dialog.tsx +1 -1
  126. package/src/components/story/views/kanban-view.tsx +2 -2
  127. package/src/components/story/views/story-list/batch-preview-dialog.tsx +2 -2
  128. package/src/components/story/views/story-list/batch-toolbar.tsx +3 -3
  129. package/src/components/story/views/story-list/group.tsx +18 -5
  130. package/src/components/story/views/story-list/index.tsx +4 -2
  131. package/src/components/story/views/story-list/inline-story-creator.tsx +2 -2
  132. package/src/components/story/views/story-list/row.tsx +2 -2
  133. package/src/components/story/views/story-list/use-flat-rows.ts +1 -1
  134. package/src/components/story/views/timeline/__tests__/mappers.test.ts +2 -2
  135. package/src/components/story/views/timeline/mappers.ts +2 -2
  136. package/src/components/story/views/timeline/use-date-commit.ts +1 -1
  137. package/src/components/story/views/timeline-view.tsx +2 -2
  138. package/src/components/testcase/__tests__/case-row.test.tsx +1 -1
  139. package/src/components/testcase/__tests__/create-dialog-scope.test.tsx +1 -1
  140. package/src/components/testcase/__tests__/display-options.test.tsx +1 -1
  141. package/src/components/testcase/__tests__/export-entry.test.tsx +1 -1
  142. package/src/components/testcase/__tests__/list-view.test.tsx +1 -1
  143. package/src/components/testcase/__tests__/step-editor.test.tsx +4 -2
  144. package/src/components/testcase/__tests__/toolbar.test.tsx +1 -1
  145. package/src/components/testcase/create-testcase-dialog.tsx +6 -5
  146. package/src/components/testcase/detail/case-detail-body.tsx +1 -1
  147. package/src/components/testcase/detail/properties.tsx +1 -1
  148. package/src/components/testcase/display/display-dropdown.tsx +2 -2
  149. package/src/components/testcase/display/use-testcase-display-options.ts +1 -1
  150. package/src/components/testcase/filters/field-configs.tsx +1 -1
  151. package/src/components/testcase/generation/draft-card.tsx +2 -2
  152. package/src/components/testcase/inline-editors/assignee-editor.tsx +1 -1
  153. package/src/components/testcase/list/case-row.tsx +1 -1
  154. package/src/components/testcase/list/grouped-list.tsx +1 -1
  155. package/src/components/testcase/list-view.tsx +2 -2
  156. package/src/components/testcase/runs/convert-bug-button.tsx +2 -2
  157. package/src/components/testcase/step-editor.tsx +13 -2
  158. package/src/components/testcase/testcase-toolbar.tsx +2 -2
  159. package/src/components/testcase/views/batch-toolbar.tsx +1 -1
  160. package/src/components/testcase/views/board-view.tsx +2 -2
  161. package/src/lib/changelog/sdk-versions.ts +2 -2
  162. package/src/lib/collections/knowledge/knowledge-bases-collection.ts +2 -1
  163. package/src/lib/collections/query-keys.ts +18 -0
  164. package/src/lib/collections/reference/users-brief-collection.ts +2 -1
  165. package/src/lib/collections/reference/workspaces-collection.ts +2 -1
  166. package/src/lib/story/__tests__/activity-grouper.test.ts +1 -1
  167. package/src/lib/story/activity-grouper.ts +1 -1
  168. package/src/paraglide/messages/_index.d.ts +2 -0
  169. package/src/paraglide/messages/_index.js +88269 -5642
  170. package/src/paraglide/messages/en.js +28255 -0
  171. package/src/paraglide/messages/story_group_loadfailed1.d.ts +17 -0
  172. package/src/paraglide/messages/story_group_loadfailed1.js +30 -0
  173. package/src/paraglide/messages/story_group_retry.d.ts +16 -0
  174. package/src/paraglide/messages/story_group_retry.js +29 -0
  175. package/src/paraglide/messages/zh.js +28253 -0
  176. package/src/routes/_nav/bugs/$bugId/route.lazy.tsx +2 -2
  177. package/src/routes/_nav/projects/$projectId/iterations/$iterationId/route.lazy.tsx +1 -1
  178. package/src/routes/_nav/route.tsx +1 -2
  179. package/src/routes/_nav/stories/$storyId/route.lazy.tsx +2 -2
  180. package/src/routes/_nav/stories/$storyId/route.tsx +1 -1
  181. package/src/routes/api/design/prototype/__tests__/route.test.ts +4 -1
  182. package/src/server/__tests__/task-executor.test.ts +7 -0
  183. package/src/server/ai-gateway/__tests__/ai-output-guard.test.ts +4 -4
  184. package/src/server/ai-gateway/__tests__/tools-authorization.test.ts +25 -0
  185. package/src/server/ai-gateway/tools/server.ts +24 -15
  186. package/src/server/knowledge/reindex-guard.ts +35 -0
  187. package/src/server/lib/__tests__/cron-auth.test.ts +1 -1
  188. package/src/server/serverFns/__tests__/legal-code-canonical.test.ts +169 -0
  189. package/src/server/serverFns/__tests__/story-tree.test.ts +0 -1
  190. package/src/server/serverFns/__tests__/story-types.test.ts +2 -1
  191. package/src/server/serverFns/__tests__/update-bug-guard.test.ts +1 -1
  192. package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +95 -44
  193. package/src/server/serverFns/bug/__tests__/bug-crud.test.ts +46 -18
  194. package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +1 -1
  195. package/src/server/serverFns/bug/bug-activities.ts +2 -2
  196. package/src/server/serverFns/bug/bug-comments.ts +44 -19
  197. package/src/server/serverFns/bug/bug-crud/detail-select.ts +1 -1
  198. package/src/server/serverFns/bug/bug-crud/detail.ts +2 -2
  199. package/src/server/serverFns/bug/bug-crud.ts +142 -110
  200. package/src/server/serverFns/bug/bug-group-query.ts +1 -1
  201. package/src/server/serverFns/bug/bug-groups.ts +2 -2
  202. package/src/server/serverFns/bug/bug-story-utils.ts +1 -1
  203. package/src/server/serverFns/chat/conversations.ts +27 -13
  204. package/src/server/serverFns/harness/github-workspace.ts +32 -25
  205. package/src/server/serverFns/harness/search-mention-candidates.ts +3 -1
  206. package/src/server/serverFns/iteration/iteration-crud.ts +1 -1
  207. package/src/server/serverFns/knowledge/public/legal/create.ts +3 -0
  208. package/src/server/serverFns/knowledge/public/legal/update.ts +8 -8
  209. package/src/server/serverFns/knowledge-index.ts +12 -5
  210. package/src/server/serverFns/project.ts +5 -11
  211. package/src/server/serverFns/story/__tests__/story-dependencies.test.ts +15 -4
  212. package/src/server/serverFns/story/__tests__/story-transfer.test.ts +20 -0
  213. package/src/server/serverFns/story/link-preview.ts +3 -2
  214. package/src/server/serverFns/story/story-children.ts +1 -1
  215. package/src/server/serverFns/story/story-comments.ts +1 -1
  216. package/src/server/serverFns/story/story-crud/bulk.ts +1 -1
  217. package/src/server/serverFns/story/story-crud.ts +1 -1
  218. package/src/server/serverFns/story/story-dependencies.ts +61 -69
  219. package/src/server/serverFns/story/story-groups.ts +1 -1
  220. package/src/server/serverFns/story/story-transfer.ts +8 -20
  221. package/src/server/serverFns/testcase/__tests__/automation.test.ts +21 -0
  222. package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +1 -1
  223. package/src/server/serverFns/testcase/automation.ts +24 -1
  224. package/src/server/serverFns/testcase/testcase-board.ts +1 -1
  225. package/src/server/serverFns/testcase/testcase-crud.ts +1 -1
  226. package/src/server/serverFns/testcase/testcase-display-options.ts +1 -1
  227. package/src/server/serverFns/testcase/testcase-filter-views.ts +1 -1
  228. package/src/server/serverFns/testcase/types.ts +1 -1
  229. package/src/server/story/types.ts +7 -3
  230. package/src/shared/bug/__tests__/constants.test.ts +1 -1
  231. package/src/{components → shared}/bug/types.ts +4 -3
  232. package/src/{components → shared}/story/constants.ts +1 -1
  233. package/src/{components → shared}/story/types.ts +1 -7
  234. package/src/types/project.ts +1 -2
  235. /package/src/{components/testcase/display → shared/testcase}/field-configs.ts +0 -0
@@ -59,13 +59,13 @@ import { toast } from "@/components/ui/toast";
59
59
  import { toastError } from "@/lib/toast-error";
60
60
  import { m } from "@/paraglide/messages.js";
61
61
  import { updateStoryFn, reorderStoriesFn } from "@/server/serverFns/story";
62
- import type { StoryListItem, UserBrief } from "../types";
62
+ import type { StoryListItem, UserBrief } from "@/shared/story/types";
63
63
  import { GROUP_FIELD, toGroupFieldPatch, type StoryPatch } from "../display/group-fields";
64
64
  import { buildGroupMeta, groupUniverse, resolveColumnTint } from "../display/group-resolver";
65
65
  import { COLLAPSED_SUBGROUPS_KEY } from "../display/use-collapsed-subgroups";
66
66
  import { useFitCount } from "../display/use-fit-count";
67
67
  import { DateEditor, PriorityEditor, StateEditor } from "../inline-editors";
68
- import { PRIORITY_LABEL, STATE_LABEL, STATE_ICON, STATE_COLOR } from "../constants";
68
+ import { PRIORITY_LABEL, STATE_LABEL, STATE_ICON, STATE_COLOR } from "@/shared/story/constants";
69
69
  import { PriorityIcon } from "../priority-icon";
70
70
  import type {
71
71
  DisplayOptions,
@@ -40,10 +40,10 @@ import {
40
40
  STORY_STATES,
41
41
  STORY_PRIORITIES,
42
42
  type StoryState,
43
- } from "../../constants";
43
+ } from "@/shared/story/constants";
44
44
  import { FilterCheckPopover } from "../../filters/filter-check-popover";
45
45
  import { PriorityIcon } from "../../priority-icon";
46
- import type { StoryListItem } from "../../types";
46
+ import type { StoryListItem } from "@/shared/story/types";
47
47
  import { m } from "@/paraglide/messages.js";
48
48
 
49
49
  type BatchPreviewMode = "archive" | "delete";
@@ -28,9 +28,9 @@ import {
28
28
  DateEditor,
29
29
  LabelsEditor,
30
30
  } from "../../inline-editors";
31
- import type { UserBrief } from "../../types";
31
+ import type { UserBrief } from "@/shared/story/types";
32
32
  import type { StoryPatch } from "../../display/group-fields";
33
- import type { StoryListItem } from "../../types";
33
+ import type { StoryListItem } from "@/shared/story/types";
34
34
  import { BatchPreviewDialog } from "./batch-preview-dialog";
35
35
  import { TransferStoryPopover } from "../../transfer-story-popover";
36
36
  import { m } from "@/paraglide/messages.js";
@@ -57,7 +57,7 @@ export interface BatchToolbarProps {
57
57
  onRemoveRelations?: (storyIds: string[]) => void;
58
58
  onBulkUpdate: (patch: StoryPatch) => void;
59
59
  onClearSelection: () => void;
60
- onTransfer: (updatedStories?: Array<{ id: string; identifier: string; projectId: string; parentId: string | null }>) => void;
60
+ onTransfer: (updatedStories?: Array<{ id: string; identifier: string; projectId: string | null; parentId: string | null }>) => void;
61
61
  }
62
62
 
63
63
  export function BatchToolbar({
@@ -16,11 +16,11 @@
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
18
  import { memo, useEffect, useState } from "react";
19
- import { ChevronDown, ChevronRight, Plus, Loader2, ChevronsDown } from "lucide-react";
19
+ import { ChevronDown, ChevronRight, Plus, Loader2, ChevronsDown, AlertCircle } from "lucide-react";
20
20
  import { Button } from "@/components/ui/button";
21
21
  import { Checkbox } from "@/components/ui/checkbox";
22
- import type { UserBrief } from "../../types";
23
- import { STORY_STATES, STORY_PRIORITIES, type StoryState, type StoryPriority } from "../../constants";
22
+ import type { UserBrief } from "@/shared/story/types";
23
+ import { STORY_STATES, STORY_PRIORITIES, type StoryState, type StoryPriority } from "@/shared/story/constants";
24
24
  import type { GroupNode, GroupKey } from "../../display/types";
25
25
  import { cn } from "@/lib/utils";
26
26
  import { InlineStoryCreator } from "./inline-story-creator";
@@ -35,7 +35,9 @@ const GROUP_DIMENSION_LABEL: Record<Extract<GroupKey, "assignee" | "createdBy">,
35
35
  };
36
36
 
37
37
  interface VirtualGroupHeaderProps {
38
- node: GroupNode;
38
+ node: GroupNode & { error?: boolean };
39
+ /** S11:node.error 时的重试回调(不传则只显示失败态) */
40
+ onRetry?: () => void;
39
41
  depth: number;
40
42
  collapsed: boolean;
41
43
  collapseKey: string;
@@ -51,7 +53,7 @@ interface VirtualGroupHeaderProps {
51
53
  showRoleLabel: boolean;
52
54
  }
53
55
 
54
- export const VirtualGroupHeader = memo(function VirtualGroupHeader({ node, depth, collapsed, collapseKey, onToggle, onCreate, sticky, hideCreate, selectState, onToggleSelect, showSelect, groupDimension, showRoleLabel }: VirtualGroupHeaderProps) {
56
+ export const VirtualGroupHeader = memo(function VirtualGroupHeader({ node, depth, collapsed, collapseKey, onToggle, onCreate, onRetry, sticky, hideCreate, selectState, onToggleSelect, showSelect, groupDimension, showRoleLabel }: VirtualGroupHeaderProps) {
55
57
  const textColor =
56
58
  node.color?.split(" ").find((c) => c.startsWith("text")) ?? "text-muted-foreground";
57
59
  const Icon = node.icon;
@@ -87,6 +89,17 @@ export const VirtualGroupHeader = memo(function VirtualGroupHeader({ node, depth
87
89
  </span>
88
90
  )}
89
91
  <span className="text-xs text-muted-foreground">{node.totalCount ?? node.items.length}</span>
92
+ {node.error && (
93
+ <button
94
+ type="button"
95
+ onClick={(e) => { e.stopPropagation(); onRetry?.(); }}
96
+ className="inline-flex h-4 shrink-0 items-center gap-0.5 rounded border border-destructive/30 px-1 text-[10px] font-normal text-destructive hover:bg-destructive/10"
97
+ title={m.story_group_loadFailed()}
98
+ >
99
+ <AlertCircle className="size-2.5" />
100
+ {m.story_group_retry()}
101
+ </button>
102
+ )}
90
103
  </button>
91
104
  {depth === 0 && onCreate && !hideCreate && (
92
105
  <button
@@ -26,8 +26,8 @@ import {
26
26
  reorderStoriesFn,
27
27
  } from "@/server/serverFns/story";
28
28
  import { toast } from "@/components/ui/toast";
29
- import type { UserBrief } from "../../types";
30
- import type { StoryState, StoryPriority } from "../../constants";
29
+ import type { UserBrief } from "@/shared/story/types";
30
+ import type { StoryState, StoryPriority } from "@/shared/story/constants";
31
31
  import type { DisplayOptions } from "../../display/types";
32
32
  import type { PagedGroupNode } from "../../display/use-paged-story-groups";
33
33
  import { BUTTON_ROW_HEIGHT, useFlatRows } from "./use-flat-rows";
@@ -484,6 +484,7 @@ export function StoryList({ groups, isLoading = false, displayFilters, users, la
484
484
  showSelect={selectedIds.size > 0}
485
485
  groupDimension={row.depth === 0 ? groupBy : subGroupBy}
486
486
  showRoleLabel={showRoleLabel}
487
+ onRetry={onRefresh}
487
488
  />
488
489
  </div>
489
490
  {create && (
@@ -556,6 +557,7 @@ export function StoryList({ groups, isLoading = false, displayFilters, users, la
556
557
  >
557
558
  <VirtualGroupHeader
558
559
  node={row.node}
560
+ onRetry={onRefresh}
559
561
  depth={row.depth}
560
562
  collapsed={row.isCollapsed}
561
563
  collapseKey={row.key}
@@ -18,8 +18,8 @@
18
18
  import { memo, useEffect, useRef, useMemo } from "react";
19
19
  import { Loader2, CalendarCheck, CalendarPlus2, Check, X, Trees } from "lucide-react";
20
20
  import { LucideIconByName } from "@/components/shared-kit/icons/lucide-icon-picker/lucide-icon-by-name";
21
- import type { UserBrief } from "../../types";
22
- import type { StoryState, StoryPriority } from "../../constants";
21
+ import type { UserBrief } from "@/shared/story/types";
22
+ import type { StoryState, StoryPriority } from "@/shared/story/constants";
23
23
  import { PriorityEditor, StateEditor, AssigneeEditor, DateEditor, LabelsEditor } from "../../inline-editors";
24
24
  import { m } from "@/paraglide/messages.js";
25
25
 
@@ -20,8 +20,8 @@ import { useServerFn } from "@tanstack/react-start";
20
20
  import { CalendarCheck, CalendarPlus2 } from "lucide-react";
21
21
  import { Checkbox } from "@/components/ui/checkbox";
22
22
  import { updateStoryFn } from "@/server/serverFns/story";
23
- import type { StoryListItem, UserBrief } from "../../types";
24
- import type { StoryState, StoryPriority } from "../../constants";
23
+ import type { StoryListItem, UserBrief } from "@/shared/story/types";
24
+ import type { StoryState, StoryPriority } from "@/shared/story/constants";
25
25
  import type { DisplayProperty } from "../../display/types";
26
26
  import { PriorityEditor, StateEditor, AssigneeEditor, DateEditor, LabelsEditor } from "../../inline-editors";
27
27
  import { cn } from "@/lib/utils";
@@ -16,7 +16,7 @@
16
16
  // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
 
18
18
  import { useMemo } from "react";
19
- import type { StoryListItem } from "../../types";
19
+ import type { StoryListItem } from "@/shared/story/types";
20
20
  import type { GroupNode } from "../../display/types";
21
21
 
22
22
  /** 组尾「显示更多」/「添加需求」组合行高 */
@@ -30,9 +30,9 @@ import {
30
30
  type IterationBaseline,
31
31
  } from "../mappers";
32
32
  import { STATE_COLUMN_TINT, PRIORITY_COLUMN_TINT, resolveGroupColor } from "../../../display/group-resolver";
33
- import { STORY_STATES, STATE_LABEL, PRIORITY_LABEL, type StoryState } from "../../../constants";
33
+ import { STORY_STATES, STATE_LABEL, PRIORITY_LABEL, type StoryState } from "@/shared/story/constants";
34
34
  import { parseDate } from "@/lib/datetime";
35
- import type { StoryListItem } from "../../../types";
35
+ import type { StoryListItem } from "@/shared/story/types";
36
36
  import type { GroupContext } from "../../../display/types";
37
37
  import type { GanttEvent, GanttResource } from "@/components/reui/gantt/gantt-types";
38
38
  import { m } from "@/paraglide/messages.js";
@@ -35,8 +35,8 @@ import type {
35
35
  GanttEvent,
36
36
  GanttResource,
37
37
  } from "@/components/reui/gantt/gantt-types";
38
- import { STORY_STATES, type StoryState, type StoryPriority } from "../../constants";
39
- import type { StoryListItem } from "../../types";
38
+ import { STORY_STATES, type StoryState, type StoryPriority } from "@/shared/story/constants";
39
+ import type { StoryListItem } from "@/shared/story/types";
40
40
  import type { GroupContext, GroupKey, GroupNode } from "../../display/types";
41
41
  // 分组语义单一来源:priority/assignee/labels/createdBy/project 的桶划分与
42
42
  // 标签/颜色解析全部复用 display/group-resolver(禁止重写分组语义)。
@@ -36,7 +36,7 @@ import { toast } from "@/components/ui/toast";
36
36
  import { toastError } from "@/lib/toast-error";
37
37
  import { m } from "@/paraglide/messages.js";
38
38
  import { fromGanttRangeToStoryDates } from "./mappers";
39
- import type { StoryState } from "../../constants";
39
+ import type { StoryState } from "@/shared/story/constants";
40
40
 
41
41
  /** 乐观覆盖:storyId → 覆盖后的两个日期(targetDate null = 保持无截止) */
42
42
  export interface StoryDateOverride {
@@ -70,12 +70,12 @@ import type {
70
70
  GanttResource,
71
71
  GanttScale,
72
72
  } from "@/components/reui/gantt/gantt-types";
73
- import { STATE_COLOR, STATE_LABEL } from "../constants";
73
+ import { STATE_COLOR, STATE_LABEL } from "@/shared/story/constants";
74
74
  import {
75
75
  TIMELINE_GROUP_KEYS,
76
76
  type TimelineGroupKey,
77
77
  } from "../display/constants";
78
- import type { StoryListItem, UserBrief } from "../types";
78
+ import type { StoryListItem, UserBrief } from "@/shared/story/types";
79
79
  import { applyOrder } from "../display/order-resolver";
80
80
  import type { GroupContext, GroupKey, OrderKey } from "../display/types";
81
81
  import {
@@ -84,7 +84,7 @@ class MockIntersectionObserver {
84
84
  import { CaseRow } from "../list/case-row";
85
85
  import { GroupedList, type TestCaseGroupBy, type TestCaseGroupInfo } from "../list/grouped-list";
86
86
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
87
- import type { UserBrief } from "@/components/story/types";
87
+ import type { UserBrief } from "@/shared/story/types";
88
88
  import type { FolderNodeDto } from "../folder-tree";
89
89
 
90
90
  const makeUser = (id: string, username: string): UserBrief => ({
@@ -50,7 +50,7 @@ vi.mock("@/components/story/inline-editors/story-select-dialog", () => ({
50
50
 
51
51
  import { CreateTestCaseDialog } from "../create-testcase-dialog";
52
52
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
53
- import type { UserBrief } from "@/components/story/types";
53
+ import type { UserBrief } from "@/shared/story/types";
54
54
  import type { FolderNodeDto } from "../folder-tree";
55
55
 
56
56
  const makeUser = (id: string, username: string): UserBrief => ({
@@ -36,7 +36,7 @@ import {
36
36
  DEFAULT_TESTCASE_DISPLAY_OPTIONS,
37
37
  TESTCASE_DISPLAY_PROPERTIES,
38
38
  type TestCaseDisplayOptions,
39
- } from "../display/field-configs";
39
+ } from "@/shared/testcase/field-configs";
40
40
 
41
41
  const OPTIONS: TestCaseDisplayOptions = {
42
42
  ...DEFAULT_TESTCASE_DISPLAY_OPTIONS,
@@ -64,7 +64,7 @@ vi.mock("@/components/ui/toast", () => ({
64
64
  vi.mock("@/lib/toast-error", () => ({ toastError: H.toastErrorFn }));
65
65
 
66
66
  import { TestCaseToolbar } from "../testcase-toolbar";
67
- import type { TestCaseDisplayOptions } from "../display/field-configs";
67
+ import type { TestCaseDisplayOptions } from "@/shared/testcase/field-configs";
68
68
  import type { TestcaseFilterCondition } from "../filters";
69
69
 
70
70
  const CSV = "\uFEFF编号,标题,类型,优先级,状态,负责人\r\nWEB-TC1,登录成功,functional,p1,approved,";
@@ -180,7 +180,7 @@ class MockIntersectionObserver {
180
180
  import { TestCaseListView } from "../list-view";
181
181
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
182
182
  import type { TestCaseFilterViewDTO } from "@/server/serverFns/testcase/testcase-filter-views";
183
- import { DEFAULT_TESTCASE_DISPLAY_OPTIONS, type TestCaseDisplayOptions } from "../display/field-configs";
183
+ import { DEFAULT_TESTCASE_DISPLAY_OPTIONS, type TestCaseDisplayOptions } from "@/shared/testcase/field-configs";
184
184
  import { listTestCaseBoardColumnsFn } from "@/server/serverFns/testcase/testcase-board";
185
185
  import { serializeFilters } from "../filters";
186
186
 
@@ -71,7 +71,7 @@ import { useState } from "react";
71
71
  import { StepEditor, type StepDraft } from "../step-editor";
72
72
  import { CreateTestCaseDialog } from "../create-testcase-dialog";
73
73
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
74
- import type { UserBrief } from "@/components/story/types";
74
+ import type { UserBrief } from "@/shared/story/types";
75
75
  import type { FolderNodeDto } from "../folder-tree";
76
76
  import { toast } from "@/components/ui/toast";
77
77
 
@@ -157,7 +157,9 @@ describe("StepEditor", () => {
157
157
  fireEvent.click(screen.getByTestId("step-add"));
158
158
  const last = onChange.mock.calls.at(-1)![0] as StepDraft[];
159
159
  expect(last).toHaveLength(3);
160
- expect(last[2]).toEqual({ action: "", expected: "" });
160
+ // S15:新增行携带稳定 id(仅本地草稿键控用)
161
+ expect(last[2]).toMatchObject({ action: "", expected: "" });
162
+ expect(typeof last[2]!.id).toBe("string");
161
163
  });
162
164
 
163
165
  it("边界序号:首行上移/末行下移为 no-op(按钮禁用)", () => {
@@ -60,7 +60,7 @@ vi.mock("@/server/serverFns/testcase/testcase-export", () => ({
60
60
  }));
61
61
 
62
62
  import { TestCaseToolbar } from "../testcase-toolbar";
63
- import type { TestCaseDisplayOptions } from "../display/field-configs";
63
+ import type { TestCaseDisplayOptions } from "@/shared/testcase/field-configs";
64
64
 
65
65
  afterEach(() => {
66
66
  cleanup();
@@ -47,13 +47,13 @@ import { toast } from "@/components/ui/toast";
47
47
  import { createTestCaseFn } from "@/server/serverFns/testcase/testcase-crud";
48
48
  import type { TestCasePriority, TestCaseType } from "@/server/serverFns/testcase/constants";
49
49
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
50
- import type { UserBrief } from "@/components/story/types";
50
+ import type { UserBrief } from "@/shared/story/types";
51
51
  import { StorySelectDialog } from "@/components/story/inline-editors/story-select-dialog";
52
52
  import { TypeEditor } from "./inline-editors/type-editor";
53
53
  import { PriorityEditor } from "./inline-editors/priority-editor";
54
54
  import { FolderEditor } from "./inline-editors/folder-editor";
55
55
  import { AssigneeEditor } from "./inline-editors/assignee-editor";
56
- import { StepEditor, isStepDraftsValid, type StepDraft } from "./step-editor";
56
+ import { StepEditor, isStepDraftsValid, newStepDraftId, type StepDraft } from "./step-editor";
57
57
  import type { FolderNodeDto } from "./folder-tree";
58
58
  import { m } from "@/paraglide/messages.js";
59
59
 
@@ -77,7 +77,8 @@ interface CreateTestCaseDialogProps {
77
77
  onSuccess?: (testCase: TestCaseSummary) => void;
78
78
  }
79
79
 
80
- const INITIAL_STEPS: StepDraft[] = [{ action: "", expected: "" }];
80
+ // S15:工厂替代模块级常量(每次打开 dialog 生成带稳定 id 的行)
81
+ const makeInitialSteps = (): StepDraft[] => [{ id: newStepDraftId(), action: "", expected: "" }];
81
82
 
82
83
  export function CreateTestCaseDialog({
83
84
  open,
@@ -91,7 +92,7 @@ export function CreateTestCaseDialog({
91
92
  }: CreateTestCaseDialogProps) {
92
93
  const [title, setTitle] = useState("");
93
94
  const [precondition, setPrecondition] = useState("");
94
- const [steps, setSteps] = useState<StepDraft[]>(INITIAL_STEPS);
95
+ const [steps, setSteps] = useState<StepDraft[]>(makeInitialSteps);
95
96
  const [type, setType] = useState<TestCaseType>("functional");
96
97
  const [priority, setPriority] = useState<TestCasePriority>("p1");
97
98
  const [folderId, setFolderId] = useState<string | null>(null);
@@ -109,7 +110,7 @@ export function CreateTestCaseDialog({
109
110
  if (!open) return;
110
111
  setTitle("");
111
112
  setPrecondition("");
112
- setSteps(INITIAL_STEPS.map((s) => ({ ...s })));
113
+ setSteps(makeInitialSteps());
113
114
  setType("functional");
114
115
  setPriority("p1");
115
116
  setFolderId(initialFolderId ?? null);
@@ -39,7 +39,7 @@ import type {
39
39
  TestCaseDetail,
40
40
  TestCaseStoryRef,
41
41
  } from "@/server/serverFns/testcase/types";
42
- import type { UserBrief } from "@/components/story/types";
42
+ import type { UserBrief } from "@/shared/story/types";
43
43
  import type { FolderNodeDto } from "../folder-tree";
44
44
  import type { TestCaseUpdatePatch } from "../list/case-row";
45
45
  import { buildRoomName } from "@allbluecn/collab-core/rooms";
@@ -24,7 +24,7 @@ import { ExternalLink } from "lucide-react";
24
24
  import { Badge } from "@/components/ui/badge";
25
25
  import type { TestCaseDetail, TestCaseStoryRef } from "@/server/serverFns/testcase/types";
26
26
  import type { TestCaseAutomation } from "@/server/serverFns/testcase/constants";
27
- import type { UserBrief } from "@/components/story/types";
27
+ import type { UserBrief } from "@/shared/story/types";
28
28
  import type { FolderNodeDto } from "../folder-tree";
29
29
  import type { TestCaseUpdatePatch } from "../list/case-row";
30
30
  import { AssigneeEditor } from "../inline-editors/assignee-editor";
@@ -42,14 +42,14 @@ import type {
42
42
  TestCaseDisplayProperty,
43
43
  TestCaseGroupKey,
44
44
  TestCaseOrderKey,
45
- } from "./field-configs";
45
+ } from "@/shared/testcase/field-configs";
46
46
  import {
47
47
  DEFAULT_TESTCASE_DISPLAY_OPTIONS,
48
48
  TESTCASE_DISPLAY_PROPERTIES,
49
49
  TESTCASE_GROUP_KEYS,
50
50
  TESTCASE_ORDER_KEYS,
51
51
  TESTCASE_PAGE_SIZE_OPTIONS,
52
- } from "./field-configs";
52
+ } from "@/shared/testcase/field-configs";
53
53
 
54
54
  /** 分组维度标签(testcase_display_ 域文案,照 bug display/constants 裁剪) */
55
55
  const GROUP_LABELS: Record<TestCaseGroupKey, () => string> = {
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  DEFAULT_TESTCASE_DISPLAY_OPTIONS,
28
28
  type TestCaseDisplayOptions,
29
- } from "./field-configs";
29
+ } from "@/shared/testcase/field-configs";
30
30
 
31
31
  export interface TestCaseDisplayOptionsState {
32
32
  filters: TestCaseDisplayOptions;
@@ -69,7 +69,7 @@ import type {
69
69
  FilterUserLike,
70
70
  } from "@/components/shared-kit/list-kit/filters";
71
71
  import { cn } from "@/lib/utils";
72
- import type { UserBrief } from "@/components/story/types";
72
+ import type { UserBrief } from "@/shared/story/types";
73
73
 
74
74
  export { serializeFilters, deserializeFilters, OPERATOR_LABELS, generateConditionId } from "@/components/shared-kit/list-kit/filters";
75
75
  export type { FilterOperator, FilterFieldType };
@@ -33,7 +33,7 @@ import {
33
33
  } from "@/components/ui/dialog";
34
34
  import { Input } from "@/components/ui/input";
35
35
  import { Textarea } from "@/components/ui/textarea";
36
- import { StepEditor, isStepDraftsValid, type StepDraft } from "@/components/testcase/step-editor";
36
+ import { StepEditor, isStepDraftsValid, newStepDraftId, type StepDraft } from "@/components/testcase/step-editor";
37
37
  import { stateLabel, typeLabel, priorityLabel } from "@/components/testcase/filters";
38
38
  import type {
39
39
  TestCasePriority,
@@ -121,7 +121,7 @@ export function DraftCard({
121
121
  const [title, setTitle] = useState(draft.title);
122
122
  const [precondition, setPrecondition] = useState(draft.precondition ?? "");
123
123
  const [steps, setSteps] = useState<StepDraft[]>(() =>
124
- draft.steps.steps.map((s) => ({ action: s.action, expected: s.expected ?? "" })),
124
+ draft.steps.steps.map((s) => ({ id: newStepDraftId(), action: s.action, expected: s.expected ?? "" })),
125
125
  );
126
126
 
127
127
  const openEditDialog = () => {
@@ -22,7 +22,7 @@ import { Check, CircleUser, Search } from "lucide-react";
22
22
  import { Input } from "@/components/ui/input";
23
23
  import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
24
24
  import { UserAvatar } from "@/components/shared-kit";
25
- import type { UserBrief } from "@/components/story/types";
25
+ import type { UserBrief } from "@/shared/story/types";
26
26
  import { cn } from "@/lib/utils";
27
27
  import { m } from "@/paraglide/messages.js";
28
28
 
@@ -40,7 +40,7 @@ import {
40
40
  import { updateTestCaseFn } from "@/server/serverFns/testcase/testcase-crud";
41
41
  import type { TestCaseAutomation, TestCasePriority, TestCaseState, TestCaseType } from "@/server/serverFns/testcase/constants";
42
42
  import type { TestCaseSteps, TestCaseSummary } from "@/server/serverFns/testcase/types";
43
- import type { UserBrief } from "@/components/story/types";
43
+ import type { UserBrief } from "@/shared/story/types";
44
44
  import type { FolderNodeDto } from "../folder-tree";
45
45
  import { PriorityEditor } from "../inline-editors/priority-editor";
46
46
  import { StateEditor } from "../inline-editors/state-editor";
@@ -27,7 +27,7 @@ import { ChevronDown, ChevronRight } from "lucide-react";
27
27
  import { InfiniteScrollSentinel } from "@/components/infinite-scroll-sentinel";
28
28
  import type { TestCaseState, TestCasePriority } from "@/server/serverFns/testcase/constants";
29
29
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
30
- import type { UserBrief } from "@/components/story/types";
30
+ import type { UserBrief } from "@/shared/story/types";
31
31
  import type { FolderNodeDto } from "../folder-tree";
32
32
  import { priorityLabel, stateLabel } from "../filters";
33
33
  import { CaseRow } from "./case-row";
@@ -53,7 +53,7 @@ import {
53
53
  updateFilterViewFn,
54
54
  } from "@/server/serverFns/testcase/testcase-filter-views";
55
55
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
56
- import type { UserBrief } from "@/components/story/types";
56
+ import type { UserBrief } from "@/shared/story/types";
57
57
  import { GroupedList, groupKeyOf, type TestCaseGroupInfo } from "./list/grouped-list";
58
58
  import { computeMove, canMove } from "@/components/shared-kit/list-kit/move";
59
59
  import { TestCaseBoardView } from "./views/board-view";
@@ -62,7 +62,7 @@ import { manualOrderSwitchPatch } from "@/components/shared-kit/list-kit/kanban/
62
62
  import { toast } from "@/components/ui/toast";
63
63
  import { TestCasePeekPanel, type PeekMode } from "./peek-panel";
64
64
  import { useTestcaseDisplayOptions } from "./display/use-testcase-display-options";
65
- import type { TestCaseGroupKey } from "./display/field-configs";
65
+ import type { TestCaseGroupKey } from "@/shared/testcase/field-configs";
66
66
  import { CreateViewDialog, type CreateViewDialogPayload } from "./views/create-view-dialog";
67
67
  import { CreateTestCaseDialog } from "./create-testcase-dialog";
68
68
  import { TestCaseBatchToolbar } from "./views/batch-toolbar";
@@ -26,10 +26,10 @@ import { useCallback, useState } from "react";
26
26
  import { Bug as BugIcon } from "lucide-react";
27
27
  import { Button } from "@/components/ui/button";
28
28
  import { CreateBugDialog, type CreateBugDialogDefaults } from "@/components/bug/create-bug-dialog";
29
- import type { ReproduceSteps } from "@/components/bug/types";
29
+ import type { ReproduceSteps } from "@/shared/bug/types";
30
30
  import { listUsersBriefFn, listLabelsFn } from "@/server/serverFns/story";
31
31
  import { listProjectsBriefFn } from "@/server/serverFns/project";
32
- import type { UserBrief } from "@/components/story/types";
32
+ import type { UserBrief } from "@/shared/story/types";
33
33
  import type {
34
34
  RunCaseExecutionDetail,
35
35
  RunCaseSummary,
@@ -31,6 +31,17 @@ import { m } from "@/paraglide/messages.js";
31
31
  export interface StepDraft {
32
32
  action: string;
33
33
  expected: string;
34
+ /**
35
+ * 行稳定 id(S15):仅本地草稿模式(create dialog / AI draft)用于 React key,
36
+ * 消除中段插入/删除时的跨行状态串位;granular 协同模式不消费(Y 增量仍按
37
+ * 索引映射,id 不落库——提交侧 order 由索引推导,id 自然剥离)。
38
+ */
39
+ readonly id?: string;
40
+ }
41
+
42
+ /** 行稳定 id 生成(本地草稿专用,S15) */
43
+ export function newStepDraftId(): string {
44
+ return crypto.randomUUID();
34
45
  }
35
46
 
36
47
  interface StepEditorProps {
@@ -79,7 +90,7 @@ export function StepEditor({ value, onChange, disabled, className, granular }: S
79
90
  granular.onInsertStep(value.length, { action: "", expected: "" });
80
91
  return;
81
92
  }
82
- onChange([...value, { action: "", expected: "" }]);
93
+ onChange([...value, { id: newStepDraftId(), action: "", expected: "" }]);
83
94
  };
84
95
 
85
96
  const removeStep = (i: number) => {
@@ -128,7 +139,7 @@ export function StepEditor({ value, onChange, disabled, className, granular }: S
128
139
  const showActionError = dirty.has(i) && actionEmpty;
129
140
  return (
130
141
  <li
131
- key={i}
142
+ key={step.id ?? `idx-${i}`}
132
143
  className="flex items-start gap-2"
133
144
  data-testid={`step-row-${i}`}
134
145
  >
@@ -72,11 +72,11 @@ import {
72
72
  import { toast } from "@/components/ui/toast";
73
73
  import { toastError } from "@/lib/toast-error";
74
74
  import type { FolderNodeDto } from "./folder-tree";
75
- import type { UserBrief } from "@/components/story/types";
75
+ import type { UserBrief } from "@/shared/story/types";
76
76
  import { ViewSelectorButton } from "./views/view-selector-button";
77
77
  import { ViewSwitcher } from "./views/view-switcher";
78
78
  import { TestCaseDisplayDropdown } from "./display/display-dropdown";
79
- import type { TestCaseDisplayOptions } from "./display/field-configs";
79
+ import type { TestCaseDisplayOptions } from "@/shared/testcase/field-configs";
80
80
  import type { TestCaseSavedView, TestCaseViewType } from "./views/view-types";
81
81
  import { m } from "@/paraglide/messages.js";
82
82
  import { getLocale } from "@/paraglide/runtime.js";
@@ -37,7 +37,7 @@ import { PriorityEditor } from "../inline-editors/priority-editor";
37
37
  import { AssigneeEditor } from "../inline-editors/assignee-editor";
38
38
  import { FolderEditor } from "../inline-editors/folder-editor";
39
39
  import type { FolderNodeDto } from "../folder-tree";
40
- import type { UserBrief } from "@/components/story/types";
40
+ import type { UserBrief } from "@/shared/story/types";
41
41
  import { toastError } from "@/lib/toast-error";
42
42
  import { cn } from "@/lib/utils";
43
43
  import { m } from "@/paraglide/messages.js";
@@ -44,7 +44,7 @@ import type {
44
44
  TestCaseType,
45
45
  } from "@/server/serverFns/testcase/constants";
46
46
  import type { TestCaseSummary } from "@/server/serverFns/testcase/types";
47
- import type { UserBrief } from "@/components/story/types";
47
+ import type { UserBrief } from "@/shared/story/types";
48
48
  import type { FolderNodeDto } from "../folder-tree";
49
49
  import type { TestCaseUpdatePatch } from "../list/case-row";
50
50
  import { PriorityEditor } from "../inline-editors/priority-editor";
@@ -56,7 +56,7 @@ import {
56
56
  TEST_CASE_TYPE_META,
57
57
  } from "../constants";
58
58
  import { priorityLabel, stateLabel, typeLabel } from "../filters";
59
- import type { TestCaseDisplayProperty } from "../display/field-configs";
59
+ import type { TestCaseDisplayProperty } from "@/shared/testcase/field-configs";
60
60
  import {
61
61
  KanbanBoardView,
62
62
  type ColumnTint,
@@ -96,14 +96,14 @@ export const SDK_GROUPS: SdkGroup[] = [
96
96
  "items": [
97
97
  {
98
98
  "name": "@allbluecn/web-app",
99
- "version": "v0.19.5",
99
+ "version": "v0.21.0",
100
100
  "lastUpdate": "Oct 28, 2026",
101
101
  "source": "apps/web/package.json",
102
102
  "docsUrl": "https://github.com/allbluecn/allblue"
103
103
  },
104
104
  {
105
105
  "name": "@allbluecn/kernel",
106
- "version": "v0.10.1",
106
+ "version": "v0.11.0",
107
107
  "lastUpdate": "Oct 28, 2026",
108
108
  "source": "packages/kernel/package.json",
109
109
  "docsUrl": "https://github.com/allbluecn/allblue"
@@ -20,7 +20,8 @@ import { queryCollectionOptions } from '@tanstack/query-db-collection'
20
20
  import { listKnowledgeFn } from '@/server/serverFns/knowledge'
21
21
  import { getQueryClient } from '@/lib/query-client-seam'
22
22
 
23
- export const KNOWLEDGE_BASES_QUERY_KEY = ['knowledge-bases'] as const
23
+ import { KNOWLEDGE_BASES_QUERY_KEY } from "@/lib/collections/query-keys"
24
+ export { KNOWLEDGE_BASES_QUERY_KEY }
24
25
 
25
26
  export const knowledgeBasesCollection = createCollection(
26
27
  queryCollectionOptions({
@@ -0,0 +1,18 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+
5
+ // React Query key 常量(零依赖纯模块)。
6
+ //
7
+ // 为什么单独成文件:collection 模块在**模块作用域**执行
8
+ // `createCollection({ ... queryClient: getQueryClient() })`,而 getQueryClient()
9
+ // 在 SSR 未注入时 fail-fast(防跨请求缓存串染)。路由文件(如 _nav/route.tsx)
10
+ // 只需要 key 常量做 prefetch,却值引入 collection 模块 → SSR 模块初始化阶段
11
+ // 就触发 getQueryClient(),早于 router.tsx 的 setQueryClient,全站 500。
12
+ // 消费方一律从本模块取 key;collection 模块转 re-export 保持兼容。
13
+ export const WORKSPACES_QUERY_KEY = ['workspaces'] as const
14
+ export const KNOWLEDGE_BASES_QUERY_KEY = ['knowledge-bases'] as const
15
+ export const PROJECTS_QUERY_KEY = ['projects'] as const
16
+ export const LABELS_QUERY_KEY = ['labels'] as const
17
+ export const USERS_BRIEF_QUERY_KEY = ['users-brief'] as const
18
+ export const BOOKMARK_CATEGORIES_QUERY_KEY = ['bookmark-categories'] as const