fix: backend alignment - remove drafts toggle, clean dead code, truncation indicator
- Remove non-functional drafts toggle and clean TreeFilters type - Fix AccountInvite type to match backend schema - Remove dead API methods: pinnedFlows.pin/reorder, trees.getSharedTree - Remove unused types: SessionListResponse, RatingCreate.is_verified_use - Add session list truncation indicator with size=51 lookahead Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,19 +22,9 @@ export const pinnedFlowsApi = {
|
|||||||
return data
|
return data
|
||||||
},
|
},
|
||||||
|
|
||||||
pin: async (treeId: string): Promise<PinnedFlow> => {
|
|
||||||
const { data } = await apiClient.post(`/trees/${treeId}/pin`)
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
|
|
||||||
unpin: async (treeId: string): Promise<void> => {
|
unpin: async (treeId: string): Promise<void> => {
|
||||||
await apiClient.delete(`/trees/${treeId}/pin`)
|
await apiClient.delete(`/trees/${treeId}/pin`)
|
||||||
},
|
},
|
||||||
|
|
||||||
reorder: async (order: { tree_id: string; display_order: number }[]): Promise<PinnedFlowsResponse> => {
|
|
||||||
const { data } = await apiClient.patch('/trees/pinned/reorder', { order })
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default pinnedFlowsApi
|
export default pinnedFlowsApi
|
||||||
|
|||||||
@@ -15,14 +15,6 @@ export interface SessionListParams {
|
|||||||
completed_before?: string
|
completed_before?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SessionListResponse {
|
|
||||||
items: Session[]
|
|
||||||
total: number
|
|
||||||
page: number
|
|
||||||
size: number
|
|
||||||
pages: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sessionsApi = {
|
export const sessionsApi = {
|
||||||
async list(params?: SessionListParams): Promise<Session[]> {
|
async list(params?: SessionListParams): Promise<Session[]> {
|
||||||
const response = await apiClient.get<Session[]>('/sessions', { params })
|
const response = await apiClient.get<Session[]>('/sessions', { params })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import apiClient from './client'
|
import apiClient from './client'
|
||||||
import type { Tree, TreeListItem, TreeCreate, TreeUpdate, TreeFilters, TreeShareCreate, TreeShare, TreeVisibilityUpdate, SharedTree, TreeValidationResponse } from '@/types'
|
import type { Tree, TreeListItem, TreeCreate, TreeUpdate, TreeFilters, TreeShareCreate, TreeShare, TreeVisibilityUpdate, TreeValidationResponse } from '@/types'
|
||||||
|
|
||||||
export const treesApi = {
|
export const treesApi = {
|
||||||
async list(params?: TreeFilters): Promise<TreeListItem[]> {
|
async list(params?: TreeFilters): Promise<TreeListItem[]> {
|
||||||
@@ -60,11 +60,6 @@ export const treesApi = {
|
|||||||
return response.data
|
return response.data
|
||||||
},
|
},
|
||||||
|
|
||||||
async getSharedTree(shareToken: string): Promise<SharedTree> {
|
|
||||||
const response = await apiClient.get<SharedTree>(`/shared/${shareToken}`)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
|
|
||||||
// Tree validation
|
// Tree validation
|
||||||
async canPublish(id: string): Promise<TreeValidationResponse> {
|
async canPublish(id: string): Promise<TreeValidationResponse> {
|
||||||
const response = await apiClient.post<TreeValidationResponse>(`/trees/${id}/can-publish`)
|
const response = await apiClient.post<TreeValidationResponse>(`/trees/${id}/can-publish`)
|
||||||
|
|||||||
@@ -244,8 +244,7 @@ export function SessionDetailPage() {
|
|||||||
rating: data.rating,
|
rating: data.rating,
|
||||||
review_text: data.review || undefined,
|
review_text: data.review || undefined,
|
||||||
was_helpful: data.helpful !== null ? data.helpful : undefined,
|
was_helpful: data.helpful !== null ? data.helpful : undefined,
|
||||||
session_id: session.id,
|
session_id: session.id
|
||||||
is_verified_use: true
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export function SessionHistoryPage() {
|
|||||||
const [searchParams, setSearchParams] = useSearchParams()
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
|
|
||||||
const [sessions, setSessions] = useState<Session[]>([])
|
const [sessions, setSessions] = useState<Session[]>([])
|
||||||
|
const [hasMore, setHasMore] = useState(false)
|
||||||
const [trees, setTrees] = useState<TreeListItem[]>([])
|
const [trees, setTrees] = useState<TreeListItem[]>([])
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [filter, setFilter] = useState<'all' | 'completed' | 'active'>('all')
|
const [filter, setFilter] = useState<'all' | 'completed' | 'active'>('all')
|
||||||
@@ -111,8 +112,10 @@ export function SessionHistoryPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionsData = await sessionsApi.list(params)
|
const sessionsData = await sessionsApi.list({ ...params, size: 51 })
|
||||||
setSessions(sessionsData)
|
const truncated = sessionsData.length > 50
|
||||||
|
setHasMore(truncated)
|
||||||
|
setSessions(truncated ? sessionsData.slice(0, 50) : sessionsData)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error('Failed to load sessions')
|
toast.error('Failed to load sessions')
|
||||||
console.error(err)
|
console.error(err)
|
||||||
@@ -295,6 +298,15 @@ export function SessionHistoryPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{hasMore ? (
|
||||||
|
<p className="text-center text-sm text-muted-foreground py-4">
|
||||||
|
Showing the 50 most recent sessions
|
||||||
|
</p>
|
||||||
|
) : sessions.length > 0 ? (
|
||||||
|
<p className="text-center text-sm text-muted-foreground py-4">
|
||||||
|
Showing all {sessions.length} sessions
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ export function TreeLibraryPage() {
|
|||||||
const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null)
|
const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null)
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
const [showDrafts, setShowDrafts] = useState(false)
|
|
||||||
|
|
||||||
// Read type filter from URL query params (e.g. /trees?type=procedural)
|
// Read type filter from URL query params (e.g. /trees?type=procedural)
|
||||||
const urlType = searchParams.get('type')
|
const urlType = searchParams.get('type')
|
||||||
const [typeFilter, setTypeFilter] = useState<'all' | 'troubleshooting' | 'procedural' | 'maintenance'>(
|
const [typeFilter, setTypeFilter] = useState<'all' | 'troubleshooting' | 'procedural' | 'maintenance'>(
|
||||||
@@ -131,7 +129,7 @@ export function TreeLibraryPage() {
|
|||||||
// Load trees when filters change
|
// Load trees when filters change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTrees()
|
loadTrees()
|
||||||
}, [selectedCategoryId, selectedTags, selectedFolderId, treeLibrarySortBy, showDrafts, typeFilter])
|
}, [selectedCategoryId, selectedTags, selectedFolderId, treeLibrarySortBy, typeFilter])
|
||||||
|
|
||||||
// Load folders on mount and listen for changes
|
// Load folders on mount and listen for changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -150,7 +148,6 @@ export function TreeLibraryPage() {
|
|||||||
tags: selectedTags.length > 0 ? selectedTags.join(',') : undefined,
|
tags: selectedTags.length > 0 ? selectedTags.join(',') : undefined,
|
||||||
folder_id: selectedFolderId || undefined,
|
folder_id: selectedFolderId || undefined,
|
||||||
sort_by: treeLibrarySortBy,
|
sort_by: treeLibrarySortBy,
|
||||||
include_drafts: showDrafts || undefined,
|
|
||||||
})
|
})
|
||||||
setTrees(treesData)
|
setTrees(treesData)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -326,33 +323,22 @@ export function TreeLibraryPage() {
|
|||||||
|
|
||||||
{/* View Controls */}
|
{/* View Controls */}
|
||||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
{/* Type filter tabs — includes Drafts as a first-class filter */}
|
{/* Type filter tabs */}
|
||||||
<div className="flex rounded-lg border border-border p-0.5">
|
<div className="flex rounded-lg border border-border p-0.5">
|
||||||
{(['all', 'troubleshooting', 'procedural', 'maintenance', 'drafts'] as const).map((t) => {
|
{(['all', 'troubleshooting', 'procedural', 'maintenance'] as const).map((t) => (
|
||||||
const isActive = t === 'drafts' ? showDrafts && typeFilter === 'all' : !showDrafts && typeFilter === t
|
|
||||||
return (
|
|
||||||
<button
|
<button
|
||||||
key={t}
|
key={t}
|
||||||
onClick={() => {
|
onClick={() => setTypeFilter(t)}
|
||||||
if (t === 'drafts') {
|
|
||||||
setShowDrafts(true)
|
|
||||||
setTypeFilter('all')
|
|
||||||
} else {
|
|
||||||
setShowDrafts(false)
|
|
||||||
setTypeFilter(t)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-md px-3 py-1 text-xs font-medium transition-colors',
|
'rounded-md px-3 py-1 text-xs font-medium transition-colors',
|
||||||
isActive
|
typeFilter === t
|
||||||
? 'bg-accent text-foreground'
|
? 'bg-accent text-foreground'
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{t === 'all' ? 'All' : t === 'troubleshooting' ? 'Troubleshooting' : t === 'procedural' ? 'Projects' : t === 'maintenance' ? 'Maintenance' : 'Drafts'}
|
{t === 'all' ? 'All' : t === 'troubleshooting' ? 'Troubleshooting' : t === 'procedural' ? 'Projects' : 'Maintenance'}
|
||||||
</button>
|
</button>
|
||||||
)
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right controls: sort + view toggle */}
|
{/* Right controls: sort + view toggle */}
|
||||||
|
|||||||
@@ -44,9 +44,7 @@ export interface AccountInvite {
|
|||||||
email: string
|
email: string
|
||||||
role: 'engineer' | 'viewer'
|
role: 'engineer' | 'viewer'
|
||||||
code: string
|
code: string
|
||||||
invited_by_id: string
|
expires_at: string | null
|
||||||
accepted_by_id: string | null
|
|
||||||
expires_at: string
|
|
||||||
used_at: string | null
|
used_at: string | null
|
||||||
created_at: string
|
created_at: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ export interface RatingCreate {
|
|||||||
review_text?: string
|
review_text?: string
|
||||||
was_helpful?: boolean
|
was_helpful?: boolean
|
||||||
session_id?: string
|
session_id?: string
|
||||||
is_verified_use?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RatingUpdate {
|
export interface RatingUpdate {
|
||||||
|
|||||||
@@ -215,7 +215,6 @@ export interface TreeFilters {
|
|||||||
is_active?: boolean
|
is_active?: boolean
|
||||||
author_id?: string
|
author_id?: string
|
||||||
is_public?: boolean
|
is_public?: boolean
|
||||||
include_drafts?: boolean
|
|
||||||
sort_by?: 'usage_count' | 'updated_at' | 'created_at' | 'name' | 'name_desc' | 'version'
|
sort_by?: 'usage_count' | 'updated_at' | 'created_at' | 'name' | 'name_desc' | 'version'
|
||||||
skip?: number
|
skip?: number
|
||||||
limit?: number
|
limit?: number
|
||||||
|
|||||||
Reference in New Issue
Block a user