mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-26 14:44:55 +00:00
feat: migrate DocumentationView to JSON
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,73 +0,0 @@
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { BookOpen, GitBranch, MagnifyingGlass, MapPin, Sparkle, PaintBrush, Rocket } from '@phosphor-icons/react'
|
||||
import { AgentsTab } from './DocumentationView/AgentsTab'
|
||||
import { CicdTab } from './DocumentationView/CicdTab'
|
||||
import { PwaTab } from './DocumentationView/PwaTab'
|
||||
import { ReadmeTab } from './DocumentationView/ReadmeTab'
|
||||
import { RoadmapTab } from './DocumentationView/RoadmapTab'
|
||||
import { SassTab } from './DocumentationView/SassTab'
|
||||
import { useDocumentationViewState } from './DocumentationView/useDocumentationViewState'
|
||||
|
||||
const tabs = [
|
||||
{ value: 'readme', label: 'README', icon: <BookOpen size={16} /> },
|
||||
{ value: 'roadmap', label: 'Roadmap', icon: <MapPin size={16} /> },
|
||||
{ value: 'agents', label: 'Agents', icon: <Sparkle size={16} /> },
|
||||
{ value: 'pwa', label: 'PWA', icon: <Rocket size={16} /> },
|
||||
{ value: 'sass', label: 'Sass', icon: <PaintBrush size={16} /> },
|
||||
{ value: 'cicd', label: 'CI/CD', icon: <GitBranch size={16} /> }
|
||||
]
|
||||
|
||||
export function DocumentationView() {
|
||||
const { activeTab, setActiveTab, searchQuery, handleSearchChange } = useDocumentationViewState()
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col bg-background">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 flex flex-col">
|
||||
<div className="border-b border-border bg-card px-6 py-3 space-y-3">
|
||||
<TabsList className="bg-muted/50">
|
||||
{tabs.map((tab) => (
|
||||
<TabsTrigger key={tab.value} value={tab.value} className="gap-2">
|
||||
{tab.icon}
|
||||
{tab.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
<div className="relative">
|
||||
<MagnifyingGlass size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search documentation..."
|
||||
value={searchQuery}
|
||||
onChange={handleSearchChange}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="flex-1">
|
||||
<div className="max-w-5xl mx-auto p-8">
|
||||
<TabsContent value="readme" className="m-0 space-y-6">
|
||||
<ReadmeTab />
|
||||
</TabsContent>
|
||||
<TabsContent value="roadmap" className="m-0 space-y-6">
|
||||
<RoadmapTab />
|
||||
</TabsContent>
|
||||
<TabsContent value="agents" className="m-0 space-y-6">
|
||||
<AgentsTab />
|
||||
</TabsContent>
|
||||
<TabsContent value="pwa" className="m-0 space-y-6">
|
||||
<PwaTab />
|
||||
</TabsContent>
|
||||
<TabsContent value="sass" className="m-0 space-y-6">
|
||||
<SassTab />
|
||||
</TabsContent>
|
||||
<TabsContent value="cicd" className="m-0 space-y-6">
|
||||
<CicdTab />
|
||||
</TabsContent>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Sparkle } from '@phosphor-icons/react'
|
||||
|
||||
export function AIFeatureCard({ title, description }: { title: string; description: string }) {
|
||||
return (
|
||||
<Card className="bg-primary/5 border-primary/20">
|
||||
<CardContent className="pt-4 pb-4">
|
||||
<div className="flex gap-3">
|
||||
<Sparkle size={20} weight="duotone" className="text-accent flex-shrink-0 mt-0.5" />
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-semibold text-sm">{title}</h4>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { FileCode, CheckCircle, Sparkle } from '@phosphor-icons/react'
|
||||
|
||||
export function AgentFileItem({ filename, path, description, features, featureLabel }: {
|
||||
filename: string
|
||||
path: string
|
||||
description: string
|
||||
features: string[]
|
||||
featureLabel: string
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-3 border-l-2 border-accent pl-4">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<FileCode size={18} className="text-accent" />
|
||||
<code className="text-sm font-semibold text-accent">{filename}</code>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground font-mono">{path}</p>
|
||||
<p className="text-sm text-foreground/90">{description}</p>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">{featureLabel}</p>
|
||||
<ul className="space-y-1">
|
||||
{features.map((feature) => (
|
||||
<li key={feature} className="text-sm text-foreground/80 flex items-start gap-2">
|
||||
<CheckCircle size={14} weight="fill" className="text-accent mt-1 flex-shrink-0" />
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function IntegrationPoint({ component, capabilities }: { component: string; capabilities: string[] }) {
|
||||
return (
|
||||
<div className="space-y-2 border rounded-lg p-4 bg-card">
|
||||
<h4 className="font-semibold text-sm flex items-center gap-2">
|
||||
<Sparkle size={16} weight="duotone" className="text-accent" />
|
||||
{component}
|
||||
</h4>
|
||||
<ul className="space-y-1">
|
||||
{capabilities.map((capability) => (
|
||||
<li key={capability} className="text-sm text-muted-foreground flex items-start gap-2">
|
||||
<span className="text-accent">•</span>
|
||||
<span>{capability}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import agentsData from '@/data/documentation/agents-data.json'
|
||||
import { AgentFileItem } from './AgentItems'
|
||||
|
||||
export function AgentsCoreServices() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{agentsData.coreServicesTitle}</CardTitle>
|
||||
<CardDescription>{agentsData.coreServicesDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{agentsData.coreServices.map((service) => (
|
||||
<AgentFileItem
|
||||
key={service.filename}
|
||||
featureLabel={agentsData.coreServicesFeaturesLabel}
|
||||
filename={service.filename}
|
||||
path={service.path}
|
||||
description={service.description}
|
||||
features={service.features}
|
||||
/>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Target, Package } from '@phosphor-icons/react'
|
||||
import agentsData from '@/data/documentation/agents-data.json'
|
||||
|
||||
export function AgentsFutureEnhancements() {
|
||||
return (
|
||||
<Card className="bg-muted/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Package size={20} weight="duotone" />
|
||||
{agentsData.futureEnhancementsTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{agentsData.futureEnhancements.map((item) => (
|
||||
<li key={item.title} className="flex items-start gap-2">
|
||||
<Target size={16} className="text-accent mt-1 flex-shrink-0" />
|
||||
<span>
|
||||
<strong>{item.title}:</strong> {item.description}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import agentsData from '@/data/documentation/agents-data.json'
|
||||
import { IntegrationPoint } from './AgentItems'
|
||||
|
||||
export function AgentsIntegrationPoints() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{agentsData.integrationPointsTitle}</CardTitle>
|
||||
<CardDescription>{agentsData.integrationPointsDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4">
|
||||
{agentsData.integrationPoints.map((point) => (
|
||||
<IntegrationPoint key={point.component} component={point.component} capabilities={point.capabilities} />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { FileCode } from '@phosphor-icons/react'
|
||||
import agentsData from '@/data/documentation/agents-data.json'
|
||||
|
||||
export function AgentsOverviewSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-4xl font-bold flex items-center gap-3">
|
||||
<FileCode size={36} weight="duotone" className="text-accent" />
|
||||
{agentsData.title}
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground">{agentsData.subtitle}</p>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{agentsData.overviewTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{agentsData.overview}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import agentsData from '@/data/documentation/agents-data.json'
|
||||
|
||||
export function AgentsPromptEngineering() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{agentsData.promptEngineeringTitle}</CardTitle>
|
||||
<CardDescription>{agentsData.promptEngineeringDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{agentsData.promptEngineering.map((item) => (
|
||||
<div key={item.title} className="space-y-2">
|
||||
<h3 className="font-semibold">{item.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">{item.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { AgentsCoreServices } from './AgentsCoreServices'
|
||||
import { AgentsFutureEnhancements } from './AgentsFutureEnhancements'
|
||||
import { AgentsIntegrationPoints } from './AgentsIntegrationPoints'
|
||||
import { AgentsOverviewSection } from './AgentsOverviewSection'
|
||||
import { AgentsPromptEngineering } from './AgentsPromptEngineering'
|
||||
|
||||
export function AgentsTab() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<AgentsOverviewSection />
|
||||
<div className="space-y-4">
|
||||
<AgentsCoreServices />
|
||||
<AgentsIntegrationPoints />
|
||||
<AgentsPromptEngineering />
|
||||
<AgentsFutureEnhancements />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { CheckCircle, Rocket } from '@phosphor-icons/react'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
export function CicdBestPracticesCard() {
|
||||
return (
|
||||
<Card className="bg-muted/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Rocket size={20} weight="duotone" />
|
||||
{cicdData.bestPracticesTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{cicdData.bestPractices.map((practice) => (
|
||||
<li key={practice} className="flex items-start gap-2">
|
||||
<CheckCircle size={16} className="text-accent mt-1 flex-shrink-0" weight="fill" />
|
||||
<span>{practice}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { GitBranch } from '@phosphor-icons/react'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
const toneStyles = {
|
||||
green: {
|
||||
card: 'bg-green-500/5 border-green-500/20',
|
||||
icon: 'text-green-500'
|
||||
},
|
||||
blue: {
|
||||
card: 'bg-blue-500/5 border-blue-500/20',
|
||||
icon: 'text-blue-500'
|
||||
},
|
||||
purple: {
|
||||
card: 'bg-purple-500/5 border-purple-500/20',
|
||||
icon: 'text-purple-500'
|
||||
},
|
||||
orange: {
|
||||
card: 'bg-orange-500/5 border-orange-500/20',
|
||||
icon: 'text-orange-500'
|
||||
}
|
||||
} as const
|
||||
|
||||
export function CicdBranchStrategySection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{cicdData.branchStrategyTitle}</h2>
|
||||
<div className="grid gap-4">
|
||||
{cicdData.branches.map((branch) => {
|
||||
const styles = toneStyles[branch.tone]
|
||||
return (
|
||||
<Card key={branch.name} className={styles.card}>
|
||||
<CardContent className="pt-4 pb-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<GitBranch size={20} weight="duotone" className={`${styles.icon} mt-0.5`} />
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-semibold">{branch.name}</h4>
|
||||
<p className="text-sm text-muted-foreground">{branch.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { CheckCircle } from '@phosphor-icons/react'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
export function CicdDockerCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{cicdData.dockerTitle}</CardTitle>
|
||||
<CardDescription>{cicdData.dockerDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold">{cicdData.dockerFilesTitle}</h3>
|
||||
<div className="space-y-2 ml-4">
|
||||
{cicdData.docker.files.map((file) => (
|
||||
<div key={file.name} className="space-y-1">
|
||||
<code className="text-sm font-mono text-accent">{file.name}</code>
|
||||
<p className="text-sm text-muted-foreground">{file.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold">{cicdData.dockerCommandsTitle}</h3>
|
||||
<pre className="custom-mui-code-block">{cicdData.docker.commands}</pre>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold">{cicdData.dockerFeaturesTitle}</h3>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{cicdData.docker.features.map((feature) => (
|
||||
<li key={feature} className="flex items-start gap-2">
|
||||
<CheckCircle size={16} className="text-accent mt-1 flex-shrink-0" weight="fill" />
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
export function CicdEnvVarsCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{cicdData.envVarsTitle}</CardTitle>
|
||||
<CardDescription>{cicdData.envVarsDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b">
|
||||
{cicdData.envVarsColumns.map((column) => (
|
||||
<th key={column} className="text-left py-2 pr-4 font-semibold">{column}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-muted-foreground">
|
||||
{cicdData.environmentVariables.map((variable) => (
|
||||
<tr key={variable.variable} className="border-b">
|
||||
<td className="py-2 pr-4">
|
||||
<code className="text-accent">{variable.variable}</code>
|
||||
</td>
|
||||
<td className="py-2 pr-4">{variable.description}</td>
|
||||
<td className="py-2">{variable.required}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { CheckCircle, GitBranch } from '@phosphor-icons/react'
|
||||
|
||||
export function CICDPlatformItem({ name, file, description, features, featureLabel }: {
|
||||
name: string
|
||||
file: string
|
||||
description: string
|
||||
features: string[]
|
||||
featureLabel: string
|
||||
}) {
|
||||
return (
|
||||
<div className="space-y-3 border-l-2 border-accent pl-4">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<GitBranch size={18} className="text-accent" />
|
||||
<h3 className="text-base font-semibold">{name}</h3>
|
||||
</div>
|
||||
<code className="text-xs text-muted-foreground font-mono">{file}</code>
|
||||
<p className="text-sm text-foreground/90">{description}</p>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<p className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">{featureLabel}</p>
|
||||
<ul className="space-y-1">
|
||||
{features.map((feature) => (
|
||||
<li key={feature} className="text-sm text-foreground/80 flex items-start gap-2">
|
||||
<CheckCircle size={14} weight="fill" className="text-accent mt-1 flex-shrink-0" />
|
||||
<span>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function PipelineStageCard({ stage, description, duration }: {
|
||||
stage: string
|
||||
description: string
|
||||
duration: string
|
||||
}) {
|
||||
return (
|
||||
<Card className="bg-primary/5 border-primary/20">
|
||||
<CardContent className="pt-4 pb-4">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-1 flex-1">
|
||||
<h4 className="font-semibold text-sm">{stage}</h4>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
<Badge variant="secondary" className="text-xs whitespace-nowrap">
|
||||
{duration}
|
||||
</Badge>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { GitBranch } from '@phosphor-icons/react'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
export function CicdOverviewSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
|
||||
<GitBranch size={32} weight="duotone" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold">{cicdData.title}</h1>
|
||||
<p className="text-lg text-muted-foreground">{cicdData.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{cicdData.overviewTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{cicdData.overview}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
import { PipelineStageCard } from './CicdItems'
|
||||
|
||||
export function CicdPipelineSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{cicdData.pipelineTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{cicdData.pipeline.intro}</p>
|
||||
<div className="grid gap-3">
|
||||
{cicdData.pipeline.stages.map((stage) => (
|
||||
<PipelineStageCard
|
||||
key={stage.stage}
|
||||
stage={stage.stage}
|
||||
description={stage.description}
|
||||
duration={stage.duration}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
import { CICDPlatformItem } from './CicdItems'
|
||||
|
||||
export function CicdPlatformsCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{cicdData.platformsTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{cicdData.platforms.map((platform) => (
|
||||
<CICDPlatformItem
|
||||
key={platform.name}
|
||||
featureLabel={cicdData.platformsFeaturesLabel}
|
||||
name={platform.name}
|
||||
file={platform.file}
|
||||
description={platform.description}
|
||||
features={platform.features}
|
||||
/>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Lightbulb } from '@phosphor-icons/react'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
export function CicdQuickStartCard() {
|
||||
return (
|
||||
<Card className="bg-accent/10 border-accent/20">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Lightbulb size={20} weight="duotone" className="text-accent" />
|
||||
{cicdData.quickStartTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-3">
|
||||
{cicdData.quickStart.map((step) => (
|
||||
<div key={step.step} className="space-y-2">
|
||||
<h3 className="font-semibold flex items-center gap-2">
|
||||
<span className="w-6 h-6 rounded-full bg-accent text-accent-foreground flex items-center justify-center text-sm">
|
||||
{step.step}
|
||||
</span>
|
||||
{step.title}
|
||||
</h3>
|
||||
<p className="text-sm text-foreground/80 ml-8">{step.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { FileCode, Package } from '@phosphor-icons/react'
|
||||
import cicdData from '@/data/documentation/cicd-data.json'
|
||||
|
||||
export function CicdResourcesCard() {
|
||||
return (
|
||||
<Card className="border-primary/30">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Package size={20} weight="duotone" />
|
||||
{cicdData.resourcesTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{cicdData.resources.map((resource) => (
|
||||
<li key={resource.label} className="flex items-start gap-2">
|
||||
<FileCode size={16} className="text-accent mt-1 flex-shrink-0" />
|
||||
<span>
|
||||
<code className="text-accent">{resource.label}</code> - {resource.description}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { CicdBestPracticesCard } from './CicdBestPracticesCard'
|
||||
import { CicdBranchStrategySection } from './CicdBranchStrategySection'
|
||||
import { CicdDockerCard } from './CicdDockerCard'
|
||||
import { CicdEnvVarsCard } from './CicdEnvVarsCard'
|
||||
import { CicdOverviewSection } from './CicdOverviewSection'
|
||||
import { CicdPipelineSection } from './CicdPipelineSection'
|
||||
import { CicdPlatformsCard } from './CicdPlatformsCard'
|
||||
import { CicdQuickStartCard } from './CicdQuickStartCard'
|
||||
import { CicdResourcesCard } from './CicdResourcesCard'
|
||||
|
||||
export function CicdTab() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<CicdOverviewSection />
|
||||
<CicdPlatformsCard />
|
||||
<CicdPipelineSection />
|
||||
<CicdDockerCard />
|
||||
<CicdEnvVarsCard />
|
||||
<CicdBranchStrategySection />
|
||||
<CicdQuickStartCard />
|
||||
<CicdBestPracticesCard />
|
||||
<CicdResourcesCard />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export function FeatureItem({ icon, title, description }: { icon: ReactNode; title: string; description: string }) {
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<div className="text-accent mt-0.5">{icon}</div>
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-semibold text-sm">{title}</h4>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Sparkle } from '@phosphor-icons/react'
|
||||
|
||||
export function FeatureItem({ icon, title, description }: { icon: React.ReactNode; title: string; description: string }) {
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<div className="text-accent mt-0.5">{icon}</div>
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-semibold text-sm">{title}</h4>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AIFeatureCard({ title, description }: { title: string; description: string }) {
|
||||
return (
|
||||
<Card className="bg-primary/5 border-primary/20">
|
||||
<CardContent className="pt-4 pb-4">
|
||||
<div className="flex gap-3">
|
||||
<Sparkle size={20} weight="duotone" className="text-accent flex-shrink-0 mt-0.5" />
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-semibold text-sm">{title}</h4>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { CheckCircle } from '@phosphor-icons/react'
|
||||
import pwaData from '@/data/documentation/pwa-data.json'
|
||||
|
||||
export function PwaFeaturesCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{pwaData.featuresTitle}</CardTitle>
|
||||
<CardDescription>{pwaData.featuresDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid md:grid-cols-2 gap-4">
|
||||
{pwaData.features.map((feature) => (
|
||||
<div key={feature.title} className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<CheckCircle size={16} weight="fill" className="text-accent" />
|
||||
<span className="font-semibold text-sm">{feature.title}</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground ml-6">{feature.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import pwaData from '@/data/documentation/pwa-data.json'
|
||||
|
||||
function InstallationCard({ title, items }: { title: string; items: { title: string; steps: string[] }[] }) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-sm">
|
||||
{items.map((item) => (
|
||||
<div key={item.title}>
|
||||
<div className="font-semibold mb-1">{item.title}</div>
|
||||
<ol className="list-decimal list-inside space-y-1 text-muted-foreground ml-2">
|
||||
{item.steps.map((step) => (
|
||||
<li key={step}>{step}</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export function PwaInstallationSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{pwaData.installationTitle}</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<InstallationCard title={pwaData.installationDesktopTitle} items={pwaData.installation.desktop} />
|
||||
<InstallationCard title={pwaData.installationMobileTitle} items={pwaData.installation.mobile} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { CheckCircle, Wrench } from '@phosphor-icons/react'
|
||||
import pwaData from '@/data/documentation/pwa-data.json'
|
||||
|
||||
function OfflineList({ items, accent }: { items: string[]; accent: boolean }) {
|
||||
return (
|
||||
<ul className={`space-y-2 text-sm ${accent ? 'text-foreground/80' : 'text-muted-foreground'}`}>
|
||||
{items.map((item) => (
|
||||
<li key={item} className="flex items-start gap-2">
|
||||
<span className={accent ? 'text-accent mt-0.5' : 'mt-0.5'}>•</span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export function PwaOfflineSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{pwaData.offlineTitle}</h2>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<Card className="border-accent/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<CheckCircle size={20} weight="fill" className="text-accent" />
|
||||
{pwaData.offlineWorksTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<OfflineList items={pwaData.offline.worksOffline} accent />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="border-muted">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Wrench size={20} weight="duotone" className="text-muted-foreground" />
|
||||
{pwaData.offlineRequiresTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<OfflineList items={pwaData.offline.requiresInternet} accent={false} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Rocket } from '@phosphor-icons/react'
|
||||
import pwaData from '@/data/documentation/pwa-data.json'
|
||||
|
||||
export function PwaOverviewSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
|
||||
<Rocket size={32} weight="duotone" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold">{pwaData.title}</h1>
|
||||
<p className="text-lg text-muted-foreground">{pwaData.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{pwaData.overviewTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{pwaData.overview}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Lightbulb } from '@phosphor-icons/react'
|
||||
import pwaData from '@/data/documentation/pwa-data.json'
|
||||
|
||||
export function PwaProTipsCard() {
|
||||
return (
|
||||
<Card className="bg-accent/10 border-accent/20">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Lightbulb size={20} weight="duotone" className="text-accent" />
|
||||
{pwaData.proTipsTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{pwaData.proTips.map((tip) => (
|
||||
<li key={tip.title} className="flex items-start gap-2">
|
||||
<span className="text-accent mt-1">•</span>
|
||||
<span>
|
||||
<strong>{tip.title}:</strong> {tip.description}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import pwaData from '@/data/documentation/pwa-data.json'
|
||||
|
||||
export function PwaSettingsCard() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{pwaData.settingsTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{pwaData.settingsDescription}</p>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{pwaData.settingsCardTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{pwaData.settings.map((setting, index) => (
|
||||
<div key={setting.title} className="space-y-2">
|
||||
<div className="font-semibold">{setting.title}</div>
|
||||
<p className="text-sm text-muted-foreground">{setting.description}</p>
|
||||
{index < pwaData.settings.length - 1 && <Separator />}
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { PwaFeaturesCard } from './PwaFeaturesCard'
|
||||
import { PwaInstallationSection } from './PwaInstallationSection'
|
||||
import { PwaOfflineSection } from './PwaOfflineSection'
|
||||
import { PwaOverviewSection } from './PwaOverviewSection'
|
||||
import { PwaProTipsCard } from './PwaProTipsCard'
|
||||
import { PwaSettingsCard } from './PwaSettingsCard'
|
||||
|
||||
export function PwaTab() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PwaOverviewSection />
|
||||
<PwaFeaturesCard />
|
||||
<PwaInstallationSection />
|
||||
<PwaSettingsCard />
|
||||
<PwaOfflineSection />
|
||||
<PwaProTipsCard />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Code, Database, Tree, PaintBrush, Flask, Play, Cube, Wrench, Gear, Rocket, Lightbulb, CheckCircle } from '@phosphor-icons/react'
|
||||
import { AIFeatureCard } from './AIFeatureCard'
|
||||
import { FeatureItem } from './FeatureItem'
|
||||
import readmeData from '@/data/documentation/readme-data.json'
|
||||
|
||||
const Sparkle = ({ size }: { size: number }) => <span style={{ fontSize: size }}>✨</span>
|
||||
|
||||
const iconMap: Record<string, any> = {
|
||||
Code, Database, Tree, PaintBrush, Flask, Play, Cube, Wrench, Gear, Sparkle
|
||||
}
|
||||
|
||||
export function ReadmeTab() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
|
||||
<Code size={32} weight="duotone" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold">{readmeData.title}</h1>
|
||||
<p className="text-lg text-muted-foreground">{readmeData.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{readmeData.sections.overviewTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{readmeData.overview}</p>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Rocket size={20} weight="duotone" />
|
||||
{readmeData.sections.featuresTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{readmeData.features.map((feature, idx) => {
|
||||
const Icon = iconMap[feature.icon] || Code
|
||||
return (
|
||||
<FeatureItem
|
||||
key={idx}
|
||||
icon={<Icon size={18} />}
|
||||
title={feature.title}
|
||||
description={feature.description}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{readmeData.sections.gettingStartedTitle}</h2>
|
||||
<Card>
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
{readmeData.gettingStarted.map((step) => (
|
||||
<div key={step.step} className="space-y-2">
|
||||
<h3 className="text-lg font-semibold flex items-center gap-2">
|
||||
<span className="w-6 h-6 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm">
|
||||
{step.step}
|
||||
</span>
|
||||
{step.title}
|
||||
</h3>
|
||||
<p className="text-muted-foreground ml-8">{step.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{readmeData.sections.aiFeaturesTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">
|
||||
{readmeData.sections.aiFeaturesDescription}
|
||||
</p>
|
||||
<div className="grid gap-3">
|
||||
{readmeData.aiFeatures.map((feature, idx) => (
|
||||
<AIFeatureCard key={idx} title={feature.title} description={feature.description} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{readmeData.sections.techStackTitle}</h2>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{readmeData.sections.techStackFrontendTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm text-foreground/80">
|
||||
{readmeData.techStack.frontend.map((tech, idx) => (
|
||||
<li key={idx} className="flex items-center gap-2">
|
||||
<CheckCircle size={16} weight="fill" className="text-accent" />
|
||||
{tech}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">{readmeData.sections.techStackBackendTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm text-foreground/80">
|
||||
{readmeData.techStack.backend.map((tech, idx) => (
|
||||
<li key={idx} className="flex items-center gap-2">
|
||||
<CheckCircle size={16} weight="fill" className="text-accent" />
|
||||
{tech}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="bg-accent/10 border-accent/20">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Lightbulb size={20} weight="duotone" className="text-accent" />
|
||||
{readmeData.sections.proTipsTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 text-sm">
|
||||
{readmeData.proTips.map((tip, idx) => (
|
||||
<p key={idx}>{tip}</p>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
export function RoadmapItem({ status, title, description, version }: {
|
||||
status: 'completed' | 'planned'
|
||||
title: string
|
||||
description: string
|
||||
version: string
|
||||
}) {
|
||||
return (
|
||||
<Card className={status === 'completed' ? 'bg-green-500/5 border-green-500/20' : 'bg-muted/50'}>
|
||||
<CardContent className="pt-4 pb-4">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-1 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="font-semibold">{title}</h4>
|
||||
<Badge variant={status === 'completed' ? 'default' : 'secondary'} className="text-xs">
|
||||
{version}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { CheckCircle, Clock, MapPin } from '@phosphor-icons/react'
|
||||
import roadmapData from '@/data/documentation/roadmap-data.json'
|
||||
import { RoadmapItem } from './RoadmapItem'
|
||||
|
||||
const sections = [
|
||||
{
|
||||
key: 'completed',
|
||||
title: roadmapData.sections.completedTitle,
|
||||
icon: <CheckCircle size={24} weight="fill" className="text-green-500" />,
|
||||
items: roadmapData.completed
|
||||
},
|
||||
{
|
||||
key: 'planned',
|
||||
title: roadmapData.sections.plannedTitle,
|
||||
icon: <Clock size={24} weight="duotone" className="text-accent" />,
|
||||
items: roadmapData.planned
|
||||
}
|
||||
]
|
||||
|
||||
export function RoadmapTab() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-4xl font-bold flex items-center gap-3">
|
||||
<MapPin size={36} weight="duotone" className="text-accent" />
|
||||
{roadmapData.title}
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground">{roadmapData.subtitle}</p>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-6">
|
||||
{sections.map((section) => (
|
||||
<div key={section.key}>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
{section.icon}
|
||||
<h2 className="text-2xl font-semibold">{section.title}</h2>
|
||||
</div>
|
||||
<div className="space-y-3 ml-9">
|
||||
{section.items.map((item) => (
|
||||
<RoadmapItem
|
||||
key={`${section.key}-${item.title}`}
|
||||
status={section.key === 'completed' ? 'completed' : 'planned'}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
version={item.version}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
import { AnimationItem } from './SassItems'
|
||||
|
||||
export function SassAnimationsCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{sassData.animationsTitle}</CardTitle>
|
||||
<CardDescription>{sassData.animationsDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||
{sassData.animations.map((animation) => (
|
||||
<AnimationItem key={animation.name} name={animation.name} description={animation.description} />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { CheckCircle, Target } from '@phosphor-icons/react'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
|
||||
export function SassBestPracticesCard() {
|
||||
return (
|
||||
<Card className="bg-muted/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Target size={20} weight="duotone" />
|
||||
{sassData.bestPracticesTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{sassData.bestPractices.map((practice) => (
|
||||
<li key={practice} className="flex items-start gap-2">
|
||||
<CheckCircle size={16} className="text-accent mt-1 flex-shrink-0" weight="fill" />
|
||||
<span>{practice}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
import { SassComponentItem } from './SassItems'
|
||||
|
||||
export function SassComponentsCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{sassData.componentsTitle}</CardTitle>
|
||||
<CardDescription>{sassData.componentsDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{sassData.components.map((component) => (
|
||||
<SassComponentItem
|
||||
key={component.name}
|
||||
name={component.name}
|
||||
classes={component.classes}
|
||||
description={component.description}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
|
||||
export function SassFileStructureCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{sassData.fileStructureTitle}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{sassData.fileStructure.map((item) => (
|
||||
<div key={item.file} className="space-y-2">
|
||||
<code className="text-sm font-mono text-accent">{item.file}</code>
|
||||
<p className="text-sm text-muted-foreground ml-4">{item.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
export function SassComponentItem({ name, classes, description }: { name: string; classes: string[]; description: string }) {
|
||||
return (
|
||||
<div className="space-y-2 p-4 border rounded-lg bg-card">
|
||||
<h4 className="font-semibold">{name}</h4>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
<div className="space-y-1">
|
||||
{classes.map((cls) => (
|
||||
<code key={cls} className="text-xs font-mono text-accent block">{cls}</code>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AnimationItem({ name, description }: { name: string; description: string }) {
|
||||
return (
|
||||
<div className="space-y-1 p-3 border rounded-lg bg-card">
|
||||
<code className="text-xs font-mono text-accent">{name}</code>
|
||||
<p className="text-xs text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Code } from '@phosphor-icons/react'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
import { FeatureItem } from './FeatureItems'
|
||||
|
||||
export function SassLayoutCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{sassData.layoutTitle}</CardTitle>
|
||||
<CardDescription>{sassData.layoutDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{sassData.layoutComponents.map((item) => (
|
||||
<FeatureItem
|
||||
key={item.title}
|
||||
icon={<Code size={18} />}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
/>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { PaintBrush } from '@phosphor-icons/react'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
|
||||
export function SassOverviewSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-16 h-16 rounded-xl bg-gradient-to-br from-primary to-accent flex items-center justify-center">
|
||||
<PaintBrush size={32} weight="duotone" className="text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold">{sassData.title}</h1>
|
||||
<p className="text-lg text-muted-foreground">{sassData.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-semibold">{sassData.overviewTitle}</h2>
|
||||
<p className="text-foreground/90 leading-relaxed">{sassData.overview}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Rocket } from '@phosphor-icons/react'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
|
||||
export function SassQuickStartCard() {
|
||||
return (
|
||||
<Card className="bg-accent/5 border-accent/20">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Rocket size={20} weight="duotone" />
|
||||
{sassData.quickStartTitle}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold">{sassData.quickStart.components.title}</h3>
|
||||
<pre className="custom-mui-code-block">{sassData.quickStart.components.code}</pre>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold">{sassData.quickStart.mixins.title}</h3>
|
||||
<pre className="custom-mui-code-block">{sassData.quickStart.mixins.code}</pre>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { SassAnimationsCard } from './SassAnimationsCard'
|
||||
import { SassBestPracticesCard } from './SassBestPracticesCard'
|
||||
import { SassComponentsCard } from './SassComponentsCard'
|
||||
import { SassFileStructureCard } from './SassFileStructureCard'
|
||||
import { SassLayoutCard } from './SassLayoutCard'
|
||||
import { SassOverviewSection } from './SassOverviewSection'
|
||||
import { SassQuickStartCard } from './SassQuickStartCard'
|
||||
import { SassUtilitiesCard } from './SassUtilitiesCard'
|
||||
|
||||
export function SassTab() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<SassOverviewSection />
|
||||
<SassFileStructureCard />
|
||||
<SassComponentsCard />
|
||||
<SassLayoutCard />
|
||||
<SassUtilitiesCard />
|
||||
<SassAnimationsCard />
|
||||
<SassQuickStartCard />
|
||||
<SassBestPracticesCard />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Lightbulb } from '@phosphor-icons/react'
|
||||
import sassData from '@/data/documentation/sass-data.json'
|
||||
|
||||
export function SassUtilitiesCard() {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>{sassData.utilitiesTitle}</CardTitle>
|
||||
<CardDescription>{sassData.utilitiesDescription}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
{sassData.utilities.map((utility, index) => (
|
||||
<div key={utility.title} className="space-y-2">
|
||||
<h3 className="font-semibold flex items-center gap-2">
|
||||
<Lightbulb size={18} weight="duotone" className="text-accent" />
|
||||
{utility.title}
|
||||
</h3>
|
||||
<div className="ml-6 space-y-2 text-sm">
|
||||
<p className="font-mono text-accent">{utility.mixin}</p>
|
||||
<p className="text-muted-foreground">{utility.description}</p>
|
||||
<pre className="custom-mui-code-block text-xs mt-2">{utility.snippet}</pre>
|
||||
</div>
|
||||
{index < sassData.utilities.length - 1 && <Separator />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
|
||||
export function useDocumentationViewState() {
|
||||
const [activeTab, setActiveTab] = useState('readme')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
|
||||
const handleSearchChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchQuery(event.target.value)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
searchQuery,
|
||||
handleSearchChange
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,545 @@
|
||||
{
|
||||
"id": "conflict-card",
|
||||
"type": "div",
|
||||
"id": "conflict-card-motion",
|
||||
"type": "motion.div",
|
||||
"props": {
|
||||
"className": "conflict-card"
|
||||
"layout": true,
|
||||
"initial": { "opacity": 0, "y": 20 },
|
||||
"animate": { "opacity": 1, "y": 0 },
|
||||
"exit": { "opacity": 0, "x": -100 },
|
||||
"transition": { "duration": 0.2 }
|
||||
},
|
||||
"children": []
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-container",
|
||||
"type": "Card",
|
||||
"props": {
|
||||
"className": "border-destructive/30 hover:border-destructive/50 transition-colors"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-header",
|
||||
"type": "CardHeader",
|
||||
"props": {
|
||||
"className": "pb-3"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-header-flex",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-start justify-between gap-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-header-content",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-start gap-3 flex-1 min-w-0"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-entity-icon",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "mt-0.5"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-icon-code",
|
||||
"type": "Code",
|
||||
"conditional": {
|
||||
"if": "conflict.entityType === 'files'"
|
||||
},
|
||||
"props": {
|
||||
"size": 20,
|
||||
"weight": "duotone",
|
||||
"className": "text-primary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-icon-database",
|
||||
"type": "Database",
|
||||
"conditional": {
|
||||
"if": "conflict.entityType !== 'files'"
|
||||
},
|
||||
"props": {
|
||||
"size": 20,
|
||||
"weight": "duotone",
|
||||
"className": "text-accent"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-card-info",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex-1 min-w-0"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-title",
|
||||
"type": "CardTitle",
|
||||
"bindings": {
|
||||
"children": "conflict.id"
|
||||
},
|
||||
"props": {
|
||||
"className": "text-base font-mono truncate"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-card-description",
|
||||
"type": "CardDescription",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2 mt-1"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-entity-badge",
|
||||
"type": "Badge",
|
||||
"bindings": {
|
||||
"children": "conflict.entityType"
|
||||
},
|
||||
"props": {
|
||||
"variant": "outline",
|
||||
"className": "text-xs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-time-diff",
|
||||
"type": "span",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "cardState.timeDiffMinutes",
|
||||
"transform": "`${data} minutes ago`"
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"className": "text-xs text-muted-foreground"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-card-toggle",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "cardState.setExpanded",
|
||||
"transform": "(prev) => data(!prev)"
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "ghost"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-toggle-icon",
|
||||
"type": "CaretDown",
|
||||
"conditional": {
|
||||
"if": "cardState.expanded"
|
||||
},
|
||||
"props": {
|
||||
"size": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-card-toggle-icon-collapsed",
|
||||
"type": "CaretRight",
|
||||
"conditional": {
|
||||
"if": "!cardState.expanded"
|
||||
},
|
||||
"props": {
|
||||
"size": 16
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-card-content-animation",
|
||||
"type": "AnimatePresence",
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-content-motion",
|
||||
"type": "motion.div",
|
||||
"conditional": {
|
||||
"if": "cardState.expanded"
|
||||
},
|
||||
"props": {
|
||||
"initial": { "height": 0, "opacity": 0 },
|
||||
"animate": { "height": "auto", "opacity": 1 },
|
||||
"exit": { "height": 0, "opacity": 0 },
|
||||
"transition": { "duration": 0.2 }
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-card-content",
|
||||
"type": "CardContent",
|
||||
"props": {
|
||||
"className": "space-y-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-separator-1",
|
||||
"type": "Separator"
|
||||
},
|
||||
{
|
||||
"id": "conflict-versions-grid",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "grid grid-cols-2 gap-4"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-local-version",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-local-header",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-local-icon",
|
||||
"type": "Database",
|
||||
"props": {
|
||||
"size": 16,
|
||||
"className": "text-primary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-local-label",
|
||||
"type": "h4",
|
||||
"bindings": {
|
||||
"children": "conflict.localVersion ? 'Local Version' : 'Local'"
|
||||
},
|
||||
"props": {
|
||||
"className": "text-sm font-medium"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-local-newer-badge",
|
||||
"type": "Badge",
|
||||
"conditional": {
|
||||
"if": "cardState.isLocalNewer"
|
||||
},
|
||||
"props": {
|
||||
"variant": "secondary",
|
||||
"className": "text-xs",
|
||||
"children": "Newer"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-local-content",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "bg-muted/50 rounded-md p-3 space-y-1"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-local-time",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-1.5 text-xs text-muted-foreground"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-local-clock",
|
||||
"type": "Clock",
|
||||
"props": {
|
||||
"size": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-local-timestamp",
|
||||
"type": "span",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "conflict.localTimestamp",
|
||||
"transform": "new Date(data).toLocaleString()"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-local-json",
|
||||
"type": "pre",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "conflict.localVersion",
|
||||
"transform": "JSON.stringify(data, null, 2).slice(0, 200) + '...'"
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"className": "text-xs overflow-hidden text-ellipsis text-primary"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-remote-version",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "space-y-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-remote-header",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-remote-icon",
|
||||
"type": "Cloud",
|
||||
"props": {
|
||||
"size": 16,
|
||||
"className": "text-accent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-remote-label",
|
||||
"type": "h4",
|
||||
"bindings": {
|
||||
"children": "conflict.remoteVersion ? 'Remote Version' : 'Remote'"
|
||||
},
|
||||
"props": {
|
||||
"className": "text-sm font-medium"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-remote-newer-badge",
|
||||
"type": "Badge",
|
||||
"conditional": {
|
||||
"if": "!cardState.isLocalNewer"
|
||||
},
|
||||
"props": {
|
||||
"variant": "secondary",
|
||||
"className": "text-xs",
|
||||
"children": "Newer"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-remote-content",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "bg-muted/50 rounded-md p-3 space-y-1"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-remote-time",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex items-center gap-1.5 text-xs text-muted-foreground"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-remote-clock",
|
||||
"type": "Clock",
|
||||
"props": {
|
||||
"size": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-remote-timestamp",
|
||||
"type": "span",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "conflict.remoteTimestamp",
|
||||
"transform": "new Date(data).toLocaleString()"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-remote-json",
|
||||
"type": "pre",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "conflict.remoteVersion",
|
||||
"transform": "JSON.stringify(data, null, 2).slice(0, 200) + '...'"
|
||||
}
|
||||
},
|
||||
"props": {
|
||||
"className": "text-xs overflow-hidden text-ellipsis text-accent"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-separator-2",
|
||||
"type": "Separator"
|
||||
},
|
||||
{
|
||||
"id": "conflict-actions",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "flex flex-wrap gap-2"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-resolve-local",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onResolve",
|
||||
"transform": "() => data(conflict.id, 'local')"
|
||||
},
|
||||
"disabled": "isResolving"
|
||||
},
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "outline",
|
||||
"className": "flex-1 min-w-[120px]"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-resolve-local-icon",
|
||||
"type": "Database",
|
||||
"props": {
|
||||
"size": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-local-text",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"children": "Keep Local"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-remote",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onResolve",
|
||||
"transform": "() => data(conflict.id, 'remote')"
|
||||
},
|
||||
"disabled": "isResolving"
|
||||
},
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "outline",
|
||||
"className": "flex-1 min-w-[120px]"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-resolve-remote-icon",
|
||||
"type": "Cloud",
|
||||
"props": {
|
||||
"size": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-remote-text",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"children": "Keep Remote"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-merge",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onResolve",
|
||||
"transform": "() => data(conflict.id, 'merge')"
|
||||
},
|
||||
"disabled": "isResolving"
|
||||
},
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "outline",
|
||||
"className": "flex-1 min-w-[120px]"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-resolve-merge-icon",
|
||||
"type": "ArrowsLeftRight",
|
||||
"props": {
|
||||
"size": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-merge-text",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"children": "Merge Both"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-details",
|
||||
"type": "Button",
|
||||
"bindings": {
|
||||
"onClick": {
|
||||
"source": "onViewDetails",
|
||||
"transform": "() => data(conflict)"
|
||||
},
|
||||
"disabled": "isResolving"
|
||||
},
|
||||
"props": {
|
||||
"size": "sm",
|
||||
"variant": "secondary"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "conflict-resolve-details-icon",
|
||||
"type": "MagnifyingGlass",
|
||||
"props": {
|
||||
"size": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "conflict-resolve-details-text",
|
||||
"type": "span",
|
||||
"props": {
|
||||
"children": "Details"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
89
src/components/json-definitions/documentation-view.json
Normal file
89
src/components/json-definitions/documentation-view.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"id": "documentation-view",
|
||||
"type": "div",
|
||||
"className": "h-full flex flex-col bg-background",
|
||||
"children": [
|
||||
{
|
||||
"id": "documentation-tabs",
|
||||
"type": "Tabs",
|
||||
"className": "flex-1 flex flex-col",
|
||||
"bindings": {
|
||||
"value": {
|
||||
"source": "activeTab",
|
||||
"transform": "data"
|
||||
},
|
||||
"onValueChange": {
|
||||
"source": "setActiveTab",
|
||||
"transform": "data"
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "documentation-header",
|
||||
"type": "div",
|
||||
"className": "border-b border-border bg-card px-6 py-3 space-y-3",
|
||||
"children": [
|
||||
{
|
||||
"id": "tabs-list",
|
||||
"type": "TabsList",
|
||||
"className": "bg-muted/50",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "tabsData",
|
||||
"transform": "data"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "search-input-wrapper",
|
||||
"type": "div",
|
||||
"className": "relative",
|
||||
"children": [
|
||||
{
|
||||
"id": "search-icon",
|
||||
"type": "MagnifyingGlass",
|
||||
"className": "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground",
|
||||
"size": 18
|
||||
},
|
||||
{
|
||||
"id": "search-input",
|
||||
"type": "Input",
|
||||
"placeholder": "Search documentation...",
|
||||
"className": "pl-10",
|
||||
"bindings": {
|
||||
"value": {
|
||||
"source": "searchQuery",
|
||||
"transform": "data"
|
||||
},
|
||||
"onChange": {
|
||||
"source": "handleSearchChange",
|
||||
"transform": "data"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tabs-content",
|
||||
"type": "ScrollArea",
|
||||
"className": "flex-1",
|
||||
"children": [
|
||||
{
|
||||
"id": "content-wrapper",
|
||||
"type": "div",
|
||||
"className": "max-w-5xl mx-auto p-8",
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "activeTab",
|
||||
"transform": "data"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -43,3 +43,6 @@ export { useAppLayout } from './use-app-layout'
|
||||
export { useAppRouterLayout } from './use-app-router-layout'
|
||||
export { useNavigationMenu } from './use-navigation-menu'
|
||||
export { useDataSourceManagerState } from './use-data-source-manager-state'
|
||||
export { useConflictResolution } from './use-conflict-resolution'
|
||||
export { useConflictCard } from './use-conflict-card'
|
||||
export { useDocumentationView } from './use-documentation-view'
|
||||
|
||||
28
src/hooks/use-documentation-view.ts
Normal file
28
src/hooks/use-documentation-view.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
|
||||
const tabs = [
|
||||
{ value: 'readme', label: 'README', icon: 'BookOpen' },
|
||||
{ value: 'roadmap', label: 'Roadmap', icon: 'MapPin' },
|
||||
{ value: 'agents', label: 'Agents', icon: 'Sparkle' },
|
||||
{ value: 'pwa', label: 'PWA', icon: 'Rocket' },
|
||||
{ value: 'sass', label: 'Sass', icon: 'PaintBrush' },
|
||||
{ value: 'cicd', label: 'CI/CD', icon: 'GitBranch' }
|
||||
]
|
||||
|
||||
export function useDocumentationView() {
|
||||
const [activeTab, setActiveTab] = useState('readme')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
|
||||
const handleSearchChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchQuery(event.target.value)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
searchQuery,
|
||||
handleSearchChange,
|
||||
tabsData: tabs,
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { useDataSourceManagerState } from '@/hooks/use-data-source-manager-state
|
||||
import { useFormatValue } from '@/hooks/use-format-value'
|
||||
import { useConflictResolution } from '@/hooks/use-conflict-resolution'
|
||||
import { useConflictCard } from '@/hooks/use-conflict-card'
|
||||
import { useDocumentationView } from '@/hooks/use-documentation-view'
|
||||
|
||||
export interface HookRegistry {
|
||||
[key: string]: (...args: any[]) => any
|
||||
@@ -53,6 +54,7 @@ export const hooksRegistry: HookRegistry = {
|
||||
useFormatValue,
|
||||
useConflictResolution,
|
||||
useConflictCard,
|
||||
useDocumentationView,
|
||||
// Add more hooks here as needed
|
||||
}
|
||||
|
||||
|
||||
4
src/lib/json-ui/interfaces/documentation-view.ts
Normal file
4
src/lib/json-ui/interfaces/documentation-view.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface DocumentationViewProps {
|
||||
// This component is stateful and uses useDocumentationViewState hook
|
||||
// No props required - all state is managed internally
|
||||
}
|
||||
@@ -231,3 +231,4 @@ export * from './template-explorer'
|
||||
export * from './project-manager'
|
||||
export * from './storage-settings-panel'
|
||||
export * from './feature-toggle-settings'
|
||||
export * from './documentation-view'
|
||||
|
||||
@@ -250,6 +250,7 @@ import type {
|
||||
ProjectManagerProps,
|
||||
StorageSettingsPanelProps,
|
||||
FeatureToggleSettingsProps,
|
||||
DocumentationViewProps,
|
||||
} from './interfaces'
|
||||
|
||||
// Import JSON definitions
|
||||
@@ -497,6 +498,7 @@ import templateExplorerDef from '@/components/json-definitions/template-explorer
|
||||
import projectManagerDef from '@/components/json-definitions/project-manager.json'
|
||||
import storageSettingsPanelDef from '@/components/json-definitions/storage-settings-panel.json'
|
||||
import featureToggleSettingsDef from '@/components/json-definitions/feature-toggle-settings.json'
|
||||
import documentationViewDef from '@/components/json-definitions/documentation-view.json'
|
||||
|
||||
// Create pure JSON components (no hooks)
|
||||
export const BindingIndicator = createJsonComponent<BindingIndicatorProps>(bindingIndicatorDef)
|
||||
@@ -884,7 +886,17 @@ export const PreloadIndicator = createJsonComponent<PreloadIndicatorProps>(prelo
|
||||
export const PWAStatusBar = createJsonComponent<PWAStatusBarProps>(pwaStatusBarDef)
|
||||
export const PWAUpdatePrompt = createJsonComponent<PWAUpdatePromptProps>(pwaUpdatePromptDef)
|
||||
export const PWAInstallPrompt = createJsonComponent<PWAInstallPromptProps>(pwaInstallPromptDef)
|
||||
export const ConflictCard = createJsonComponent<ConflictCardProps>(conflictCardDef)
|
||||
export const ConflictCard = createJsonComponentWithHooks<ConflictCardProps>(
|
||||
conflictCardDef,
|
||||
{
|
||||
hooks: {
|
||||
cardState: {
|
||||
hookName: 'useConflictCard',
|
||||
args: (props) => [props.conflict]
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
export const ConflictIndicator = createJsonComponentWithHooks<ConflictIndicatorProps>(
|
||||
conflictIndicatorDef,
|
||||
{
|
||||
@@ -926,4 +938,13 @@ export const ProjectManager = createJsonComponent<ProjectManagerProps>(projectMa
|
||||
export const StorageSettingsPanel = createJsonComponent<StorageSettingsPanelProps>(storageSettingsPanelDef)
|
||||
export const FeatureToggleSettings = createJsonComponent<FeatureToggleSettingsProps>(featureToggleSettingsDef)
|
||||
|
||||
export const DocumentationView = createJsonComponentWithHooks<DocumentationViewProps>(documentationViewDef, {
|
||||
hooks: {
|
||||
viewState: {
|
||||
hookName: 'useDocumentationView',
|
||||
args: () => []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// All components converted to pure JSON! 🎉
|
||||
|
||||
Reference in New Issue
Block a user