@interactive-inc/docs-client 0.0.1 → 0.0.3
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/bin/docs.js +3 -0
- package/build/assets/index.css +1 -1
- package/build/assets/index.js +53 -53
- package/build/index.html +13 -0
- package/build/index.js +24057 -16
- package/package.json +13 -6
- package/.tanstack/tmp/router-generator-Eq20Yx/50b11a0f488e61cf92364a7e585c1448 +0 -123
- package/.tanstack/tmp/router-generator-KQIYSa/aecb094e4891f9475b91de590abc8809 +0 -117
- package/.tanstack/tmp/router-generator-ekW2pI/aecb094e4891f9475b91de590abc8809 +0 -117
- package/app/components/archived-file-list-view.tsx +0 -99
- package/app/components/directory-file-list-view.tsx +0 -32
- package/app/components/directory-file-tree-node.tsx +0 -96
- package/app/components/directory-layout-sidebar.tsx +0 -181
- package/app/components/directory-page-view.tsx +0 -208
- package/app/components/directory-table-view.tsx +0 -383
- package/app/components/editable-table-cell/boolean-editable-cell.tsx +0 -44
- package/app/components/editable-table-cell/multi-boolean-editable-cell.tsx +0 -59
- package/app/components/editable-table-cell/multi-number-editable-cell.tsx +0 -62
- package/app/components/editable-table-cell/multi-relation-editable-cell.tsx +0 -19
- package/app/components/editable-table-cell/multi-select-number-editable-cell.tsx +0 -82
- package/app/components/editable-table-cell/multi-select-text-editable-cell.tsx +0 -82
- package/app/components/editable-table-cell/multi-text-editable-cell.tsx +0 -62
- package/app/components/editable-table-cell/number-editable-cell.tsx +0 -54
- package/app/components/editable-table-cell/relation-editable-cell.tsx +0 -20
- package/app/components/editable-table-cell/select-number-editable-cell.tsx +0 -43
- package/app/components/editable-table-cell/select-text-editable-cell.tsx +0 -33
- package/app/components/editable-table-cell/string-editable-cell.tsx +0 -42
- package/app/components/editable-table-cell.tsx +0 -129
- package/app/components/file-card.tsx +0 -120
- package/app/components/file-tree-icon.tsx +0 -26
- package/app/components/file-view/archive-restore-banner.tsx +0 -86
- package/app/components/file-view/csv-file-view.tsx +0 -50
- package/app/components/file-view/default-file-view.tsx +0 -11
- package/app/components/file-view/editable-csv-cell.tsx +0 -37
- package/app/components/file-view/editable-front-matter-view.tsx +0 -105
- package/app/components/file-view/file-csv-columns-view.tsx +0 -181
- package/app/components/file-view/file-csv-table.tsx +0 -142
- package/app/components/file-view/file-header.tsx +0 -115
- package/app/components/file-view/file-page-view.tsx +0 -165
- package/app/components/file-view/front-matter-input-field.tsx +0 -157
- package/app/components/file-view/front-matter-view.tsx +0 -30
- package/app/components/file-view/json-file-editor.tsx +0 -38
- package/app/components/file-view/markdown-file-view.tsx +0 -71
- package/app/components/file-view/multi-relation-select.tsx +0 -129
- package/app/components/file-view/render-front-matter-value.tsx +0 -57
- package/app/components/file-view/single-relation-select.tsx +0 -41
- package/app/components/file-view/sortable-csv-row.tsx +0 -57
- package/app/components/file-view/sortable-header.tsx +0 -30
- package/app/components/file-view/utils.ts +0 -8
- package/app/components/milestones-editor.tsx +0 -95
- package/app/components/page-header.tsx +0 -20
- package/app/components/project-view/feature-item.tsx +0 -139
- package/app/components/project-view/project-page-group.tsx +0 -125
- package/app/components/project-view/project-view.tsx +0 -230
- package/app/components/project-view/unlinked-features-section.tsx +0 -73
- package/app/components/providers.tsx +0 -21
- package/app/components/reload-button.tsx +0 -25
- package/app/components/sidebar-button.tsx +0 -13
- package/app/components/ui/alert.tsx +0 -66
- package/app/components/ui/badge.tsx +0 -46
- package/app/components/ui/button.tsx +0 -59
- package/app/components/ui/card.tsx +0 -92
- package/app/components/ui/dialog.tsx +0 -133
- package/app/components/ui/emoji-picker.tsx +0 -133
- package/app/components/ui/icons.tsx +0 -3
- package/app/components/ui/input.tsx +0 -21
- package/app/components/ui/select.tsx +0 -183
- package/app/components/ui/separator.tsx +0 -26
- package/app/components/ui/sheet.tsx +0 -137
- package/app/components/ui/sidebar.tsx +0 -724
- package/app/components/ui/simple-sidebar.tsx +0 -50
- package/app/components/ui/skeleton.tsx +0 -13
- package/app/components/ui/sonner.tsx +0 -23
- package/app/components/ui/table.tsx +0 -114
- package/app/components/ui/tabs.tsx +0 -53
- package/app/components/ui/textarea.tsx +0 -18
- package/app/components/ui/tooltip.tsx +0 -59
- package/app/components/vscode-button.tsx +0 -23
- package/app/hooks/use-directory-query.ts +0 -22
- package/app/hooks/use-file-properties-mutation.ts +0 -38
- package/app/hooks/use-file-query.ts +0 -21
- package/app/hooks/use-mobile.ts +0 -19
- package/app/main.css +0 -122
- package/app/main.tsx +0 -38
- package/app/routes/$.index.tsx +0 -25
- package/app/routes/$.tsx +0 -20
- package/app/routes/__root.tsx +0 -20
- package/app/routes/_index.tsx +0 -17
- package/app/routes/apps.client.$.tsx +0 -21
- package/build/index.d.ts +0 -2
- package/build/index.d.ts.map +0 -1
- package/components.json +0 -21
- package/tsconfig.index.json +0 -17
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { Card } from "@/components/ui/card"
|
|
2
|
-
import {
|
|
3
|
-
Select,
|
|
4
|
-
SelectContent,
|
|
5
|
-
SelectItem,
|
|
6
|
-
SelectTrigger,
|
|
7
|
-
SelectValue,
|
|
8
|
-
} from "@/components/ui/select"
|
|
9
|
-
import type { DocFile, DocFileMd, DocRelationFile } from "@/lib/types"
|
|
10
|
-
import { FeatureItem } from "./feature-item"
|
|
11
|
-
|
|
12
|
-
function isDocFileMd(file: DocFile): file is DocFileMd {
|
|
13
|
-
return file.type === 'markdown'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
type Priority = "high" | "medium" | "low"
|
|
17
|
-
|
|
18
|
-
type PageGroup = {
|
|
19
|
-
page: DocFile
|
|
20
|
-
features: DocFile[]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
type Props = {
|
|
24
|
-
group: PageGroup
|
|
25
|
-
milestoneOptions?: DocRelationFile[]
|
|
26
|
-
onMilestoneUpdate?: (featurePath: string, milestone: string) => void
|
|
27
|
-
onPropertyUpdate?: (
|
|
28
|
-
featurePath: string,
|
|
29
|
-
field: string,
|
|
30
|
-
value: unknown,
|
|
31
|
-
) => void
|
|
32
|
-
allFeatures?: DocFile[]
|
|
33
|
-
onFeatureAdd?: (pagePath: string, featurePath: string) => void
|
|
34
|
-
onFeatureRemove?: (pagePath: string, featurePath: string) => void
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function ProjectPageGroup(props: Props) {
|
|
38
|
-
const sortedFeatures = props.group.features
|
|
39
|
-
.filter(isDocFileMd)
|
|
40
|
-
.sort((a, b) => {
|
|
41
|
-
const priorityOrder: Record<Priority, number> = {
|
|
42
|
-
high: 0,
|
|
43
|
-
medium: 1,
|
|
44
|
-
low: 2,
|
|
45
|
-
}
|
|
46
|
-
const aPriority =
|
|
47
|
-
((a.content.frontMatter as Record<string, unknown>)?.priority as Priority) ||
|
|
48
|
-
"low"
|
|
49
|
-
const bPriority =
|
|
50
|
-
((b.content.frontMatter as Record<string, unknown>)?.priority as Priority) ||
|
|
51
|
-
"low"
|
|
52
|
-
return (priorityOrder[aPriority] || 2) - (priorityOrder[bPriority] || 2)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
// このページに関連付けられていない機能を取得
|
|
56
|
-
const linkedFeaturePaths = props.group.features.map((f) => f.path.path)
|
|
57
|
-
const availableFeatures = (props.allFeatures || [])
|
|
58
|
-
.filter(isDocFileMd)
|
|
59
|
-
.filter((feature) => !linkedFeaturePaths.includes(feature.path.path))
|
|
60
|
-
|
|
61
|
-
const handleFeatureAdd = (featureValue: string) => {
|
|
62
|
-
if (featureValue && props.onFeatureAdd) {
|
|
63
|
-
props.onFeatureAdd(props.group.page.path.path, featureValue)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<Card className="w-full rounded-md p-2">
|
|
69
|
-
<div className="flex flex-col gap-2 lg:flex-row">
|
|
70
|
-
{/* 左側: ページ情報 */}
|
|
71
|
-
<div className="space-y-2 lg:w-1/3">
|
|
72
|
-
<h2 className="font-semibold text-lg">
|
|
73
|
-
{isDocFileMd(props.group.page) ? props.group.page.content.title || props.group.page.path.name : props.group.page.path.name}
|
|
74
|
-
</h2>
|
|
75
|
-
<div className="text-xs opacity-50">{props.group.page.path.name}</div>
|
|
76
|
-
{isDocFileMd(props.group.page) && props.group.page.content.description && (
|
|
77
|
-
<p className="text-sm opacity-50">{props.group.page.content.description}</p>
|
|
78
|
-
)}
|
|
79
|
-
</div>
|
|
80
|
-
{/* 右側: 関連機能一覧 */}
|
|
81
|
-
<div className="flex-1">
|
|
82
|
-
<div className="space-y-2">
|
|
83
|
-
{sortedFeatures.length > 0 ? (
|
|
84
|
-
sortedFeatures.map((feature) => (
|
|
85
|
-
<FeatureItem
|
|
86
|
-
key={feature.path.name}
|
|
87
|
-
feature={feature}
|
|
88
|
-
milestoneOptions={props.milestoneOptions}
|
|
89
|
-
onMilestoneUpdate={props.onMilestoneUpdate}
|
|
90
|
-
onPropertyUpdate={props.onPropertyUpdate}
|
|
91
|
-
onFeatureRemove={(featurePath) =>
|
|
92
|
-
props.onFeatureRemove?.(
|
|
93
|
-
props.group.page.path.path,
|
|
94
|
-
featurePath,
|
|
95
|
-
)
|
|
96
|
-
}
|
|
97
|
-
/>
|
|
98
|
-
))
|
|
99
|
-
) : (
|
|
100
|
-
<p className="text-sm italic opacity-50">
|
|
101
|
-
このページに関連する機能はありません
|
|
102
|
-
</p>
|
|
103
|
-
)}
|
|
104
|
-
|
|
105
|
-
{/* 機能追加用Select */}
|
|
106
|
-
{availableFeatures.length > 0 && (
|
|
107
|
-
<Select value="" onValueChange={handleFeatureAdd}>
|
|
108
|
-
<SelectTrigger className="w-full">
|
|
109
|
-
<SelectValue placeholder="機能を追加..." />
|
|
110
|
-
</SelectTrigger>
|
|
111
|
-
<SelectContent>
|
|
112
|
-
{availableFeatures.map((feature) => (
|
|
113
|
-
<SelectItem key={feature.path.path} value={feature.path.path || ""}>
|
|
114
|
-
{feature.content.title || feature.path.name}
|
|
115
|
-
</SelectItem>
|
|
116
|
-
))}
|
|
117
|
-
</SelectContent>
|
|
118
|
-
</Select>
|
|
119
|
-
)}
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</Card>
|
|
124
|
-
)
|
|
125
|
-
}
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import { useSuspenseQuery } from "@tanstack/react-query"
|
|
2
|
-
import { useState } from "react"
|
|
3
|
-
import { Button } from "@/components/ui/button"
|
|
4
|
-
import { useFilePropertiesMutation } from "@/hooks/use-file-properties-mutation"
|
|
5
|
-
import { apiClient } from "@/lib/api-client"
|
|
6
|
-
import type { DocDirectory, DocFile, DocFileMd, DocRelation, DocRelationFile } from "@/lib/types"
|
|
7
|
-
import { ProjectPageGroup } from "./project-page-group"
|
|
8
|
-
import { UnlinkedFeaturesSection } from "./unlinked-features-section"
|
|
9
|
-
|
|
10
|
-
type Props = {
|
|
11
|
-
project: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
type PageGroup = {
|
|
15
|
-
page: DocFile
|
|
16
|
-
features: DocFile[]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function ProjectView(props: Props) {
|
|
20
|
-
const pagesQuery = useSuspenseQuery({
|
|
21
|
-
queryKey: ["apps/client", props.project],
|
|
22
|
-
queryFn: async () => {
|
|
23
|
-
const response = await apiClient.api.directories[":path{.+}"].$get({
|
|
24
|
-
param: { path: `${props.project}/pages` },
|
|
25
|
-
})
|
|
26
|
-
return response.json()
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
const featuresQuery = useSuspenseQuery({
|
|
31
|
-
queryKey: ["apps/client",`products/${props.project}/features`],
|
|
32
|
-
queryFn: async () => {
|
|
33
|
-
const response = await apiClient.api.directories[":path{.+}"].$get({
|
|
34
|
-
param: { path: `${props.project}/features` },
|
|
35
|
-
})
|
|
36
|
-
return response.json()
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
const pages: DocFile[] = pagesQuery.data.files || []
|
|
41
|
-
|
|
42
|
-
const features: DocFile[] = featuresQuery.data.files || []
|
|
43
|
-
|
|
44
|
-
const milestoneRelation = featuresQuery.data.relations?.find(
|
|
45
|
-
(rel: DocRelation) => {return rel.path === `${props.project}/milestones`},
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
const milestoneOptions = milestoneRelation?.files || []
|
|
49
|
-
|
|
50
|
-
// マイルストーンフィルター状態
|
|
51
|
-
const [selectedMilestone, setSelectedMilestone] = useState<string | null>(
|
|
52
|
-
null,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
const updatePropertiesMutation = useFilePropertiesMutation()
|
|
56
|
-
|
|
57
|
-
const handleMilestoneUpdate = async (
|
|
58
|
-
featurePath: string,
|
|
59
|
-
milestone: string,
|
|
60
|
-
) => {
|
|
61
|
-
try {
|
|
62
|
-
await updatePropertiesMutation.mutateAsync({
|
|
63
|
-
path: featurePath,
|
|
64
|
-
field: "milestone",
|
|
65
|
-
value: milestone,
|
|
66
|
-
})
|
|
67
|
-
await featuresQuery.refetch()
|
|
68
|
-
} catch (error) {
|
|
69
|
-
console.error("Failed to update milestone:", error)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const handlePropertyUpdate = async (
|
|
74
|
-
featurePath: string,
|
|
75
|
-
field: string,
|
|
76
|
-
value: unknown,
|
|
77
|
-
) => {
|
|
78
|
-
try {
|
|
79
|
-
await updatePropertiesMutation.mutateAsync({
|
|
80
|
-
path: featurePath,
|
|
81
|
-
field: field,
|
|
82
|
-
value: value,
|
|
83
|
-
})
|
|
84
|
-
await featuresQuery.refetch()
|
|
85
|
-
} catch (error) {
|
|
86
|
-
console.error("Failed to update property:", error)
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const handleFeatureAdd = async (pagePath: string, featurePath: string) => {
|
|
91
|
-
// ページのfeaturesフィールドに新しい機能を追加
|
|
92
|
-
const page = pages.find((p: DocFile) => p.path.path === pagePath)
|
|
93
|
-
if (!page) return
|
|
94
|
-
|
|
95
|
-
const currentFeatures =
|
|
96
|
-
(page.type === 'markdown'
|
|
97
|
-
? (page.content.frontMatter as Record<string, unknown>)?.features as string[]
|
|
98
|
-
: []) || []
|
|
99
|
-
const updatedFeatures = [...currentFeatures, featurePath]
|
|
100
|
-
|
|
101
|
-
await updatePropertiesMutation.mutateAsync({
|
|
102
|
-
path: pagePath,
|
|
103
|
-
field: "features",
|
|
104
|
-
value: updatedFeatures,
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
// 両方のクエリを再取得
|
|
108
|
-
await Promise.all([pagesQuery.refetch(), featuresQuery.refetch()])
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const handleFeatureRemove = async (pagePath: string, featurePath: string) => {
|
|
112
|
-
try {
|
|
113
|
-
const page = pages.find((p: DocFile) => p.path.path === pagePath)
|
|
114
|
-
if (!page) return
|
|
115
|
-
|
|
116
|
-
const currentFeatures =
|
|
117
|
-
(page.type === 'markdown'
|
|
118
|
-
? (page.content.frontMatter as Record<string, unknown>)?.features as string[]
|
|
119
|
-
: []) || []
|
|
120
|
-
|
|
121
|
-
// フィーチャーを削除
|
|
122
|
-
const updatedFeatures = currentFeatures.filter((f) => f !== featurePath)
|
|
123
|
-
|
|
124
|
-
await updatePropertiesMutation.mutateAsync({
|
|
125
|
-
path: pagePath,
|
|
126
|
-
field: "features",
|
|
127
|
-
value: updatedFeatures,
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
await Promise.all([pagesQuery.refetch(), featuresQuery.refetch()])
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.error("Failed to remove feature from page:", error)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// フィルタリング関数
|
|
137
|
-
const filterFeaturesByMilestone = (features: DocFile[]) => {
|
|
138
|
-
if (!selectedMilestone) return features
|
|
139
|
-
return features.filter((feature) => {
|
|
140
|
-
if (feature.type !== 'markdown') return false
|
|
141
|
-
const milestone = (feature.content.frontMatter as Record<string, unknown>)
|
|
142
|
-
?.milestone as string
|
|
143
|
-
return milestone === selectedMilestone
|
|
144
|
-
})
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function isDocFileMdWithFeatures(file: DocFile): file is DocFileMd {
|
|
148
|
-
return file.type === 'markdown'
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ページと関連機能をグループ化
|
|
152
|
-
const pageGroups: PageGroup[] = pages
|
|
153
|
-
.filter(isDocFileMdWithFeatures)
|
|
154
|
-
.map((page: DocFileMd) => {
|
|
155
|
-
const pageFeatures = isDocFileMdWithFeatures(page)
|
|
156
|
-
? ((page.content.frontMatter as Record<string, unknown>)?.features as string[]) || []
|
|
157
|
-
: []
|
|
158
|
-
const relatedFeatures = features.filter((feature: DocFile) => {
|
|
159
|
-
return pageFeatures.includes(feature.path.path)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
// フィルタリング適用
|
|
163
|
-
const filteredFeatures = filterFeaturesByMilestone(relatedFeatures).filter((feature): feature is DocFileMd => feature.type === 'markdown')
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
page,
|
|
167
|
-
features: filteredFeatures,
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
// 関連付けられていない機能
|
|
172
|
-
const allLinkedFeatures = pageGroups.flatMap((group) =>
|
|
173
|
-
group.features.map((f) => f.path.path),
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
const unlinkedFeatures = filterFeaturesByMilestone(
|
|
177
|
-
features.filter(
|
|
178
|
-
(feature: DocFile) => !allLinkedFeatures.includes(feature.path.path),
|
|
179
|
-
),
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
return (
|
|
183
|
-
<main className="w-full space-y-2 p-2">
|
|
184
|
-
{/* マイルストーンフィルターボタン */}
|
|
185
|
-
<div className="flex flex-wrap gap-2">
|
|
186
|
-
<Button
|
|
187
|
-
variant={selectedMilestone === null ? "default" : "secondary"}
|
|
188
|
-
onClick={() => setSelectedMilestone(null)}
|
|
189
|
-
size="sm"
|
|
190
|
-
>
|
|
191
|
-
すべて
|
|
192
|
-
</Button>
|
|
193
|
-
{milestoneOptions.map((milestone: DocRelationFile) => (
|
|
194
|
-
<Button
|
|
195
|
-
key={milestone.name}
|
|
196
|
-
variant={
|
|
197
|
-
selectedMilestone === milestone.name ? "default" : "secondary"
|
|
198
|
-
}
|
|
199
|
-
onClick={() => setSelectedMilestone(milestone.name)}
|
|
200
|
-
size="sm"
|
|
201
|
-
>
|
|
202
|
-
{milestone.name}
|
|
203
|
-
</Button>
|
|
204
|
-
))}
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
{/* ページグループ */}
|
|
208
|
-
<div className="space-y-2">
|
|
209
|
-
{pageGroups.map((group) => (
|
|
210
|
-
<ProjectPageGroup
|
|
211
|
-
key={group.page.path.name}
|
|
212
|
-
group={group}
|
|
213
|
-
milestoneOptions={milestoneOptions}
|
|
214
|
-
onMilestoneUpdate={handleMilestoneUpdate}
|
|
215
|
-
onPropertyUpdate={handlePropertyUpdate}
|
|
216
|
-
allFeatures={features}
|
|
217
|
-
onFeatureAdd={handleFeatureAdd}
|
|
218
|
-
onFeatureRemove={handleFeatureRemove}
|
|
219
|
-
/>
|
|
220
|
-
))}
|
|
221
|
-
<UnlinkedFeaturesSection
|
|
222
|
-
unlinkedFeatures={unlinkedFeatures.filter((feature): feature is DocFileMd => 'frontMatter' in feature && 'title' in feature)}
|
|
223
|
-
milestoneOptions={milestoneOptions}
|
|
224
|
-
onMilestoneUpdate={handleMilestoneUpdate}
|
|
225
|
-
onPropertyUpdate={handlePropertyUpdate}
|
|
226
|
-
/>
|
|
227
|
-
</div>
|
|
228
|
-
</main>
|
|
229
|
-
)
|
|
230
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Badge } from "@/components/ui/badge"
|
|
2
|
-
import { Card } from "@/components/ui/card"
|
|
3
|
-
import type { DocFile, DocFileMd, DocRelationFile } from "@/lib/types"
|
|
4
|
-
import { FeatureItem } from "./feature-item"
|
|
5
|
-
|
|
6
|
-
function isDocFileMd(file: DocFile): file is DocFileMd {
|
|
7
|
-
return file.type === 'markdown'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type Priority = "high" | "medium" | "low"
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
unlinkedFeatures: DocFile[]
|
|
14
|
-
milestoneOptions?: DocRelationFile[]
|
|
15
|
-
onMilestoneUpdate?: (featurePath: string, milestone: string) => void
|
|
16
|
-
onPropertyUpdate?: (
|
|
17
|
-
featurePath: string,
|
|
18
|
-
field: string,
|
|
19
|
-
value: unknown,
|
|
20
|
-
) => void
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function UnlinkedFeaturesSection(props: Props) {
|
|
24
|
-
if (props.unlinkedFeatures.length === 0) {
|
|
25
|
-
return null
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const sortedFeatures = props.unlinkedFeatures
|
|
29
|
-
.filter(isDocFileMd)
|
|
30
|
-
.sort((a, b) => {
|
|
31
|
-
const priorityOrder: Record<Priority, number> = {
|
|
32
|
-
high: 0,
|
|
33
|
-
medium: 1,
|
|
34
|
-
low: 2,
|
|
35
|
-
}
|
|
36
|
-
const aPriority =
|
|
37
|
-
((a.content.frontMatter as Record<string, unknown>)?.priority as Priority) ||
|
|
38
|
-
"low"
|
|
39
|
-
const bPriority =
|
|
40
|
-
((b.content.frontMatter as Record<string, unknown>)?.priority as Priority) ||
|
|
41
|
-
"low"
|
|
42
|
-
return (priorityOrder[aPriority] || 2) - (priorityOrder[bPriority] || 2)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<Card className="w-full p-2">
|
|
47
|
-
<div className="flex gap-2">
|
|
48
|
-
<div className="w-1/3 border-r pr-4">
|
|
49
|
-
<h2 className="font-semibold text-lg">未関連付け機能</h2>
|
|
50
|
-
<p className="text-gray-600 text-sm">
|
|
51
|
-
ページに関連付けられていない機能
|
|
52
|
-
</p>
|
|
53
|
-
<Badge variant="outline" className="mt-2">
|
|
54
|
-
{props.unlinkedFeatures.length}機能
|
|
55
|
-
</Badge>
|
|
56
|
-
</div>
|
|
57
|
-
<div className="flex-1">
|
|
58
|
-
<div className="space-y-3">
|
|
59
|
-
{sortedFeatures.map((feature) => (
|
|
60
|
-
<FeatureItem
|
|
61
|
-
key={feature.path.name}
|
|
62
|
-
feature={feature}
|
|
63
|
-
milestoneOptions={props.milestoneOptions}
|
|
64
|
-
onMilestoneUpdate={props.onMilestoneUpdate}
|
|
65
|
-
onPropertyUpdate={props.onPropertyUpdate}
|
|
66
|
-
/>
|
|
67
|
-
))}
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
</Card>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
|
2
|
-
import { useState } from "react"
|
|
3
|
-
|
|
4
|
-
export function Providers(props: { children: React.ReactNode }) {
|
|
5
|
-
const [queryClient] = useState(() => {
|
|
6
|
-
return new QueryClient({
|
|
7
|
-
defaultOptions: {
|
|
8
|
-
queries: {
|
|
9
|
-
staleTime: 60 * 1000, // 1分
|
|
10
|
-
retry: 1,
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
})
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<QueryClientProvider client={queryClient}>
|
|
18
|
-
{props.children}
|
|
19
|
-
</QueryClientProvider>
|
|
20
|
-
)
|
|
21
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { RefreshCw } from "lucide-react"
|
|
2
|
-
import { Button } from "./ui/button"
|
|
3
|
-
|
|
4
|
-
type Props = {
|
|
5
|
-
onReload: () => void
|
|
6
|
-
size?: "sm" | "default" | "lg" | "icon"
|
|
7
|
-
variant?: "default" | "secondary" | "ghost" | "outline"
|
|
8
|
-
disabled?: boolean
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* リロードボタンコンポーネント
|
|
13
|
-
*/
|
|
14
|
-
export function ReloadButton(props: Props) {
|
|
15
|
-
return (
|
|
16
|
-
<Button
|
|
17
|
-
size={props.size || "sm"}
|
|
18
|
-
variant={props.variant || "secondary"}
|
|
19
|
-
onClick={props.onReload}
|
|
20
|
-
disabled={props.disabled}
|
|
21
|
-
>
|
|
22
|
-
<RefreshCw />
|
|
23
|
-
</Button>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { PanelLeftIcon } from "lucide-react"
|
|
2
|
-
import { Button } from "@/components/ui/button"
|
|
3
|
-
import { useSidebar } from "@/components/ui/sidebar"
|
|
4
|
-
|
|
5
|
-
export function SidebarButton() {
|
|
6
|
-
const sidebar = useSidebar()
|
|
7
|
-
|
|
8
|
-
return (
|
|
9
|
-
<Button variant={"outline"} onClick={sidebar.toggleSidebar} size={"icon"}>
|
|
10
|
-
<PanelLeftIcon />
|
|
11
|
-
</Button>
|
|
12
|
-
)
|
|
13
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
-
import type * as React from "react"
|
|
3
|
-
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
const alertVariants = cva(
|
|
7
|
-
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
|
8
|
-
{
|
|
9
|
-
variants: {
|
|
10
|
-
variant: {
|
|
11
|
-
default: "bg-card text-card-foreground",
|
|
12
|
-
destructive:
|
|
13
|
-
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
defaultVariants: {
|
|
17
|
-
variant: "default",
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
function Alert({
|
|
23
|
-
className,
|
|
24
|
-
variant,
|
|
25
|
-
...props
|
|
26
|
-
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
|
27
|
-
return (
|
|
28
|
-
<div
|
|
29
|
-
data-slot="alert"
|
|
30
|
-
role="alert"
|
|
31
|
-
className={cn(alertVariants({ variant }), className)}
|
|
32
|
-
{...props}
|
|
33
|
-
/>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
38
|
-
return (
|
|
39
|
-
<div
|
|
40
|
-
data-slot="alert-title"
|
|
41
|
-
className={cn(
|
|
42
|
-
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
|
43
|
-
className,
|
|
44
|
-
)}
|
|
45
|
-
{...props}
|
|
46
|
-
/>
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function AlertDescription({
|
|
51
|
-
className,
|
|
52
|
-
...props
|
|
53
|
-
}: React.ComponentProps<"div">) {
|
|
54
|
-
return (
|
|
55
|
-
<div
|
|
56
|
-
data-slot="alert-description"
|
|
57
|
-
className={cn(
|
|
58
|
-
"col-start-2 grid justify-items-start gap-1 text-muted-foreground text-sm [&_p]:leading-relaxed",
|
|
59
|
-
className,
|
|
60
|
-
)}
|
|
61
|
-
{...props}
|
|
62
|
-
/>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export { Alert, AlertTitle, AlertDescription }
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
-
import { Slot as SlotPrimitive } from "radix-ui"
|
|
3
|
-
import type * as React from "react"
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
|
|
7
|
-
const badgeVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default:
|
|
13
|
-
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
14
|
-
secondary:
|
|
15
|
-
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
16
|
-
destructive:
|
|
17
|
-
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
18
|
-
outline:
|
|
19
|
-
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
defaultVariants: {
|
|
23
|
-
variant: "default",
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
function Badge({
|
|
29
|
-
className,
|
|
30
|
-
variant,
|
|
31
|
-
asChild = false,
|
|
32
|
-
...props
|
|
33
|
-
}: React.ComponentProps<"span"> &
|
|
34
|
-
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
35
|
-
const Comp = asChild ? SlotPrimitive.Slot : "span"
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<Comp
|
|
39
|
-
data-slot="badge"
|
|
40
|
-
className={cn(badgeVariants({ variant }), className)}
|
|
41
|
-
{...props}
|
|
42
|
-
/>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export { Badge, badgeVariants }
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
-
import { Slot as SlotPrimitive } from "radix-ui"
|
|
3
|
-
import type * as React from "react"
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
|
|
7
|
-
const buttonVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default:
|
|
13
|
-
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
14
|
-
destructive:
|
|
15
|
-
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
16
|
-
outline:
|
|
17
|
-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
18
|
-
secondary:
|
|
19
|
-
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
20
|
-
ghost:
|
|
21
|
-
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
22
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
23
|
-
},
|
|
24
|
-
size: {
|
|
25
|
-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
26
|
-
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
27
|
-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
-
icon: "size-9",
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
defaultVariants: {
|
|
32
|
-
variant: "default",
|
|
33
|
-
size: "default",
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
function Button({
|
|
39
|
-
className,
|
|
40
|
-
variant,
|
|
41
|
-
size,
|
|
42
|
-
asChild = false,
|
|
43
|
-
...props
|
|
44
|
-
}: React.ComponentProps<"button"> &
|
|
45
|
-
VariantProps<typeof buttonVariants> & {
|
|
46
|
-
asChild?: boolean
|
|
47
|
-
}) {
|
|
48
|
-
const Comp = asChild ? SlotPrimitive.Slot : "button"
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<Comp
|
|
52
|
-
data-slot="button"
|
|
53
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
54
|
-
{...props}
|
|
55
|
-
/>
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export { Button, buttonVariants }
|