prjct-cli 0.18.1 → 0.19.0
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/CHANGELOG.md +53 -0
- package/CLAUDE.md +74 -211
- package/core/agentic/prompt-builder.ts +3 -7
- package/core/command-registry/optional-commands.ts +0 -20
- package/core/infrastructure/command-installer/command-installer.ts +8 -1
- package/core/infrastructure/command-installer/global-config.ts +31 -1
- package/core/infrastructure/command-installer/index.ts +1 -1
- package/core/infrastructure/setup.ts +3 -0
- package/package.json +3 -17
- package/templates/agentic/subagent-generation.md +8 -6
- package/templates/commands/done.md +57 -258
- package/templates/commands/now.md +72 -277
- package/templates/commands/ship.md +55 -261
- package/templates/commands/sync.md +7 -7
- package/templates/commands/test.md +328 -21
- package/templates/global/CLAUDE.md +40 -205
- package/templates/global/docs/agents.md +88 -0
- package/templates/global/docs/architecture.md +103 -0
- package/templates/global/docs/commands.md +98 -0
- package/templates/global/docs/validation.md +95 -0
- package/templates/mcp-config.json +36 -0
- package/bin/dev.js +0 -216
- package/bin/serve.js +0 -361
- package/packages/web/README.md +0 -36
- package/packages/web/app/api/claude/sessions/route.ts +0 -44
- package/packages/web/app/api/claude/status/route.ts +0 -34
- package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
- package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
- package/packages/web/app/api/projects/[id]/route.ts +0 -29
- package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
- package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
- package/packages/web/app/api/projects/route.ts +0 -16
- package/packages/web/app/api/sessions/current/route.ts +0 -132
- package/packages/web/app/api/sessions/history/route.ts +0 -204
- package/packages/web/app/error.tsx +0 -34
- package/packages/web/app/favicon.ico +0 -0
- package/packages/web/app/globals.css +0 -198
- package/packages/web/app/layout.tsx +0 -53
- package/packages/web/app/loading.tsx +0 -7
- package/packages/web/app/not-found.tsx +0 -25
- package/packages/web/app/page.tsx +0 -12
- package/packages/web/app/project/[id]/code/layout.tsx +0 -18
- package/packages/web/app/project/[id]/code/page.tsx +0 -408
- package/packages/web/app/project/[id]/error.tsx +0 -41
- package/packages/web/app/project/[id]/loading.tsx +0 -9
- package/packages/web/app/project/[id]/not-found.tsx +0 -27
- package/packages/web/app/project/[id]/page.tsx +0 -384
- package/packages/web/app/project/[id]/reports/page.tsx +0 -59
- package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
- package/packages/web/app/sessions/page.tsx +0 -165
- package/packages/web/app/settings/page.tsx +0 -151
- package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
- package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
- package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
- package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
- package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
- package/packages/web/components/ActivityTimeline/index.ts +0 -2
- package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
- package/packages/web/components/AgentsCard/index.ts +0 -2
- package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
- package/packages/web/components/AppSidebar/index.ts +0 -1
- package/packages/web/components/BackLink/BackLink.tsx +0 -18
- package/packages/web/components/BackLink/BackLink.types.ts +0 -5
- package/packages/web/components/BackLink/index.ts +0 -2
- package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
- package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
- package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
- package/packages/web/components/BentoCard/index.ts +0 -2
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
- package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
- package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
- package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
- package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
- package/packages/web/components/BentoGrid/index.ts +0 -2
- package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
- package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
- package/packages/web/components/BlockersCard/index.ts +0 -2
- package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
- package/packages/web/components/CommandBar/index.ts +0 -1
- package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
- package/packages/web/components/CommandButton/index.ts +0 -1
- package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
- package/packages/web/components/ConnectionStatus/index.ts +0 -1
- package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
- package/packages/web/components/DashboardContent/index.ts +0 -1
- package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
- package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
- package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
- package/packages/web/components/DateGroup/index.ts +0 -2
- package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
- package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
- package/packages/web/components/EmptyState/index.ts +0 -2
- package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
- package/packages/web/components/EventRow/EventRow.tsx +0 -49
- package/packages/web/components/EventRow/EventRow.types.ts +0 -7
- package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
- package/packages/web/components/EventRow/index.ts +0 -2
- package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
- package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
- package/packages/web/components/ExpandButton/index.ts +0 -2
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
- package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
- package/packages/web/components/HealthGradientBackground/index.ts +0 -2
- package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
- package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
- package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
- package/packages/web/components/HeroSection/hooks/index.ts +0 -2
- package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
- package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
- package/packages/web/components/HeroSection/index.ts +0 -2
- package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
- package/packages/web/components/IdeasCard/index.ts +0 -2
- package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
- package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
- package/packages/web/components/InsightMessage/index.ts +0 -2
- package/packages/web/components/Logo/Logo.tsx +0 -65
- package/packages/web/components/Logo/index.ts +0 -1
- package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
- package/packages/web/components/MarkdownContent/index.ts +0 -1
- package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
- package/packages/web/components/MasonryGrid/index.ts +0 -1
- package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
- package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
- package/packages/web/components/MomentumWidget/index.ts +0 -2
- package/packages/web/components/NowCard/NowCard.tsx +0 -118
- package/packages/web/components/NowCard/NowCard.types.ts +0 -16
- package/packages/web/components/NowCard/index.ts +0 -2
- package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
- package/packages/web/components/PageHeader/index.ts +0 -1
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
- package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
- package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
- package/packages/web/components/ProgressRing/index.ts +0 -2
- package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
- package/packages/web/components/ProjectAvatar/index.ts +0 -1
- package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
- package/packages/web/components/ProjectColorDot/index.ts +0 -1
- package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
- package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
- package/packages/web/components/Providers/Providers.tsx +0 -48
- package/packages/web/components/Providers/index.ts +0 -1
- package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
- package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
- package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
- package/packages/web/components/QueueCard/index.ts +0 -2
- package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
- package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
- package/packages/web/components/RecoverCard/index.ts +0 -2
- package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
- package/packages/web/components/RoadmapCard/index.ts +0 -2
- package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
- package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
- package/packages/web/components/ShipsCard/index.ts +0 -2
- package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
- package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
- package/packages/web/components/SparklineChart/index.ts +0 -2
- package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
- package/packages/web/components/StatsMasonry/index.ts +0 -1
- package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
- package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
- package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
- package/packages/web/components/StreakCard/index.ts +0 -2
- package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
- package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
- package/packages/web/components/TasksCounter/index.ts +0 -2
- package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
- package/packages/web/components/TechStackBadges/index.ts +0 -1
- package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
- package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
- package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
- package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
- package/packages/web/components/TerminalDock/index.ts +0 -2
- package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
- package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
- package/packages/web/components/TerminalTabs/index.ts +0 -1
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
- package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
- package/packages/web/components/VelocityBadge/index.ts +0 -2
- package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
- package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
- package/packages/web/components/VelocityCard/index.ts +0 -2
- package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
- package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
- package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
- package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
- package/packages/web/components/WeeklyReports/index.ts +0 -4
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
- package/packages/web/components/WeeklySparkline/index.ts +0 -2
- package/packages/web/components/charts/SessionsChart.tsx +0 -175
- package/packages/web/components/ui/alert-dialog.tsx +0 -157
- package/packages/web/components/ui/badge.tsx +0 -46
- package/packages/web/components/ui/button.tsx +0 -60
- package/packages/web/components/ui/card.tsx +0 -92
- package/packages/web/components/ui/chart.tsx +0 -385
- package/packages/web/components/ui/dialog.tsx +0 -143
- package/packages/web/components/ui/drawer.tsx +0 -135
- package/packages/web/components/ui/dropdown-menu.tsx +0 -257
- package/packages/web/components/ui/input.tsx +0 -21
- package/packages/web/components/ui/scroll-area.tsx +0 -58
- package/packages/web/components/ui/select.tsx +0 -187
- package/packages/web/components/ui/sheet.tsx +0 -139
- package/packages/web/components/ui/tabs.tsx +0 -66
- package/packages/web/components/ui/tooltip.tsx +0 -61
- package/packages/web/components.json +0 -22
- package/packages/web/context/GlobalTerminalContext.tsx +0 -538
- package/packages/web/context/TerminalContext.tsx +0 -45
- package/packages/web/context/TerminalTabsContext.tsx +0 -181
- package/packages/web/eslint.config.mjs +0 -18
- package/packages/web/hooks/useClaudeTerminal.ts +0 -425
- package/packages/web/hooks/useProjectStats.ts +0 -93
- package/packages/web/hooks/useProjects.ts +0 -73
- package/packages/web/lib/actions/projects.ts +0 -15
- package/packages/web/lib/commands.ts +0 -81
- package/packages/web/lib/format.ts +0 -23
- package/packages/web/lib/generate-week-report.ts +0 -285
- package/packages/web/lib/parse-prjct-files.ts +0 -1123
- package/packages/web/lib/project-colors.ts +0 -58
- package/packages/web/lib/projects.ts +0 -506
- package/packages/web/lib/pty.ts +0 -101
- package/packages/web/lib/query-config.ts +0 -44
- package/packages/web/lib/services/index.ts +0 -9
- package/packages/web/lib/services/projects.server.ts +0 -66
- package/packages/web/lib/services/stats.server.ts +0 -562
- package/packages/web/lib/unified-loader.ts +0 -396
- package/packages/web/lib/utils.ts +0 -6
- package/packages/web/next-env.d.ts +0 -6
- package/packages/web/next.config.ts +0 -7
- package/packages/web/package.json +0 -57
- package/packages/web/postcss.config.mjs +0 -7
- package/packages/web/public/file.svg +0 -1
- package/packages/web/public/globe.svg +0 -1
- package/packages/web/public/next.svg +0 -1
- package/packages/web/public/vercel.svg +0 -1
- package/packages/web/public/window.svg +0 -1
- package/packages/web/server.ts +0 -312
- package/packages/web/tsconfig.json +0 -34
- package/templates/commands/serve.md +0 -121
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState, useCallback, useTransition } from 'react'
|
|
4
|
-
import Link from 'next/link'
|
|
5
|
-
import { useRouter } from 'next/navigation'
|
|
6
|
-
import { SessionsChart } from '@/components/charts/SessionsChart'
|
|
7
|
-
import { Button } from '@/components/ui/button'
|
|
8
|
-
import { ProgressRing } from '@/components/ProgressRing'
|
|
9
|
-
import { TechStackBadges } from '@/components/TechStackBadges'
|
|
10
|
-
import { formatRelativeTime, formatPath } from '@/lib/format'
|
|
11
|
-
import {
|
|
12
|
-
MoreHorizontal,
|
|
13
|
-
Trash2,
|
|
14
|
-
AlertTriangle,
|
|
15
|
-
Target,
|
|
16
|
-
Lightbulb,
|
|
17
|
-
ListTodo,
|
|
18
|
-
Zap,
|
|
19
|
-
FolderGit2,
|
|
20
|
-
Play,
|
|
21
|
-
FileText
|
|
22
|
-
} from 'lucide-react'
|
|
23
|
-
import {
|
|
24
|
-
DropdownMenu,
|
|
25
|
-
DropdownMenuContent,
|
|
26
|
-
DropdownMenuItem,
|
|
27
|
-
DropdownMenuTrigger,
|
|
28
|
-
} from '@/components/ui/dropdown-menu'
|
|
29
|
-
import {
|
|
30
|
-
AlertDialog,
|
|
31
|
-
AlertDialogAction,
|
|
32
|
-
AlertDialogCancel,
|
|
33
|
-
AlertDialogContent,
|
|
34
|
-
AlertDialogDescription,
|
|
35
|
-
AlertDialogFooter,
|
|
36
|
-
AlertDialogHeader,
|
|
37
|
-
AlertDialogTitle,
|
|
38
|
-
} from '@/components/ui/alert-dialog'
|
|
39
|
-
import { deleteProject } from '@/lib/actions/projects'
|
|
40
|
-
|
|
41
|
-
// Traffic light colors based on completion rate
|
|
42
|
-
type AccentColor = 'default' | 'success' | 'warning' | 'destructive'
|
|
43
|
-
function getCompletionColor(rate: number): AccentColor {
|
|
44
|
-
if (rate >= 75) return 'success'
|
|
45
|
-
if (rate >= 25) return 'warning'
|
|
46
|
-
return 'destructive'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface Project {
|
|
50
|
-
id: string
|
|
51
|
-
name: string
|
|
52
|
-
path: string
|
|
53
|
-
repoPath?: string | null
|
|
54
|
-
currentTask?: string | null
|
|
55
|
-
hasActiveSession?: boolean
|
|
56
|
-
lastActivity?: string | null
|
|
57
|
-
ideasCount?: number
|
|
58
|
-
nextTasksCount?: number
|
|
59
|
-
shippedCount?: number
|
|
60
|
-
techStack?: string[]
|
|
61
|
-
iconPath?: string | null
|
|
62
|
-
version?: string
|
|
63
|
-
stack?: string
|
|
64
|
-
filesCount?: number
|
|
65
|
-
commitsCount?: number
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface GlobalStats {
|
|
69
|
-
userName: string
|
|
70
|
-
totalProjects: number
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
interface DashboardContentProps {
|
|
74
|
-
projects: Project[]
|
|
75
|
-
stats: GlobalStats
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function DashboardContent({ projects, stats }: DashboardContentProps) {
|
|
79
|
-
const [projectToDelete, setProjectToDelete] = useState<Project | null>(null)
|
|
80
|
-
const [isPending, startTransition] = useTransition()
|
|
81
|
-
const router = useRouter()
|
|
82
|
-
|
|
83
|
-
const handleDeleteClick = useCallback((project: Project, e: React.MouseEvent) => {
|
|
84
|
-
e.preventDefault()
|
|
85
|
-
e.stopPropagation()
|
|
86
|
-
setProjectToDelete(project)
|
|
87
|
-
}, [])
|
|
88
|
-
|
|
89
|
-
const handleConfirmDelete = useCallback(() => {
|
|
90
|
-
if (projectToDelete) {
|
|
91
|
-
startTransition(async () => {
|
|
92
|
-
await deleteProject(projectToDelete.id)
|
|
93
|
-
setProjectToDelete(null)
|
|
94
|
-
router.refresh()
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
}, [projectToDelete, router])
|
|
98
|
-
|
|
99
|
-
const projectCount = projects?.length || 0
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<div className="p-4 sm:p-6 md:p-8 h-full overflow-auto">
|
|
103
|
-
{/* Mobile: Add padding for hamburger menu */}
|
|
104
|
-
<div className="pl-10 md:pl-0">
|
|
105
|
-
<p className="text-sm sm:text-base text-muted-foreground">Hola! {stats?.userName || 'Developer'}</p>
|
|
106
|
-
|
|
107
|
-
<h1 className="text-6xl sm:text-7xl md:text-8xl font-bold tracking-tighter tabular-nums mt-2">{projectCount}</h1>
|
|
108
|
-
<p className="text-sm sm:text-base text-muted-foreground mt-1">{projectCount === 1 ? 'project' : 'projects'}</p>
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
<section className="mt-6 sm:mt-8">
|
|
112
|
-
<SessionsChart />
|
|
113
|
-
</section>
|
|
114
|
-
|
|
115
|
-
<section className="mt-8 sm:mt-10 md:mt-12">
|
|
116
|
-
<h2 className="font-bold uppercase tracking-wide text-muted-foreground mb-4 sm:mb-6 text-sm">Active Projects</h2>
|
|
117
|
-
|
|
118
|
-
{projects?.length === 0 ? (
|
|
119
|
-
<div className="border-2 border-dashed border-border rounded-xl p-6 sm:p-8 text-center">
|
|
120
|
-
<p className="text-muted-foreground text-sm sm:text-base">
|
|
121
|
-
No projects yet. Initialize with <code className="bg-muted px-2 py-1 rounded font-mono text-xs sm:text-sm">/p:init</code>
|
|
122
|
-
</p>
|
|
123
|
-
</div>
|
|
124
|
-
) : (
|
|
125
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
|
|
126
|
-
{projects?.map((project: Project) => (
|
|
127
|
-
<ProjectCard key={project.id} project={project} onDeleteClick={handleDeleteClick} />
|
|
128
|
-
))}
|
|
129
|
-
</div>
|
|
130
|
-
)}
|
|
131
|
-
</section>
|
|
132
|
-
|
|
133
|
-
<AlertDialog open={!!projectToDelete} onOpenChange={(open: boolean) => !open && setProjectToDelete(null)}>
|
|
134
|
-
<AlertDialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-lg">
|
|
135
|
-
<AlertDialogHeader>
|
|
136
|
-
<AlertDialogTitle className="flex items-center gap-2">
|
|
137
|
-
<AlertTriangle className="w-5 h-5 text-destructive shrink-0" />
|
|
138
|
-
Delete Project?
|
|
139
|
-
</AlertDialogTitle>
|
|
140
|
-
<AlertDialogDescription>
|
|
141
|
-
<strong>"{projectToDelete?.name}"</strong> will be moved to trash.
|
|
142
|
-
<br />
|
|
143
|
-
<span className="text-muted-foreground text-xs sm:text-sm break-all">
|
|
144
|
-
Location: ~/.prjct-cli/.trash/{projectToDelete?.id}
|
|
145
|
-
</span>
|
|
146
|
-
</AlertDialogDescription>
|
|
147
|
-
</AlertDialogHeader>
|
|
148
|
-
<AlertDialogFooter className="flex-col sm:flex-row gap-2">
|
|
149
|
-
<AlertDialogCancel disabled={isPending} className="w-full sm:w-auto">Cancel</AlertDialogCancel>
|
|
150
|
-
<AlertDialogAction
|
|
151
|
-
onClick={handleConfirmDelete}
|
|
152
|
-
disabled={isPending}
|
|
153
|
-
className="bg-destructive text-destructive-foreground hover:bg-destructive/90 w-full sm:w-auto"
|
|
154
|
-
>
|
|
155
|
-
{isPending ? 'Deleting...' : 'Delete'}
|
|
156
|
-
</AlertDialogAction>
|
|
157
|
-
</AlertDialogFooter>
|
|
158
|
-
</AlertDialogContent>
|
|
159
|
-
</AlertDialog>
|
|
160
|
-
|
|
161
|
-
{/* Bottom safe area padding */}
|
|
162
|
-
<div className="h-6 sm:h-8" />
|
|
163
|
-
</div>
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
interface ProjectCardProps {
|
|
168
|
-
project: Project
|
|
169
|
-
onDeleteClick: (project: Project, e: React.MouseEvent) => void
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function ProjectCard({ project, onDeleteClick }: ProjectCardProps) {
|
|
173
|
-
const hasStats = project.currentTask || project.nextTasksCount || project.ideasCount || project.lastActivity
|
|
174
|
-
|
|
175
|
-
// Calculate completion rate
|
|
176
|
-
const shipped = project.shippedCount ?? 0
|
|
177
|
-
const pending = project.nextTasksCount ?? 0
|
|
178
|
-
const total = shipped + pending
|
|
179
|
-
const completionRate = total > 0 ? Math.round((shipped / total) * 100) : 0
|
|
180
|
-
const completionColor = getCompletionColor(completionRate)
|
|
181
|
-
|
|
182
|
-
return (
|
|
183
|
-
<div className="group relative bg-card border border-border rounded-lg overflow-hidden hover:border-primary/50 active:scale-[0.99] transition-all">
|
|
184
|
-
{project.hasActiveSession && <div className="absolute top-0 left-0 right-0 h-0.5 bg-green-500" />}
|
|
185
|
-
|
|
186
|
-
<Link href={`/project/${project.id}`} className="block p-3 sm:p-4">
|
|
187
|
-
<div className="flex items-start gap-3">
|
|
188
|
-
<ProgressRing value={completionRate} size="md" accentColor={completionColor} className="shrink-0" />
|
|
189
|
-
|
|
190
|
-
<div className="flex-1 min-w-0">
|
|
191
|
-
<div className="flex items-center gap-2">
|
|
192
|
-
<h3 className="font-bold truncate text-sm sm:text-base">{project.name}</h3>
|
|
193
|
-
{project.hasActiveSession && (
|
|
194
|
-
<span className="flex h-2 w-2 shrink-0">
|
|
195
|
-
<span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-green-400 opacity-75" />
|
|
196
|
-
<span className="relative inline-flex rounded-full h-2 w-2 bg-green-500" />
|
|
197
|
-
</span>
|
|
198
|
-
)}
|
|
199
|
-
</div>
|
|
200
|
-
{project.repoPath && (
|
|
201
|
-
<p className="text-xs text-muted-foreground truncate mt-0.5 flex items-center gap-1">
|
|
202
|
-
<FolderGit2 className="w-3 h-3 shrink-0" />
|
|
203
|
-
{formatPath(project.repoPath)}
|
|
204
|
-
</p>
|
|
205
|
-
)}
|
|
206
|
-
</div>
|
|
207
|
-
|
|
208
|
-
{/* Mobile: Always show menu button */}
|
|
209
|
-
<DropdownMenu>
|
|
210
|
-
<DropdownMenuTrigger asChild>
|
|
211
|
-
<Button
|
|
212
|
-
variant="ghost"
|
|
213
|
-
size="icon-sm"
|
|
214
|
-
className="shrink-0 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity"
|
|
215
|
-
onClick={(e) => e.preventDefault()}
|
|
216
|
-
>
|
|
217
|
-
<MoreHorizontal className="w-4 h-4" />
|
|
218
|
-
</Button>
|
|
219
|
-
</DropdownMenuTrigger>
|
|
220
|
-
<DropdownMenuContent align="end">
|
|
221
|
-
<DropdownMenuItem asChild className="min-h-[44px] sm:min-h-0">
|
|
222
|
-
<Link href={`/project/${project.id}/code`}>
|
|
223
|
-
<Play className="w-4 h-4 mr-2" />
|
|
224
|
-
Start working
|
|
225
|
-
</Link>
|
|
226
|
-
</DropdownMenuItem>
|
|
227
|
-
<DropdownMenuItem asChild className="min-h-[44px] sm:min-h-0">
|
|
228
|
-
<Link href={`/project/${project.id}/reports`}>
|
|
229
|
-
<FileText className="w-4 h-4 mr-2" />
|
|
230
|
-
Reports
|
|
231
|
-
</Link>
|
|
232
|
-
</DropdownMenuItem>
|
|
233
|
-
<DropdownMenuItem
|
|
234
|
-
className="text-destructive focus:text-destructive min-h-[44px] sm:min-h-0"
|
|
235
|
-
onClick={(e: React.MouseEvent) => onDeleteClick(project, e)}
|
|
236
|
-
>
|
|
237
|
-
<Trash2 className="w-4 h-4 mr-2" />
|
|
238
|
-
Delete
|
|
239
|
-
</DropdownMenuItem>
|
|
240
|
-
</DropdownMenuContent>
|
|
241
|
-
</DropdownMenu>
|
|
242
|
-
</div>
|
|
243
|
-
|
|
244
|
-
{project.currentTask && (
|
|
245
|
-
<div className="mt-3 p-2 bg-primary/5 rounded border border-primary/10">
|
|
246
|
-
<div className="flex items-start gap-2">
|
|
247
|
-
<Target className="w-3.5 h-3.5 text-primary shrink-0 mt-0.5" />
|
|
248
|
-
<span className="text-xs sm:text-sm text-foreground line-clamp-2">{project.currentTask}</span>
|
|
249
|
-
</div>
|
|
250
|
-
</div>
|
|
251
|
-
)}
|
|
252
|
-
|
|
253
|
-
{hasStats && (
|
|
254
|
-
<div className="mt-3 flex flex-wrap items-center gap-2 sm:gap-3 text-xs text-muted-foreground">
|
|
255
|
-
{(project.nextTasksCount ?? 0) > 0 && (
|
|
256
|
-
<div className="flex items-center gap-1">
|
|
257
|
-
<ListTodo className="w-3.5 h-3.5" />
|
|
258
|
-
<span>{project.nextTasksCount} queued</span>
|
|
259
|
-
</div>
|
|
260
|
-
)}
|
|
261
|
-
{(project.ideasCount ?? 0) > 0 && (
|
|
262
|
-
<div className="flex items-center gap-1">
|
|
263
|
-
<Lightbulb className="w-3.5 h-3.5" />
|
|
264
|
-
<span>{project.ideasCount} ideas</span>
|
|
265
|
-
</div>
|
|
266
|
-
)}
|
|
267
|
-
{project.lastActivity && (
|
|
268
|
-
<div className="flex items-center gap-1 ml-auto">
|
|
269
|
-
<Zap className="w-3.5 h-3.5" />
|
|
270
|
-
<span>{formatRelativeTime(project.lastActivity)}</span>
|
|
271
|
-
</div>
|
|
272
|
-
)}
|
|
273
|
-
</div>
|
|
274
|
-
)}
|
|
275
|
-
|
|
276
|
-
{project.techStack && project.techStack.length > 0 && (
|
|
277
|
-
<div className="mt-3">
|
|
278
|
-
<TechStackBadges techStack={project.techStack} />
|
|
279
|
-
</div>
|
|
280
|
-
)}
|
|
281
|
-
</Link>
|
|
282
|
-
</div>
|
|
283
|
-
)
|
|
284
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { DashboardContent } from './DashboardContent'
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { EventRow } from '@/components/EventRow'
|
|
2
|
-
import { formatTimelineDate } from './DateGroup.utils'
|
|
3
|
-
import type { DateGroupProps } from './DateGroup.types'
|
|
4
|
-
|
|
5
|
-
export function DateGroup({ dateKey, events }: DateGroupProps) {
|
|
6
|
-
return (
|
|
7
|
-
<div>
|
|
8
|
-
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-2">
|
|
9
|
-
{formatTimelineDate(dateKey + 'T00:00:00')}
|
|
10
|
-
</p>
|
|
11
|
-
<div className="space-y-1">
|
|
12
|
-
{events.map((event, i) => (
|
|
13
|
-
<EventRow key={i} event={event} />
|
|
14
|
-
))}
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export function formatTimelineDate(dateString: string): string {
|
|
2
|
-
const date = new Date(dateString)
|
|
3
|
-
const today = new Date()
|
|
4
|
-
const yesterday = new Date(today)
|
|
5
|
-
yesterday.setDate(yesterday.getDate() - 1)
|
|
6
|
-
|
|
7
|
-
if (date.toDateString() === today.toDateString()) return 'Today'
|
|
8
|
-
if (date.toDateString() === yesterday.toDateString()) return 'Yesterday'
|
|
9
|
-
|
|
10
|
-
return date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })
|
|
11
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link'
|
|
4
|
-
import { cn } from '@/lib/utils'
|
|
5
|
-
import { ArrowRight } from 'lucide-react'
|
|
6
|
-
import type { EmptyStateProps } from './EmptyState.types'
|
|
7
|
-
|
|
8
|
-
export function EmptyState({
|
|
9
|
-
icon: Icon,
|
|
10
|
-
title,
|
|
11
|
-
description,
|
|
12
|
-
command,
|
|
13
|
-
href,
|
|
14
|
-
className,
|
|
15
|
-
compact = false,
|
|
16
|
-
}: EmptyStateProps) {
|
|
17
|
-
// Command button content
|
|
18
|
-
const CommandButton = href ? (
|
|
19
|
-
<Link
|
|
20
|
-
href={href}
|
|
21
|
-
className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-emerald-500/10 text-xs font-mono text-emerald-600 dark:text-emerald-400 hover:bg-emerald-500/20 transition-colors group border border-emerald-500/20"
|
|
22
|
-
>
|
|
23
|
-
{command}
|
|
24
|
-
<ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
|
|
25
|
-
</Link>
|
|
26
|
-
) : command ? (
|
|
27
|
-
<button
|
|
28
|
-
onClick={() => navigator.clipboard.writeText(command)}
|
|
29
|
-
className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted text-xs font-mono hover:bg-muted/80"
|
|
30
|
-
>
|
|
31
|
-
{command}
|
|
32
|
-
</button>
|
|
33
|
-
) : null
|
|
34
|
-
|
|
35
|
-
if (compact) {
|
|
36
|
-
return (
|
|
37
|
-
<div className={cn('flex items-center gap-2 text-muted-foreground', className)}>
|
|
38
|
-
<Icon className="h-4 w-4" />
|
|
39
|
-
<span className="text-sm">{title}</span>
|
|
40
|
-
{CommandButton}
|
|
41
|
-
</div>
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<div className={cn('flex flex-col items-center justify-center text-center py-4', className)}>
|
|
47
|
-
<div className="w-10 h-10 rounded-full bg-muted flex items-center justify-center mb-3">
|
|
48
|
-
<Icon className="h-5 w-5 text-muted-foreground" />
|
|
49
|
-
</div>
|
|
50
|
-
<p className="text-sm font-medium text-muted-foreground">{title}</p>
|
|
51
|
-
{description && (
|
|
52
|
-
<p className="text-xs text-muted-foreground/70 mt-1">{description}</p>
|
|
53
|
-
)}
|
|
54
|
-
{command && (
|
|
55
|
-
<div className="mt-2">
|
|
56
|
-
{href ? (
|
|
57
|
-
<Link
|
|
58
|
-
href={href}
|
|
59
|
-
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-emerald-500/10 text-xs font-mono text-emerald-600 dark:text-emerald-400 hover:bg-emerald-500/20 transition-colors group border border-emerald-500/20"
|
|
60
|
-
>
|
|
61
|
-
{command}
|
|
62
|
-
<ArrowRight className="h-3 w-3 group-hover:translate-x-0.5 transition-transform" />
|
|
63
|
-
</Link>
|
|
64
|
-
) : (
|
|
65
|
-
<button
|
|
66
|
-
onClick={() => navigator.clipboard.writeText(command)}
|
|
67
|
-
className="inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-muted text-xs font-mono text-muted-foreground hover:bg-muted/80 hover:text-foreground transition-colors"
|
|
68
|
-
>
|
|
69
|
-
{command}
|
|
70
|
-
</button>
|
|
71
|
-
)}
|
|
72
|
-
</div>
|
|
73
|
-
)}
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Activity, CheckCircle2, Rocket, Target, RefreshCw } from 'lucide-react'
|
|
2
|
-
import type { EventIconName } from './EventRow.types'
|
|
3
|
-
|
|
4
|
-
export const EVENT_ICON_MAP: Record<EventIconName, typeof Activity> = {
|
|
5
|
-
check: CheckCircle2,
|
|
6
|
-
target: Target,
|
|
7
|
-
rocket: Rocket,
|
|
8
|
-
refresh: RefreshCw,
|
|
9
|
-
activity: Activity,
|
|
10
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { cn } from '@/lib/utils'
|
|
2
|
-
import { EVENT_ICON_MAP } from './EventRow.constants'
|
|
3
|
-
import {
|
|
4
|
-
formatTime,
|
|
5
|
-
getEventIconName,
|
|
6
|
-
getEventColor,
|
|
7
|
-
getEventBadge,
|
|
8
|
-
getEventLabel,
|
|
9
|
-
} from './EventRow.utils'
|
|
10
|
-
import type { EventRowProps } from './EventRow.types'
|
|
11
|
-
|
|
12
|
-
export function EventRow({ event }: EventRowProps) {
|
|
13
|
-
const iconName = getEventIconName(event.type)
|
|
14
|
-
const Icon = EVENT_ICON_MAP[iconName]
|
|
15
|
-
const duration = 'duration' in event && typeof event.duration === 'string' ? event.duration : null
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<div className="flex items-center gap-2 sm:gap-3 py-2 sm:py-1.5 px-2 -mx-2 rounded-md hover:bg-muted/50 active:bg-muted/70 transition-colors group">
|
|
19
|
-
{event.ts && (
|
|
20
|
-
<span className="hidden sm:block text-xs text-muted-foreground w-14 shrink-0 tabular-nums">
|
|
21
|
-
{formatTime(event.ts)}
|
|
22
|
-
</span>
|
|
23
|
-
)}
|
|
24
|
-
|
|
25
|
-
<Icon className={cn('h-4 w-4 sm:h-3.5 sm:w-3.5 shrink-0', getEventColor(event.type))} />
|
|
26
|
-
|
|
27
|
-
<div className="flex-1 min-w-0">
|
|
28
|
-
<span className="text-sm truncate block group-hover:text-foreground transition-colors">
|
|
29
|
-
{getEventLabel(event)}
|
|
30
|
-
</span>
|
|
31
|
-
{event.ts && (
|
|
32
|
-
<span className="sm:hidden text-xs text-muted-foreground">
|
|
33
|
-
{formatTime(event.ts)}
|
|
34
|
-
</span>
|
|
35
|
-
)}
|
|
36
|
-
</div>
|
|
37
|
-
|
|
38
|
-
<span className="text-xs font-bold tracking-wider text-muted-foreground shrink-0">
|
|
39
|
-
{getEventBadge(event.type)}
|
|
40
|
-
</span>
|
|
41
|
-
|
|
42
|
-
{duration && (
|
|
43
|
-
<span className="text-xs text-muted-foreground shrink-0">
|
|
44
|
-
{duration}
|
|
45
|
-
</span>
|
|
46
|
-
)}
|
|
47
|
-
</div>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { EventIconName } from './EventRow.types'
|
|
2
|
-
|
|
3
|
-
export function formatTime(dateString: string): string {
|
|
4
|
-
const date = new Date(dateString)
|
|
5
|
-
return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function getEventIconName(type: string): EventIconName {
|
|
9
|
-
const iconMap: Record<string, EventIconName> = {
|
|
10
|
-
task_complete: 'check',
|
|
11
|
-
task_start: 'target',
|
|
12
|
-
feature_ship: 'rocket',
|
|
13
|
-
sync: 'refresh',
|
|
14
|
-
}
|
|
15
|
-
return iconMap[type] ?? 'activity'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getEventColor(type: string): string {
|
|
19
|
-
const colorMap: Record<string, string> = {
|
|
20
|
-
task_complete: 'text-muted-foreground',
|
|
21
|
-
task_start: 'text-muted-foreground',
|
|
22
|
-
feature_ship: 'text-muted-foreground',
|
|
23
|
-
sync: 'text-muted-foreground',
|
|
24
|
-
}
|
|
25
|
-
return colorMap[type] ?? 'text-muted-foreground'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function getEventBadge(type: string): string {
|
|
29
|
-
const badgeMap: Record<string, string> = {
|
|
30
|
-
task_complete: 'DONE',
|
|
31
|
-
task_start: 'START',
|
|
32
|
-
feature_ship: 'SHIP',
|
|
33
|
-
sync: 'SYNC',
|
|
34
|
-
}
|
|
35
|
-
return badgeMap[type] ?? type.toUpperCase()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function getEventLabel(event: unknown): string {
|
|
39
|
-
const e = event as { type?: string; task?: string; name?: string }
|
|
40
|
-
const type = e.type ?? ''
|
|
41
|
-
|
|
42
|
-
const labelMap: Record<string, string> = {
|
|
43
|
-
task_complete: e.task ?? 'Task completed',
|
|
44
|
-
task_start: e.task ?? 'Task started',
|
|
45
|
-
feature_ship: e.name ?? 'Feature shipped',
|
|
46
|
-
sync: 'Project synced',
|
|
47
|
-
}
|
|
48
|
-
return labelMap[type] ?? type
|
|
49
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ChevronDown } from 'lucide-react'
|
|
2
|
-
import { cn } from '@/lib/utils'
|
|
3
|
-
import { Button } from '@/components/ui/button'
|
|
4
|
-
import type { ExpandButtonProps } from './ExpandButton.types'
|
|
5
|
-
|
|
6
|
-
export function ExpandButton({ expanded, totalCount, collapsedLimit, onToggle }: ExpandButtonProps) {
|
|
7
|
-
return (
|
|
8
|
-
<Button
|
|
9
|
-
variant="ghost"
|
|
10
|
-
size="sm"
|
|
11
|
-
onClick={onToggle}
|
|
12
|
-
className="w-full text-muted-foreground min-h-[44px]"
|
|
13
|
-
>
|
|
14
|
-
<ChevronDown className={cn('h-4 w-4 mr-1 transition-transform', expanded && 'rotate-180')} />
|
|
15
|
-
{expanded ? 'Show less' : `Show ${totalCount - collapsedLimit} more`}
|
|
16
|
-
</Button>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { cn } from '@/lib/utils'
|
|
2
|
-
import { getHealthGradient } from './HealthGradientBackground.utils'
|
|
3
|
-
import type { HealthGradientBackgroundProps } from './HealthGradientBackground.types'
|
|
4
|
-
|
|
5
|
-
export function HealthGradientBackground({ score }: HealthGradientBackgroundProps) {
|
|
6
|
-
return (
|
|
7
|
-
<div
|
|
8
|
-
className={cn(
|
|
9
|
-
'absolute inset-0 -m-4 md:-m-8 rounded-2xl opacity-30 blur-3xl transition-colors duration-1000',
|
|
10
|
-
getHealthGradient(score)
|
|
11
|
-
)}
|
|
12
|
-
/>
|
|
13
|
-
)
|
|
14
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { AccentColor } from './HealthGradientBackground.types'
|
|
2
|
-
|
|
3
|
-
export function getHealthColor(score: number): AccentColor {
|
|
4
|
-
if (score >= 70) return 'success'
|
|
5
|
-
if (score >= 40) return 'warning'
|
|
6
|
-
return 'destructive'
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function getHealthGradient(score: number): string {
|
|
10
|
-
if (score >= 70) return 'bg-gradient-to-br from-emerald-500/20 to-transparent'
|
|
11
|
-
if (score >= 40) return 'bg-gradient-to-br from-amber-500/20 to-transparent'
|
|
12
|
-
return 'bg-gradient-to-br from-destructive/20 to-transparent'
|
|
13
|
-
}
|