) {
- return (
-
- )
-}
-
export function ListField({
label,
items,
@@ -140,55 +125,3 @@ export function ListField({
)
}
-
-export const RELATION_TYPE_SUGGESTIONS = [
- '친구',
- '회사 동료',
- '가족',
- '연인',
- '스터디원',
- '이웃',
- '지인',
-] as const
-
-export function RelationTypeField({
- value,
- onChange,
- inset = false,
- hideLabel = false,
-}: {
- value: string
- onChange: (value: string) => void
- inset?: boolean
- hideLabel?: boolean
-}) {
- const chips = (
-
- {RELATION_TYPE_SUGGESTIONS.map((suggestion) => (
- {
- onChange(value === suggestion ? '' : suggestion)
- }}
- >
- {suggestion}
-
- ))}
-
- )
-
- return (
-
- {hideLabel ? null : 만남 태그}
- {inset ? (
- {chips}
- ) : (
- chips
- )}
-
- )
-}
diff --git a/frontend/src/components/person/person-edit-form.tsx b/frontend/src/components/person/person-edit-form.tsx
index 54950bc..53c0e3d 100644
--- a/frontend/src/components/person/person-edit-form.tsx
+++ b/frontend/src/components/person/person-edit-form.tsx
@@ -1,7 +1,8 @@
import { useRef, useState } from 'react'
import type { FormEvent } from 'react'
import { DateWheel } from '@/components/person/date-wheel'
-import { ListField, RelationTypeField } from '@/components/person/person-fields'
+import { ListField } from '@/components/person/list-field'
+import { RelationTypeField } from '@/components/person/relation-type-field'
import {
GENDER_OPTIONS,
ProfileHero,
diff --git a/frontend/src/components/person/person-tab-nav.tsx b/frontend/src/components/person/person-tab-nav.tsx
index 244709c..606f9ec 100644
--- a/frontend/src/components/person/person-tab-nav.tsx
+++ b/frontend/src/components/person/person-tab-nav.tsx
@@ -1,4 +1,4 @@
-import { ListGroupInset } from '@/components/ui/list-group'
+import { ListGroupInset } from '@/components/ui/list-group-inset'
import { SegmentedControl } from '@/components/ui/segmented-control'
import type { PersonView } from '@/stackflow/stackflow.config'
diff --git a/frontend/src/components/person/relation-type-field.stories.tsx b/frontend/src/components/person/relation-type-field.stories.tsx
new file mode 100644
index 0000000..ccb18e7
--- /dev/null
+++ b/frontend/src/components/person/relation-type-field.stories.tsx
@@ -0,0 +1,34 @@
+import { useState } from 'react'
+import type { Meta, StoryObj } from '@storybook/react-vite'
+import { RelationTypeField } from '@/components/person/relation-type-field'
+
+function RelationTypeDemo({ initial = '' }: { initial?: string }) {
+ const [value, setValue] = useState(initial)
+ return (
+
+
+
+ )
+}
+
+const meta = {
+ title: 'Person/RelationTypeField',
+ component: RelationTypeField,
+ tags: ['autodocs'],
+ args: {
+ value: '',
+ onChange: () => {},
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const RelationTypeDefault: Story = {
+ render: () => ,
+}
+
+export const RelationTypeSelected: Story = {
+ render: () => ,
+}
diff --git a/frontend/src/components/person/relation-type-field.tsx b/frontend/src/components/person/relation-type-field.tsx
new file mode 100644
index 0000000..38dee9d
--- /dev/null
+++ b/frontend/src/components/person/relation-type-field.tsx
@@ -0,0 +1,56 @@
+import { FieldLabel } from '@/components/person/field-label'
+import { ListGroupInset } from '@/components/ui/list-group-inset'
+import { TagChip } from '@/components/ui/tag-chip'
+import { cn } from '@/lib/utils'
+
+export const RELATION_TYPE_SUGGESTIONS = [
+ '친구',
+ '회사 동료',
+ '가족',
+ '연인',
+ '스터디원',
+ '이웃',
+ '지인',
+] as const
+
+export function RelationTypeField({
+ value,
+ onChange,
+ inset = false,
+ hideLabel = false,
+}: {
+ value: string
+ onChange: (value: string) => void
+ inset?: boolean
+ hideLabel?: boolean
+}) {
+ const chips = (
+
+ {RELATION_TYPE_SUGGESTIONS.map((suggestion) => (
+ {
+ onChange(value === suggestion ? '' : suggestion)
+ }}
+ >
+ {suggestion}
+
+ ))}
+
+ )
+
+ return (
+
+ {hideLabel ? null : 만남 태그}
+ {inset ? (
+ {chips}
+ ) : (
+ chips
+ )}
+
+ )
+}
diff --git a/frontend/src/components/settings/settings-navigation-item.tsx b/frontend/src/components/settings/settings-navigation-item.tsx
index feacf4a..438b9c0 100644
--- a/frontend/src/components/settings/settings-navigation-item.tsx
+++ b/frontend/src/components/settings/settings-navigation-item.tsx
@@ -1,5 +1,5 @@
import { ChevronRight } from 'lucide-react'
-import { ListGroupItem } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
export function SettingsNavigationItem({
label,
diff --git a/frontend/src/components/timeline/timeline-filters.stories.tsx b/frontend/src/components/timeline/timeline-category-filters.stories.tsx
similarity index 61%
rename from frontend/src/components/timeline/timeline-filters.stories.tsx
rename to frontend/src/components/timeline/timeline-category-filters.stories.tsx
index 44df921..b6a3dbb 100644
--- a/frontend/src/components/timeline/timeline-filters.stories.tsx
+++ b/frontend/src/components/timeline/timeline-category-filters.stories.tsx
@@ -1,14 +1,7 @@
import { useState } from 'react'
import type { Meta, StoryObj } from '@storybook/react-vite'
-import type {
- ChipResponse,
- PersonResponse,
-} from '@/apis/generated/mongle-api.schemas'
-import {
- TimelineCategoryFilters,
- TimelineFilterReset,
- TimelinePersonFilters,
-} from '@/components/timeline/timeline-filters'
+import type { ChipResponse } from '@/apis/generated/mongle-api.schemas'
+import { TimelineCategoryFilters } from '@/components/timeline/timeline-category-filters'
const CATEGORY_CHIPS: ChipResponse[] = [
{
@@ -45,42 +38,11 @@ const CATEGORY_CHIPS: ChipResponse[] = [
},
]
-const PERSONS: PersonResponse[] = [
- {
- id: 1,
- name: '엄마',
- gender: 'FEMALE',
- favorite: true,
- relationTags: [],
- likes: [],
- cautions: [],
- },
- {
- id: 2,
- name: '김민수',
- gender: 'MALE',
- profileImageUrl: 'https://picsum.photos/seed/minsu/200',
- favorite: false,
- relationTags: [],
- likes: [],
- cautions: [],
- },
- {
- id: 3,
- name: '이지은',
- gender: 'FEMALE',
- favorite: false,
- relationTags: [],
- likes: [],
- cautions: [],
- },
-]
-
const toggle = (ids: number[], id: number) =>
ids.includes(id) ? ids.filter((x) => x !== id) : [...ids, id]
const meta = {
- title: 'Timeline/Filters',
+ title: 'Timeline/CategoryFilters',
component: TimelineCategoryFilters,
tags: ['autodocs'],
// 각 스토리는 render로 상태를 직접 관리한다. 여기 args는 필수 prop 타입만 채운다.
@@ -135,24 +97,3 @@ export const AllSelected: Story = {
)
},
}
-
-export const Persons: Story = {
- render: () => {
- const [selected, setSelected] = useState([1])
- return (
- setSelected((prev) => toggle(prev, id))}
- />
- )
- },
-}
-
-export const ResetVisible: Story = {
- render: () => {}} />,
-}
-
-export const ResetHidden: Story = {
- render: () => {}} />,
-}
diff --git a/frontend/src/components/timeline/timeline-category-filters.tsx b/frontend/src/components/timeline/timeline-category-filters.tsx
new file mode 100644
index 0000000..fde1eca
--- /dev/null
+++ b/frontend/src/components/timeline/timeline-category-filters.tsx
@@ -0,0 +1,39 @@
+import type { ChipResponse } from '@/apis/generated/mongle-api.schemas'
+import { TagChip } from '@/components/ui/tag-chip'
+
+export function TimelineCategoryFilters({
+ chips,
+ selectedIds,
+ onToggle,
+}: {
+ chips: ChipResponse[]
+ selectedIds: number[]
+ onToggle: (chipId: number) => void
+}) {
+ if (chips.length === 0) return null
+
+ return (
+
+
+ 카테고리
+
+
+ {chips.map((chip) => {
+ const selected = selectedIds.includes(chip.id)
+ return (
+ onToggle(chip.id)}
+ data-amp-mask={chip.personal || undefined}
+ >
+ {chip.label}
+
+ )
+ })}
+
+
+ )
+}
diff --git a/frontend/src/components/timeline/timeline-filter-drawer.tsx b/frontend/src/components/timeline/timeline-filter-drawer.tsx
index dc8ee2f..e0c50ba 100644
--- a/frontend/src/components/timeline/timeline-filter-drawer.tsx
+++ b/frontend/src/components/timeline/timeline-filter-drawer.tsx
@@ -3,11 +3,9 @@ import type {
ChipResponse,
PersonResponse,
} from '@/apis/generated/mongle-api.schemas'
-import {
- TimelineCategoryFilters,
- TimelineFilterReset,
- TimelinePersonFilters,
-} from '@/components/timeline/timeline-filters'
+import { TimelineCategoryFilters } from '@/components/timeline/timeline-category-filters'
+import { TimelineFilterReset } from '@/components/timeline/timeline-filter-reset'
+import { TimelinePersonFilters } from '@/components/timeline/timeline-person-filters'
import { Button } from '@/components/ui/button'
import {
Drawer,
diff --git a/frontend/src/components/timeline/timeline-filter-reset.stories.tsx b/frontend/src/components/timeline/timeline-filter-reset.stories.tsx
new file mode 100644
index 0000000..1e72708
--- /dev/null
+++ b/frontend/src/components/timeline/timeline-filter-reset.stories.tsx
@@ -0,0 +1,24 @@
+import type { Meta, StoryObj } from '@storybook/react-vite'
+import { TimelineFilterReset } from '@/components/timeline/timeline-filter-reset'
+
+const meta = {
+ title: 'Timeline/FilterReset',
+ component: TimelineFilterReset,
+ tags: ['autodocs'],
+ args: {
+ visible: true,
+ onReset: () => {},
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const ResetVisible: Story = {
+ render: () => {}} />,
+}
+
+export const ResetHidden: Story = {
+ render: () => {}} />,
+}
diff --git a/frontend/src/components/timeline/timeline-filter-reset.tsx b/frontend/src/components/timeline/timeline-filter-reset.tsx
new file mode 100644
index 0000000..6cfbf8d
--- /dev/null
+++ b/frontend/src/components/timeline/timeline-filter-reset.tsx
@@ -0,0 +1,19 @@
+export function TimelineFilterReset({
+ visible,
+ onReset,
+}: {
+ visible: boolean
+ onReset: () => void
+}) {
+ if (!visible) return null
+
+ return (
+
+ )
+}
diff --git a/frontend/src/components/timeline/timeline-filters.test.tsx b/frontend/src/components/timeline/timeline-filters.test.tsx
index ed94194..564fc1f 100644
--- a/frontend/src/components/timeline/timeline-filters.test.tsx
+++ b/frontend/src/components/timeline/timeline-filters.test.tsx
@@ -1,9 +1,7 @@
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'
-import {
- TimelineCategoryFilters,
- TimelinePersonFilters,
-} from './timeline-filters'
+import { TimelineCategoryFilters } from './timeline-category-filters'
+import { TimelinePersonFilters } from './timeline-person-filters'
import type {
ChipResponse,
PersonResponse,
diff --git a/frontend/src/components/timeline/timeline-person-filters.stories.tsx b/frontend/src/components/timeline/timeline-person-filters.stories.tsx
new file mode 100644
index 0000000..28bcc77
--- /dev/null
+++ b/frontend/src/components/timeline/timeline-person-filters.stories.tsx
@@ -0,0 +1,67 @@
+import { useState } from 'react'
+import type { Meta, StoryObj } from '@storybook/react-vite'
+import type { PersonResponse } from '@/apis/generated/mongle-api.schemas'
+import { TimelinePersonFilters } from '@/components/timeline/timeline-person-filters'
+
+const PERSONS: PersonResponse[] = [
+ {
+ id: 1,
+ name: '엄마',
+ gender: 'FEMALE',
+ favorite: true,
+ relationTags: [],
+ likes: [],
+ cautions: [],
+ },
+ {
+ id: 2,
+ name: '김민수',
+ gender: 'MALE',
+ profileImageUrl: 'https://picsum.photos/seed/minsu/200',
+ favorite: false,
+ relationTags: [],
+ likes: [],
+ cautions: [],
+ },
+ {
+ id: 3,
+ name: '이지은',
+ gender: 'FEMALE',
+ favorite: false,
+ relationTags: [],
+ likes: [],
+ cautions: [],
+ },
+]
+
+const toggle = (ids: number[], id: number) =>
+ ids.includes(id) ? ids.filter((x) => x !== id) : [...ids, id]
+
+const meta = {
+ title: 'Timeline/PersonFilters',
+ component: TimelinePersonFilters,
+ tags: ['autodocs'],
+ // 각 스토리는 render로 상태를 직접 관리한다. 여기 args는 필수 prop 타입만 채운다.
+ args: {
+ persons: PERSONS,
+ selectedIds: [],
+ onToggle: () => {},
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Default: Story = {
+ render: () => {
+ const [selected, setSelected] = useState([1])
+ return (
+ setSelected((prev) => toggle(prev, id))}
+ />
+ )
+ },
+}
diff --git a/frontend/src/components/timeline/timeline-filters.tsx b/frontend/src/components/timeline/timeline-person-filters.tsx
similarity index 50%
rename from frontend/src/components/timeline/timeline-filters.tsx
rename to frontend/src/components/timeline/timeline-person-filters.tsx
index 006ec28..a98cbe1 100644
--- a/frontend/src/components/timeline/timeline-filters.tsx
+++ b/frontend/src/components/timeline/timeline-person-filters.tsx
@@ -1,49 +1,9 @@
-import type {
- ChipResponse,
- PersonResponse,
-} from '@/apis/generated/mongle-api.schemas'
+import type { PersonResponse } from '@/apis/generated/mongle-api.schemas'
import { MonogramAvatar } from '@/components/ui/monogram-avatar'
import { TagChip } from '@/components/ui/tag-chip'
import { formatPersonName } from '@/lib/format'
import { cn } from '@/lib/utils'
-export function TimelineCategoryFilters({
- chips,
- selectedIds,
- onToggle,
-}: {
- chips: ChipResponse[]
- selectedIds: number[]
- onToggle: (chipId: number) => void
-}) {
- if (chips.length === 0) return null
-
- return (
-
-
- 카테고리
-
-
- {chips.map((chip) => {
- const selected = selectedIds.includes(chip.id)
- return (
- onToggle(chip.id)}
- data-amp-mask={chip.personal || undefined}
- >
- {chip.label}
-
- )
- })}
-
-
- )
-}
-
export function TimelinePersonFilters({
persons,
selectedIds,
@@ -88,23 +48,3 @@ export function TimelinePersonFilters({
)
}
-
-export function TimelineFilterReset({
- visible,
- onReset,
-}: {
- visible: boolean
- onReset: () => void
-}) {
- if (!visible) return null
-
- return (
-
- )
-}
diff --git a/frontend/src/components/ui/list-group-footer.stories.tsx b/frontend/src/components/ui/list-group-footer.stories.tsx
new file mode 100644
index 0000000..f26cda7
--- /dev/null
+++ b/frontend/src/components/ui/list-group-footer.stories.tsx
@@ -0,0 +1,34 @@
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupFooter } from '@/components/ui/list-group-footer'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import type { Meta, StoryObj } from '@storybook/react-vite'
+
+const meta = {
+ title: 'UI/ListGroupFooter',
+ component: ListGroupFooter,
+ tags: ['autodocs'],
+ args: {
+ children: '자주 연락하는 사람일수록 위쪽에 정렬됩니다.',
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Default: Story = {}
+
+export const BelowGroup: Story = {
+ render: () => (
+
+
+ 엄마
+ 아빠
+ 동생
+
+
+ 자주 연락하는 사람일수록 위쪽에 정렬됩니다.
+
+
+ ),
+}
diff --git a/frontend/src/components/ui/list-group-footer.tsx b/frontend/src/components/ui/list-group-footer.tsx
new file mode 100644
index 0000000..c19d91f
--- /dev/null
+++ b/frontend/src/components/ui/list-group-footer.tsx
@@ -0,0 +1,7 @@
+export function ListGroupFooter({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/frontend/src/components/ui/list-group-inset.stories.tsx b/frontend/src/components/ui/list-group-inset.stories.tsx
new file mode 100644
index 0000000..3a11721
--- /dev/null
+++ b/frontend/src/components/ui/list-group-inset.stories.tsx
@@ -0,0 +1,37 @@
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupInset } from '@/components/ui/list-group-inset'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import type { Meta, StoryObj } from '@storybook/react-vite'
+
+const meta = {
+ title: 'UI/ListGroupInset',
+ component: ListGroupInset,
+ tags: ['autodocs'],
+ args: {
+ children: (
+
+ 오랜만에 만났다. 요즘 이직 준비 중이라고 했다.
+
+ ),
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Default: Story = {}
+
+export const InListGroup: Story = {
+ render: () => (
+
+
+
+
+ 오랜만에 만났다. 요즘 이직 준비 중이라고 했다.
+
+
+
+
+ ),
+}
diff --git a/frontend/src/components/ui/list-group-inset.tsx b/frontend/src/components/ui/list-group-inset.tsx
new file mode 100644
index 0000000..dadcddf
--- /dev/null
+++ b/frontend/src/components/ui/list-group-inset.tsx
@@ -0,0 +1,20 @@
+import { cn } from '@/lib/utils'
+
+export function ListGroupInset({
+ children,
+ className,
+}: {
+ children: React.ReactNode
+ className?: string
+}) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/frontend/src/components/ui/list-group-item.stories.tsx b/frontend/src/components/ui/list-group-item.stories.tsx
new file mode 100644
index 0000000..b152f33
--- /dev/null
+++ b/frontend/src/components/ui/list-group-item.stories.tsx
@@ -0,0 +1,36 @@
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import type { Meta, StoryObj } from '@storybook/react-vite'
+
+const meta = {
+ title: 'UI/ListGroupItem',
+ component: ListGroupItem,
+ tags: ['autodocs'],
+ args: {
+ children: '엄마',
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Default: Story = {
+ render: () => (
+
+ 엄마
+ 김민수
+ 이서연
+
+ ),
+}
+
+export const WithoutDivider: Story = {
+ render: () => (
+
+
+ 구분선이 없는 마지막 항목
+
+
+ ),
+}
diff --git a/frontend/src/components/ui/list-group-item.tsx b/frontend/src/components/ui/list-group-item.tsx
new file mode 100644
index 0000000..758bd40
--- /dev/null
+++ b/frontend/src/components/ui/list-group-item.tsx
@@ -0,0 +1,23 @@
+import { cn } from '@/lib/utils'
+
+export function ListGroupItem({
+ children,
+ className,
+ withDivider = true,
+}: {
+ children: React.ReactNode
+ className?: string
+ withDivider?: boolean
+}) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/frontend/src/components/ui/list-group-label.stories.tsx b/frontend/src/components/ui/list-group-label.stories.tsx
new file mode 100644
index 0000000..544730a
--- /dev/null
+++ b/frontend/src/components/ui/list-group-label.stories.tsx
@@ -0,0 +1,32 @@
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import { ListGroupLabel } from '@/components/ui/list-group-label'
+import type { Meta, StoryObj } from '@storybook/react-vite'
+
+const meta = {
+ title: 'UI/ListGroupLabel',
+ component: ListGroupLabel,
+ tags: ['autodocs'],
+ args: {
+ children: '가족',
+ },
+} satisfies Meta
+
+export default meta
+
+type Story = StoryObj
+
+export const Default: Story = {}
+
+export const AboveGroup: Story = {
+ render: () => (
+
+ 가족
+
+ 엄마
+ 아빠
+ 동생
+
+
+ ),
+}
diff --git a/frontend/src/components/ui/list-group-label.tsx b/frontend/src/components/ui/list-group-label.tsx
new file mode 100644
index 0000000..98eb685
--- /dev/null
+++ b/frontend/src/components/ui/list-group-label.tsx
@@ -0,0 +1,7 @@
+export function ListGroupLabel({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/frontend/src/components/ui/list-group.stories.tsx b/frontend/src/components/ui/list-group.stories.tsx
index 685e99f..df61b95 100644
--- a/frontend/src/components/ui/list-group.stories.tsx
+++ b/frontend/src/components/ui/list-group.stories.tsx
@@ -1,10 +1,5 @@
-import {
- ListGroup,
- ListGroupFooter,
- ListGroupInset,
- ListGroupItem,
- ListGroupLabel,
-} from '@/components/ui/list-group'
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
import type { Meta, StoryObj } from '@storybook/react-vite'
const meta = {
@@ -29,33 +24,3 @@ export const Default: Story = {
),
}
-
-export const WithLabelAndFooter: Story = {
- render: () => (
-
- 가족
-
- 엄마
- 아빠
- 동생
-
-
- 자주 연락하는 사람일수록 위쪽에 정렬됩니다.
-
-
- ),
-}
-
-export const Inset: Story = {
- render: () => (
-
-
-
-
- 오랜만에 만났다. 요즘 이직 준비 중이라고 했다.
-
-
-
-
- ),
-}
diff --git a/frontend/src/components/ui/list-group.tsx b/frontend/src/components/ui/list-group.tsx
index c969380..50acf36 100644
--- a/frontend/src/components/ui/list-group.tsx
+++ b/frontend/src/components/ui/list-group.tsx
@@ -1,13 +1,5 @@
import { cn } from '@/lib/utils'
-export function ListGroupLabel({ children }: { children: React.ReactNode }) {
- return (
-
- {children}
-
- )
-}
-
export function ListGroup({
children,
className,
@@ -26,52 +18,3 @@ export function ListGroup({
)
}
-
-export function ListGroupItem({
- children,
- className,
- withDivider = true,
-}: {
- children: React.ReactNode
- className?: string
- withDivider?: boolean
-}) {
- return (
-
- {children}
-
- )
-}
-
-export function ListGroupFooter({ children }: { children: React.ReactNode }) {
- return (
-
- {children}
-
- )
-}
-
-export function ListGroupInset({
- children,
- className,
-}: {
- children: React.ReactNode
- className?: string
-}) {
- return (
-
- {children}
-
- )
-}
diff --git a/frontend/src/stackflow/activities/person-new-activity.tsx b/frontend/src/stackflow/activities/person-new-activity.tsx
index 75e1511..9508ad0 100644
--- a/frontend/src/stackflow/activities/person-new-activity.tsx
+++ b/frontend/src/stackflow/activities/person-new-activity.tsx
@@ -6,7 +6,8 @@ import { AnimatePresence, motion, useReducedMotion } from 'motion/react'
import { ChevronLeft, Save } from 'lucide-react'
import { useRef, useState } from 'react'
import { DateWheel } from '@/components/person/date-wheel'
-import { ListField, RelationTypeField } from '@/components/person/person-fields'
+import { ListField } from '@/components/person/list-field'
+import { RelationTypeField } from '@/components/person/relation-type-field'
import {
GENDER_OPTIONS,
ProfileHero,
diff --git a/frontend/src/stackflow/activities/person/person-profile-view.tsx b/frontend/src/stackflow/activities/person/person-profile-view.tsx
index db87d17..5ce9ac1 100644
--- a/frontend/src/stackflow/activities/person/person-profile-view.tsx
+++ b/frontend/src/stackflow/activities/person/person-profile-view.tsx
@@ -6,11 +6,9 @@ import { eventQuery, personQuery } from '@/apis/queries'
import { DeletePersonConfirm } from '@/components/person/delete-person-confirm'
import { usePersonDelete } from '@/components/person/use-person-delete'
import { MonogramAvatar } from '@/components/ui/monogram-avatar'
-import {
- ListGroup,
- ListGroupItem,
- ListGroupLabel,
-} from '@/components/ui/list-group'
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import { ListGroupLabel } from '@/components/ui/list-group-label'
import { StatusMessage } from '@/components/ui/status-message'
import { TagChip } from '@/components/ui/tag-chip'
import { optimizedImageUrl } from '@/lib/image-url'
diff --git a/frontend/src/stackflow/activities/person/person-timeline-view.tsx b/frontend/src/stackflow/activities/person/person-timeline-view.tsx
index ffa2076..ed5f61e 100644
--- a/frontend/src/stackflow/activities/person/person-timeline-view.tsx
+++ b/frontend/src/stackflow/activities/person/person-timeline-view.tsx
@@ -2,10 +2,8 @@ import { useQuery } from '@tanstack/react-query'
import { useFlow } from '@stackflow/react'
import { useMemo, useRef, useState } from 'react'
import { ActivityFlowChart } from '@/components/timeline/activity-flow-chart'
-import {
- TimelineCategoryFilters,
- TimelineFilterReset,
-} from '@/components/timeline/timeline-filters'
+import { TimelineCategoryFilters } from '@/components/timeline/timeline-category-filters'
+import { TimelineFilterReset } from '@/components/timeline/timeline-filter-reset'
import {
fromEventResponse,
TimelineEventCard,
@@ -20,7 +18,8 @@ import {
EmptyStateAction,
EmptyStateDescription,
} from '@/components/ui/empty-state'
-import { ListGroup, ListGroupItem } from '@/components/ui/list-group'
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
import { StatusMessage } from '@/components/ui/status-message'
import { chipQuery, eventQuery, personQuery } from '@/apis/queries'
import { formatPersonName } from '@/lib/format'
diff --git a/frontend/src/stackflow/activities/settings/tag-type-panel.tsx b/frontend/src/stackflow/activities/settings/tag-type-panel.tsx
index d56b1f2..4498351 100644
--- a/frontend/src/stackflow/activities/settings/tag-type-panel.tsx
+++ b/frontend/src/stackflow/activities/settings/tag-type-panel.tsx
@@ -4,7 +4,7 @@ import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { ConfirmPopup } from '@/components/ui/confirm-popup'
import { Input } from '@/components/ui/input'
-import { ListGroupInset } from '@/components/ui/list-group'
+import { ListGroupInset } from '@/components/ui/list-group-inset'
import type {
ChipResponse,
ChipResponseType,
diff --git a/frontend/src/stackflow/tabs/people-tab.tsx b/frontend/src/stackflow/tabs/people-tab.tsx
index 5465ec8..a00757f 100644
--- a/frontend/src/stackflow/tabs/people-tab.tsx
+++ b/frontend/src/stackflow/tabs/people-tab.tsx
@@ -20,12 +20,10 @@ import { Input } from '@/components/ui/input'
import { SegmentedControl } from '@/components/ui/segmented-control'
import { StatusMessage } from '@/components/ui/status-message'
import { TagChip } from '@/components/ui/tag-chip'
-import {
- ListGroup,
- ListGroupInset,
- ListGroupItem,
- ListGroupLabel,
-} from '@/components/ui/list-group'
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupInset } from '@/components/ui/list-group-inset'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import { ListGroupLabel } from '@/components/ui/list-group-label'
import { formatLastMetRelative, formatPersonName } from '@/lib/format'
import { cn } from '@/lib/utils'
diff --git a/frontend/src/stackflow/tabs/settings-tab.tsx b/frontend/src/stackflow/tabs/settings-tab.tsx
index b588a1c..6ef7fbd 100644
--- a/frontend/src/stackflow/tabs/settings-tab.tsx
+++ b/frontend/src/stackflow/tabs/settings-tab.tsx
@@ -8,11 +8,9 @@ import { useTheme } from '@/components/theme-provider'
import { Button } from '@/components/ui/button'
import { ConfirmPopup } from '@/components/ui/confirm-popup'
import { PageTitle } from '@/components/ui/page-title'
-import {
- ListGroup,
- ListGroupItem,
- ListGroupLabel,
-} from '@/components/ui/list-group'
+import { ListGroup } from '@/components/ui/list-group'
+import { ListGroupItem } from '@/components/ui/list-group-item'
+import { ListGroupLabel } from '@/components/ui/list-group-label'
import { Switch } from '@/components/ui/switch'
import { userMutation } from '@/apis/mutations'
import { featureEvents, resetAnalytics, trackFeature } from '@/lib/analytics'