import React from 'react'
import Link from 'next/link'
import type { SDKStep, RAGCorpusStatus, SDKPackageId } from '@/lib/sdk'
import { CheckIcon, LockIcon, WarningIcon, ChevronDownIcon } from './SidebarIcons'
function withProject(url: string, projectId?: string): string {
if (!projectId) return url
const separator = url.includes('?') ? '&' : '?'
return `${url}${separator}project=${projectId}`
}
// =============================================================================
// PROGRESS BAR
// =============================================================================
interface ProgressBarProps {
value: number
className?: string
}
export function ProgressBar({ value, className = '' }: ProgressBarProps) {
return (
)
}
// =============================================================================
// PACKAGE INDICATOR
// =============================================================================
interface PackageIndicatorProps {
packageId: SDKPackageId
order: number
name: string
icon: string
completion: number
isActive: boolean
isExpanded: boolean
isLocked: boolean
onToggle: () => void
collapsed: boolean
}
export function PackageIndicator({
order,
name,
icon,
completion,
isActive,
isExpanded,
isLocked,
onToggle,
collapsed,
}: PackageIndicatorProps) {
if (collapsed) {
return (
)
}
return (
)
}
// =============================================================================
// STEP ITEM
// =============================================================================
interface StepItemProps {
step: SDKStep
isActive: boolean
isCompleted: boolean
isLocked: boolean
checkpointStatus?: 'passed' | 'failed' | 'warning' | 'pending'
collapsed: boolean
projectId?: string
}
export function StepItem({ step, isActive, isCompleted, isLocked, checkpointStatus, collapsed, projectId }: StepItemProps) {
const content = (
{isCompleted ? (
) : isLocked ? (
) : isActive ? (
) : (
)}
{!collapsed &&
{step.nameShort}}
{!collapsed && checkpointStatus && checkpointStatus !== 'pending' && (
{checkpointStatus === 'passed' ? (
) : checkpointStatus === 'failed' ? (
!
) : (
)}
)}
)
if (isLocked) return content
return (
{content}
)
}
// =============================================================================
// ADDITIONAL MODULE ITEM
// =============================================================================
interface AdditionalModuleItemProps {
href: string
icon: React.ReactNode
label: string
isActive: boolean | undefined
collapsed: boolean
projectId?: string
}
export function AdditionalModuleItem({ href, icon, label, isActive, collapsed, projectId }: AdditionalModuleItemProps) {
const isExternal = href.startsWith('http')
const className = `flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
collapsed ? 'justify-center' : ''
} ${
isActive
? 'bg-purple-100 text-purple-900 font-medium'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
}`
if (isExternal) {
return (
{icon}
{!collapsed && (
{label}
)}
)
}
return (
{icon}
{!collapsed && {label}}
)
}
// =============================================================================
// CORPUS STALENESS INFO
// =============================================================================
export function CorpusStalenessInfo({ ragCorpusStatus }: { ragCorpusStatus: RAGCorpusStatus }) {
const collections = ragCorpusStatus.collections
const collectionNames = Object.keys(collections)
if (collectionNames.length === 0) return null
const lastUpdated = collectionNames.reduce((latest, name) => {
const updated = new Date(collections[name].last_updated)
return updated > latest ? updated : latest
}, new Date(0))
const daysSinceUpdate = Math.floor((Date.now() - lastUpdated.getTime()) / (1000 * 60 * 60 * 24))
const totalChunks = collectionNames.reduce((sum, name) => sum + collections[name].chunks_count, 0)
return (
30 ? 'bg-amber-400' : 'bg-green-400'}`} />
RAG Corpus: {totalChunks} Chunks
{daysSinceUpdate > 30 && (
Corpus {daysSinceUpdate}d alt — Re-Evaluation empfohlen
)}
)
}