prjct-cli 0.18.2 → 0.20.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.
Files changed (246) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/CLAUDE.md +74 -211
  3. package/core/agentic/prompt-builder.ts +3 -7
  4. package/core/command-registry/optional-commands.ts +0 -20
  5. package/core/infrastructure/command-installer/command-installer.ts +8 -1
  6. package/core/infrastructure/command-installer/global-config.ts +31 -1
  7. package/core/infrastructure/command-installer/index.ts +1 -1
  8. package/core/infrastructure/setup.ts +3 -0
  9. package/package.json +3 -17
  10. package/templates/agentic/agents/uxui.md +210 -0
  11. package/templates/commands/bug.md +219 -41
  12. package/templates/commands/done.md +57 -258
  13. package/templates/commands/feature.md +368 -80
  14. package/templates/commands/now.md +72 -277
  15. package/templates/commands/ship.md +167 -246
  16. package/templates/commands/sync.md +62 -3
  17. package/templates/commands/test.md +160 -20
  18. package/templates/global/CLAUDE.md +40 -205
  19. package/templates/global/docs/agents.md +88 -0
  20. package/templates/global/docs/architecture.md +103 -0
  21. package/templates/global/docs/commands.md +98 -0
  22. package/templates/global/docs/validation.md +95 -0
  23. package/bin/dev.js +0 -216
  24. package/bin/serve.js +0 -361
  25. package/packages/web/README.md +0 -36
  26. package/packages/web/app/api/claude/sessions/route.ts +0 -44
  27. package/packages/web/app/api/claude/status/route.ts +0 -34
  28. package/packages/web/app/api/projects/[id]/icon/route.ts +0 -33
  29. package/packages/web/app/api/projects/[id]/momentum/route.ts +0 -257
  30. package/packages/web/app/api/projects/[id]/route.ts +0 -29
  31. package/packages/web/app/api/projects/[id]/stats/route.ts +0 -41
  32. package/packages/web/app/api/projects/[id]/status/route.ts +0 -21
  33. package/packages/web/app/api/projects/route.ts +0 -16
  34. package/packages/web/app/api/sessions/current/route.ts +0 -132
  35. package/packages/web/app/api/sessions/history/route.ts +0 -204
  36. package/packages/web/app/error.tsx +0 -34
  37. package/packages/web/app/favicon.ico +0 -0
  38. package/packages/web/app/globals.css +0 -198
  39. package/packages/web/app/layout.tsx +0 -53
  40. package/packages/web/app/loading.tsx +0 -7
  41. package/packages/web/app/not-found.tsx +0 -25
  42. package/packages/web/app/page.tsx +0 -12
  43. package/packages/web/app/project/[id]/code/layout.tsx +0 -18
  44. package/packages/web/app/project/[id]/code/page.tsx +0 -408
  45. package/packages/web/app/project/[id]/error.tsx +0 -41
  46. package/packages/web/app/project/[id]/loading.tsx +0 -9
  47. package/packages/web/app/project/[id]/not-found.tsx +0 -27
  48. package/packages/web/app/project/[id]/page.tsx +0 -384
  49. package/packages/web/app/project/[id]/reports/page.tsx +0 -59
  50. package/packages/web/app/project/[id]/reports/print/page.tsx +0 -58
  51. package/packages/web/app/sessions/page.tsx +0 -165
  52. package/packages/web/app/settings/page.tsx +0 -151
  53. package/packages/web/components/ActivityTimeline/ActivityTimeline.constants.ts +0 -2
  54. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -49
  55. package/packages/web/components/ActivityTimeline/ActivityTimeline.types.ts +0 -8
  56. package/packages/web/components/ActivityTimeline/hooks/index.ts +0 -2
  57. package/packages/web/components/ActivityTimeline/hooks/useExpandable.ts +0 -9
  58. package/packages/web/components/ActivityTimeline/hooks/useGroupedEvents.ts +0 -23
  59. package/packages/web/components/ActivityTimeline/index.ts +0 -2
  60. package/packages/web/components/AgentsCard/AgentsCard.tsx +0 -93
  61. package/packages/web/components/AgentsCard/AgentsCard.types.ts +0 -14
  62. package/packages/web/components/AgentsCard/index.ts +0 -2
  63. package/packages/web/components/AppSidebar/AppSidebar.tsx +0 -316
  64. package/packages/web/components/AppSidebar/index.ts +0 -1
  65. package/packages/web/components/BackLink/BackLink.tsx +0 -18
  66. package/packages/web/components/BackLink/BackLink.types.ts +0 -5
  67. package/packages/web/components/BackLink/index.ts +0 -2
  68. package/packages/web/components/BentoCard/BentoCard.constants.ts +0 -16
  69. package/packages/web/components/BentoCard/BentoCard.tsx +0 -48
  70. package/packages/web/components/BentoCard/BentoCard.types.ts +0 -15
  71. package/packages/web/components/BentoCard/index.ts +0 -2
  72. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.constants.ts +0 -9
  73. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.tsx +0 -18
  74. package/packages/web/components/BentoCardSkeleton/BentoCardSkeleton.types.ts +0 -5
  75. package/packages/web/components/BentoCardSkeleton/index.ts +0 -2
  76. package/packages/web/components/BentoGrid/BentoGrid.tsx +0 -18
  77. package/packages/web/components/BentoGrid/BentoGrid.types.ts +0 -4
  78. package/packages/web/components/BentoGrid/index.ts +0 -2
  79. package/packages/web/components/BlockersCard/BlockersCard.tsx +0 -75
  80. package/packages/web/components/BlockersCard/BlockersCard.types.ts +0 -12
  81. package/packages/web/components/BlockersCard/index.ts +0 -2
  82. package/packages/web/components/CommandBar/CommandBar.tsx +0 -67
  83. package/packages/web/components/CommandBar/index.ts +0 -1
  84. package/packages/web/components/CommandButton/CommandButton.tsx +0 -46
  85. package/packages/web/components/CommandButton/index.ts +0 -1
  86. package/packages/web/components/ConnectionStatus/ConnectionStatus.tsx +0 -29
  87. package/packages/web/components/ConnectionStatus/index.ts +0 -1
  88. package/packages/web/components/DashboardContent/DashboardContent.tsx +0 -284
  89. package/packages/web/components/DashboardContent/index.ts +0 -1
  90. package/packages/web/components/DateGroup/DateGroup.tsx +0 -18
  91. package/packages/web/components/DateGroup/DateGroup.types.ts +0 -6
  92. package/packages/web/components/DateGroup/DateGroup.utils.ts +0 -11
  93. package/packages/web/components/DateGroup/index.ts +0 -2
  94. package/packages/web/components/EmptyState/EmptyState.tsx +0 -76
  95. package/packages/web/components/EmptyState/EmptyState.types.ts +0 -11
  96. package/packages/web/components/EmptyState/index.ts +0 -2
  97. package/packages/web/components/EventRow/EventRow.constants.ts +0 -10
  98. package/packages/web/components/EventRow/EventRow.tsx +0 -49
  99. package/packages/web/components/EventRow/EventRow.types.ts +0 -7
  100. package/packages/web/components/EventRow/EventRow.utils.ts +0 -49
  101. package/packages/web/components/EventRow/index.ts +0 -2
  102. package/packages/web/components/ExpandButton/ExpandButton.tsx +0 -18
  103. package/packages/web/components/ExpandButton/ExpandButton.types.ts +0 -6
  104. package/packages/web/components/ExpandButton/index.ts +0 -2
  105. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.tsx +0 -14
  106. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.types.ts +0 -5
  107. package/packages/web/components/HealthGradientBackground/HealthGradientBackground.utils.ts +0 -13
  108. package/packages/web/components/HealthGradientBackground/index.ts +0 -2
  109. package/packages/web/components/HeroSection/HeroSection.tsx +0 -92
  110. package/packages/web/components/HeroSection/HeroSection.types.ts +0 -14
  111. package/packages/web/components/HeroSection/HeroSection.utils.ts +0 -11
  112. package/packages/web/components/HeroSection/hooks/index.ts +0 -2
  113. package/packages/web/components/HeroSection/hooks/useCountUp.ts +0 -45
  114. package/packages/web/components/HeroSection/hooks/useWeeklyActivity.ts +0 -18
  115. package/packages/web/components/HeroSection/index.ts +0 -2
  116. package/packages/web/components/IdeasCard/IdeasCard.tsx +0 -115
  117. package/packages/web/components/IdeasCard/IdeasCard.types.ts +0 -10
  118. package/packages/web/components/IdeasCard/index.ts +0 -2
  119. package/packages/web/components/InsightMessage/InsightMessage.tsx +0 -9
  120. package/packages/web/components/InsightMessage/InsightMessage.types.ts +0 -3
  121. package/packages/web/components/InsightMessage/index.ts +0 -2
  122. package/packages/web/components/Logo/Logo.tsx +0 -65
  123. package/packages/web/components/Logo/index.ts +0 -1
  124. package/packages/web/components/MarkdownContent/MarkdownContent.tsx +0 -123
  125. package/packages/web/components/MarkdownContent/index.ts +0 -1
  126. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +0 -18
  127. package/packages/web/components/MasonryGrid/index.ts +0 -1
  128. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +0 -119
  129. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +0 -16
  130. package/packages/web/components/MomentumWidget/index.ts +0 -2
  131. package/packages/web/components/NowCard/NowCard.tsx +0 -118
  132. package/packages/web/components/NowCard/NowCard.types.ts +0 -16
  133. package/packages/web/components/NowCard/index.ts +0 -2
  134. package/packages/web/components/PageHeader/PageHeader.tsx +0 -24
  135. package/packages/web/components/PageHeader/index.ts +0 -1
  136. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +0 -20
  137. package/packages/web/components/ProgressRing/ProgressRing.tsx +0 -51
  138. package/packages/web/components/ProgressRing/ProgressRing.types.ts +0 -11
  139. package/packages/web/components/ProgressRing/index.ts +0 -2
  140. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +0 -54
  141. package/packages/web/components/ProjectAvatar/index.ts +0 -1
  142. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +0 -37
  143. package/packages/web/components/ProjectColorDot/index.ts +0 -1
  144. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +0 -104
  145. package/packages/web/components/ProjectSelectorModal/index.ts +0 -1
  146. package/packages/web/components/Providers/Providers.tsx +0 -48
  147. package/packages/web/components/Providers/index.ts +0 -1
  148. package/packages/web/components/QueueCard/QueueCard.tsx +0 -125
  149. package/packages/web/components/QueueCard/QueueCard.types.ts +0 -12
  150. package/packages/web/components/QueueCard/QueueCard.utils.ts +0 -12
  151. package/packages/web/components/QueueCard/index.ts +0 -2
  152. package/packages/web/components/RecoverCard/RecoverCard.tsx +0 -72
  153. package/packages/web/components/RecoverCard/RecoverCard.types.ts +0 -16
  154. package/packages/web/components/RecoverCard/index.ts +0 -2
  155. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +0 -145
  156. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +0 -16
  157. package/packages/web/components/RoadmapCard/index.ts +0 -2
  158. package/packages/web/components/ShipsCard/ShipsCard.tsx +0 -95
  159. package/packages/web/components/ShipsCard/ShipsCard.types.ts +0 -14
  160. package/packages/web/components/ShipsCard/ShipsCard.utils.ts +0 -4
  161. package/packages/web/components/ShipsCard/index.ts +0 -2
  162. package/packages/web/components/SparklineChart/SparklineChart.tsx +0 -40
  163. package/packages/web/components/SparklineChart/SparklineChart.types.ts +0 -6
  164. package/packages/web/components/SparklineChart/index.ts +0 -2
  165. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +0 -95
  166. package/packages/web/components/StatsMasonry/index.ts +0 -1
  167. package/packages/web/components/StreakCard/StreakCard.constants.ts +0 -2
  168. package/packages/web/components/StreakCard/StreakCard.tsx +0 -55
  169. package/packages/web/components/StreakCard/StreakCard.types.ts +0 -4
  170. package/packages/web/components/StreakCard/index.ts +0 -2
  171. package/packages/web/components/TasksCounter/TasksCounter.tsx +0 -14
  172. package/packages/web/components/TasksCounter/TasksCounter.types.ts +0 -3
  173. package/packages/web/components/TasksCounter/index.ts +0 -2
  174. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +0 -28
  175. package/packages/web/components/TechStackBadges/index.ts +0 -1
  176. package/packages/web/components/TerminalDock/DockToggleTab.tsx +0 -29
  177. package/packages/web/components/TerminalDock/TerminalDock.tsx +0 -386
  178. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +0 -130
  179. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +0 -142
  180. package/packages/web/components/TerminalDock/index.ts +0 -2
  181. package/packages/web/components/TerminalTabs/TerminalTab.tsx +0 -95
  182. package/packages/web/components/TerminalTabs/TerminalTabs.tsx +0 -211
  183. package/packages/web/components/TerminalTabs/index.ts +0 -1
  184. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +0 -32
  185. package/packages/web/components/VelocityBadge/VelocityBadge.types.ts +0 -3
  186. package/packages/web/components/VelocityBadge/index.ts +0 -2
  187. package/packages/web/components/VelocityCard/VelocityCard.tsx +0 -73
  188. package/packages/web/components/VelocityCard/VelocityCard.types.ts +0 -7
  189. package/packages/web/components/VelocityCard/index.ts +0 -2
  190. package/packages/web/components/WeeklyReports/PrintableReport.tsx +0 -259
  191. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +0 -187
  192. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +0 -288
  193. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +0 -149
  194. package/packages/web/components/WeeklyReports/index.ts +0 -4
  195. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +0 -25
  196. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +0 -4
  197. package/packages/web/components/WeeklySparkline/index.ts +0 -2
  198. package/packages/web/components/charts/SessionsChart.tsx +0 -175
  199. package/packages/web/components/ui/alert-dialog.tsx +0 -157
  200. package/packages/web/components/ui/badge.tsx +0 -46
  201. package/packages/web/components/ui/button.tsx +0 -60
  202. package/packages/web/components/ui/card.tsx +0 -92
  203. package/packages/web/components/ui/chart.tsx +0 -385
  204. package/packages/web/components/ui/dialog.tsx +0 -143
  205. package/packages/web/components/ui/drawer.tsx +0 -135
  206. package/packages/web/components/ui/dropdown-menu.tsx +0 -257
  207. package/packages/web/components/ui/input.tsx +0 -21
  208. package/packages/web/components/ui/scroll-area.tsx +0 -58
  209. package/packages/web/components/ui/select.tsx +0 -187
  210. package/packages/web/components/ui/sheet.tsx +0 -139
  211. package/packages/web/components/ui/tabs.tsx +0 -66
  212. package/packages/web/components/ui/tooltip.tsx +0 -61
  213. package/packages/web/components.json +0 -22
  214. package/packages/web/context/GlobalTerminalContext.tsx +0 -538
  215. package/packages/web/context/TerminalContext.tsx +0 -45
  216. package/packages/web/context/TerminalTabsContext.tsx +0 -181
  217. package/packages/web/eslint.config.mjs +0 -18
  218. package/packages/web/hooks/useClaudeTerminal.ts +0 -425
  219. package/packages/web/hooks/useProjectStats.ts +0 -93
  220. package/packages/web/hooks/useProjects.ts +0 -73
  221. package/packages/web/lib/actions/projects.ts +0 -15
  222. package/packages/web/lib/commands.ts +0 -81
  223. package/packages/web/lib/format.ts +0 -23
  224. package/packages/web/lib/generate-week-report.ts +0 -285
  225. package/packages/web/lib/parse-prjct-files.ts +0 -1123
  226. package/packages/web/lib/project-colors.ts +0 -58
  227. package/packages/web/lib/projects.ts +0 -506
  228. package/packages/web/lib/pty.ts +0 -101
  229. package/packages/web/lib/query-config.ts +0 -44
  230. package/packages/web/lib/services/index.ts +0 -9
  231. package/packages/web/lib/services/projects.server.ts +0 -66
  232. package/packages/web/lib/services/stats.server.ts +0 -562
  233. package/packages/web/lib/unified-loader.ts +0 -396
  234. package/packages/web/lib/utils.ts +0 -6
  235. package/packages/web/next-env.d.ts +0 -6
  236. package/packages/web/next.config.ts +0 -7
  237. package/packages/web/package.json +0 -57
  238. package/packages/web/postcss.config.mjs +0 -7
  239. package/packages/web/public/file.svg +0 -1
  240. package/packages/web/public/globe.svg +0 -1
  241. package/packages/web/public/next.svg +0 -1
  242. package/packages/web/public/vercel.svg +0 -1
  243. package/packages/web/public/window.svg +0 -1
  244. package/packages/web/server.ts +0 -312
  245. package/packages/web/tsconfig.json +0 -34
  246. package/templates/commands/serve.md +0 -121
@@ -1,288 +0,0 @@
1
- 'use client'
2
-
3
- import { useRef, useEffect, useState } from 'react'
4
- import { ChevronLeft, ChevronRight } from 'lucide-react'
5
- import { cn } from '@/lib/utils'
6
- import { getCurrentYearWeek, getWeekDateRange, formatDateRange } from '@/lib/generate-week-report'
7
-
8
- export type ActivityLevel = 'none' | 'low' | 'medium' | 'high'
9
-
10
- interface WeekCalendarProps {
11
- year: number
12
- selectedWeeks: number[]
13
- activityLevels: Map<number, ActivityLevel>
14
- onWeekSelect: (weeks: number[]) => void
15
- onYearChange: (year: number) => void
16
- }
17
-
18
- const DAYS = ['M', 'T', 'W', 'T', 'F'] // Mon-Fri
19
-
20
- export function WeekCalendar({
21
- year,
22
- selectedWeeks,
23
- activityLevels,
24
- onWeekSelect,
25
- onYearChange,
26
- }: WeekCalendarProps) {
27
- const { year: currentYear, week: currentWeek } = getCurrentYearWeek()
28
- const isCurrentYear = year === currentYear
29
- const scrollRef = useRef<HTMLDivElement>(null)
30
- const [canScrollLeft, setCanScrollLeft] = useState(false)
31
- const [canScrollRight, setCanScrollRight] = useState(true)
32
-
33
- // Generate 52 weeks
34
- const weeks = Array.from({ length: 52 }, (_, i) => i + 1)
35
-
36
- // Drag to scroll state
37
- const [isDragging, setIsDragging] = useState(false)
38
- const [startX, setStartX] = useState(0)
39
- const [scrollLeftStart, setScrollLeftStart] = useState(0)
40
- const [hasMoved, setHasMoved] = useState(false)
41
-
42
- // Handle mouse down - start potential drag
43
- const handleMouseDown = (e: React.MouseEvent) => {
44
- if (!scrollRef.current) return
45
- setIsDragging(true)
46
- setStartX(e.pageX)
47
- setScrollLeftStart(scrollRef.current.scrollLeft)
48
- setHasMoved(false)
49
- }
50
-
51
- // Handle mouse move - scroll if dragging
52
- const handleMouseMove = (e: React.MouseEvent) => {
53
- if (!isDragging || !scrollRef.current) return
54
-
55
- const diff = e.pageX - startX
56
- // Only count as drag if moved more than 5px
57
- if (Math.abs(diff) > 5) {
58
- setHasMoved(true)
59
- e.preventDefault()
60
- scrollRef.current.scrollLeft = scrollLeftStart - diff
61
- }
62
- }
63
-
64
- // Handle mouse up - end drag
65
- const handleMouseUp = () => {
66
- setIsDragging(false)
67
- }
68
-
69
- // Handle week click (only if not dragging)
70
- const handleWeekClick = (week: number, e: React.MouseEvent) => {
71
- // Ignore click if we were dragging
72
- if (hasMoved) {
73
- e.preventDefault()
74
- return
75
- }
76
-
77
- const isFuture = isCurrentYear && week > currentWeek
78
- if (isFuture) return
79
-
80
- if (selectedWeeks.includes(week)) {
81
- onWeekSelect(selectedWeeks.filter(w => w !== week))
82
- } else {
83
- onWeekSelect([...selectedWeeks, week].sort((a, b) => a - b))
84
- }
85
- }
86
-
87
- // Check scroll state
88
- const updateScrollState = () => {
89
- if (scrollRef.current) {
90
- const { scrollLeft, scrollWidth, clientWidth } = scrollRef.current
91
- setCanScrollLeft(scrollLeft > 0)
92
- setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 10)
93
- }
94
- }
95
-
96
- useEffect(() => {
97
- updateScrollState()
98
- // Scroll to current week on mount
99
- if (scrollRef.current && isCurrentYear) {
100
- const weekElement = scrollRef.current.querySelector(`[data-week="${currentWeek}"]`)
101
- if (weekElement) {
102
- weekElement.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })
103
- }
104
- }
105
- }, [currentWeek, isCurrentYear])
106
-
107
- const scroll = (direction: 'left' | 'right') => {
108
- if (scrollRef.current) {
109
- const scrollAmount = 400
110
- scrollRef.current.scrollBy({
111
- left: direction === 'left' ? -scrollAmount : scrollAmount,
112
- behavior: 'smooth'
113
- })
114
- }
115
- }
116
-
117
- return (
118
- <div className="space-y-6">
119
- {/* Year header */}
120
- <div className="flex items-center justify-between">
121
- <div className="flex items-center gap-2">
122
- <button
123
- onClick={() => onYearChange(year - 1)}
124
- className="p-1.5 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
125
- >
126
- <ChevronLeft className="h-4 w-4" />
127
- </button>
128
-
129
- <span className="text-4xl font-bold tabular-nums">{year}</span>
130
-
131
- <button
132
- onClick={() => onYearChange(year + 1)}
133
- disabled={year >= currentYear}
134
- className="p-1.5 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
135
- >
136
- <ChevronRight className="h-4 w-4" />
137
- </button>
138
- </div>
139
-
140
- <span className="text-sm text-muted-foreground">
141
- {selectedWeeks.length === 0
142
- ? 'Select weeks'
143
- : selectedWeeks.length === 1
144
- ? `W${selectedWeeks[0]}`
145
- : `${selectedWeeks.length} wks`
146
- }
147
- </span>
148
- </div>
149
-
150
- {/* Horizontal week strip */}
151
- <div className="relative -mx-2">
152
- {/* Scroll buttons */}
153
- {canScrollLeft && (
154
- <button
155
- onClick={() => scroll('left')}
156
- className="absolute left-0 top-1/2 -translate-y-1/2 z-10 p-2 bg-background/80 backdrop-blur rounded-full shadow-lg border hover:bg-muted transition-colors"
157
- >
158
- <ChevronLeft className="h-4 w-4" />
159
- </button>
160
- )}
161
- {canScrollRight && (
162
- <button
163
- onClick={() => scroll('right')}
164
- className="absolute right-0 top-1/2 -translate-y-1/2 z-10 p-2 bg-background/80 backdrop-blur rounded-full shadow-lg border hover:bg-muted transition-colors"
165
- >
166
- <ChevronRight className="h-4 w-4" />
167
- </button>
168
- )}
169
-
170
- {/* Scrollable week cards - drag to scroll */}
171
- <div
172
- ref={scrollRef}
173
- onScroll={updateScrollState}
174
- onMouseDown={handleMouseDown}
175
- onMouseMove={handleMouseMove}
176
- onMouseUp={handleMouseUp}
177
- onMouseLeave={handleMouseUp}
178
- className={cn(
179
- 'flex gap-3 overflow-x-auto scrollbar-hide py-2 px-2',
180
- isDragging && hasMoved ? 'cursor-grabbing' : 'cursor-grab'
181
- )}
182
- style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
183
- >
184
- {weeks.map((week) => {
185
- const isSelected = selectedWeeks.includes(week)
186
- const isCurrent = isCurrentYear && week === currentWeek
187
- const activityLevel = activityLevels.get(week) ?? 'none'
188
- const isFuture = isCurrentYear && week > currentWeek
189
- const { start, end } = getWeekDateRange(year, week)
190
-
191
- // Get month name
192
- const monthName = start.toLocaleDateString('en-US', { month: 'short' })
193
- const dayStart = start.getDate()
194
- const dayEnd = end.getDate()
195
-
196
- return (
197
- <div
198
- key={week}
199
- data-week={week}
200
- onClick={(e) => handleWeekClick(week, e)}
201
- className={cn(
202
- 'flex-shrink-0 w-28 rounded-2xl transition-all duration-200 select-none',
203
- 'flex flex-col overflow-hidden',
204
- 'border',
205
- isFuture && 'opacity-30',
206
- // Selected state
207
- isSelected
208
- ? 'bg-foreground text-background border-foreground shadow-lg scale-105'
209
- : 'bg-card hover:bg-muted border-border',
210
- isCurrent && !isSelected && 'ring-2 ring-primary ring-offset-2 ring-offset-background',
211
- )}
212
- >
213
- {/* Week header */}
214
- <div className={cn(
215
- 'px-3 py-2 text-left border-b',
216
- isSelected ? 'border-background/20' : 'border-border'
217
- )}>
218
- <div className="flex items-baseline gap-0.5">
219
- <span className={cn(
220
- 'text-xs font-medium',
221
- isSelected ? 'text-background/60' : 'text-muted-foreground'
222
- )}>
223
- W
224
- </span>
225
- <span className="text-2xl font-bold tabular-nums">{week}</span>
226
- </div>
227
- <p className={cn(
228
- 'text-xs mt-0.5',
229
- isSelected ? 'text-background/70' : 'text-muted-foreground'
230
- )}>
231
- {monthName} {dayStart}-{dayEnd}
232
- </p>
233
- </div>
234
-
235
- {/* Days grid - Mon to Fri with GitHub-style activity levels */}
236
- <div className="px-3 py-2">
237
- <div className="flex justify-between gap-1">
238
- {DAYS.map((day, i) => {
239
- // Generate activity level per day based on week activity
240
- // This simulates varying activity levels across the week
241
- let dayActivityLevel: 0 | 1 | 2 | 3 | 4 = 0
242
-
243
- if (activityLevel === 'high') {
244
- // High activity: most days are 3-4
245
- dayActivityLevel = [4, 3, 4, 3, 2][i] as 0 | 1 | 2 | 3 | 4
246
- } else if (activityLevel === 'medium') {
247
- // Medium: mix of 2-3
248
- dayActivityLevel = [2, 3, 2, 1, 2][i] as 0 | 1 | 2 | 3 | 4
249
- } else if (activityLevel === 'low') {
250
- // Low: mostly 0-1
251
- dayActivityLevel = [1, 0, 1, 0, 0][i] as 0 | 1 | 2 | 3 | 4
252
- }
253
-
254
- // GitHub-style green gradients
255
- const activityColors = {
256
- 0: isSelected ? 'bg-background/10' : 'bg-muted',
257
- 1: isSelected ? 'bg-background/30' : 'bg-emerald-500/30',
258
- 2: isSelected ? 'bg-background/50' : 'bg-emerald-500/50',
259
- 3: isSelected ? 'bg-background/75' : 'bg-emerald-500/75',
260
- 4: isSelected ? 'bg-background' : 'bg-emerald-500',
261
- }
262
-
263
- return (
264
- <div key={i} className="flex flex-col items-center gap-1">
265
- <span className={cn(
266
- 'text-[10px] font-medium',
267
- isSelected ? 'text-background/50' : 'text-muted-foreground'
268
- )}>
269
- {day}
270
- </span>
271
- <div className={cn(
272
- 'w-3 h-3 rounded-sm',
273
- activityColors[dayActivityLevel]
274
- )} />
275
- </div>
276
- )
277
- })}
278
- </div>
279
- </div>
280
- </div>
281
- )
282
- })}
283
- </div>
284
- </div>
285
-
286
- </div>
287
- )
288
- }
@@ -1,149 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useMemo } from 'react'
4
- import { useParams } from 'next/navigation'
5
- import Link from 'next/link'
6
- import { RotateCcw, Copy, Check, Printer } from 'lucide-react'
7
- import { WeekCalendar, type ActivityLevel } from './WeekCalendar'
8
- import { ReportPreviewCard } from './ReportPreviewCard'
9
- import {
10
- getCurrentYearWeek,
11
- filterDataByWeek,
12
- generateReportText,
13
- getWeekActivityLevel,
14
- } from '@/lib/generate-week-report'
15
- import type { StatsResult } from '@/lib/services/stats.server'
16
- import { cn } from '@/lib/utils'
17
-
18
- interface WeeklyReportsProps {
19
- stats: StatsResult
20
- projectName: string
21
- }
22
-
23
- export function WeeklyReports({ stats, projectName }: WeeklyReportsProps) {
24
- const params = useParams()
25
- const projectId = params.id as string
26
- const { year: currentYear, week: currentWeek } = getCurrentYearWeek()
27
-
28
- const [year, setYear] = useState(currentYear)
29
- const [selectedWeeks, setSelectedWeeks] = useState<number[]>([currentWeek])
30
- const [copied, setCopied] = useState(false)
31
-
32
- // Build print URL with selected weeks
33
- const printUrl = `/project/${projectId}/reports/print?weeks=${selectedWeeks.join(',')}&year=${year}`
34
-
35
- const activityLevels = useMemo(() => {
36
- const levels = new Map<number, ActivityLevel>()
37
- for (let w = 1; w <= 52; w++) {
38
- levels.set(w, getWeekActivityLevel(stats, year, w))
39
- }
40
- return levels
41
- }, [stats, year])
42
-
43
- const handleReset = () => {
44
- setYear(currentYear)
45
- setSelectedWeeks([currentWeek])
46
- }
47
-
48
- const weekDataList = useMemo(() => {
49
- return selectedWeeks.map(w => filterDataByWeek(stats, year, w))
50
- }, [stats, year, selectedWeeks])
51
-
52
- const reportText = useMemo(() => {
53
- if (weekDataList.length === 0) return ''
54
- return generateReportText(weekDataList, projectName)
55
- }, [weekDataList, projectName])
56
-
57
- const handleCopy = async () => {
58
- try {
59
- await navigator.clipboard.writeText(reportText)
60
- setCopied(true)
61
- setTimeout(() => setCopied(false), 2000)
62
- } catch (err) {
63
- console.error('Failed to copy:', err)
64
- }
65
- }
66
-
67
- return (
68
- <div className="space-y-6">
69
- {/* Calendar */}
70
- <div className="relative overflow-hidden rounded-xl border bg-card p-6">
71
- <WeekCalendar
72
- year={year}
73
- selectedWeeks={selectedWeeks}
74
- activityLevels={activityLevels}
75
- onWeekSelect={setSelectedWeeks}
76
- onYearChange={setYear}
77
- />
78
-
79
- {/* Actions */}
80
- <div className="flex items-center justify-between mt-6 pt-4 border-t">
81
- <button
82
- onClick={handleReset}
83
- className="inline-flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors"
84
- >
85
- <RotateCcw className="h-4 w-4" />
86
- Reset
87
- </button>
88
-
89
- <div className="flex items-center gap-2">
90
- <Link
91
- href={printUrl}
92
- target="_blank"
93
- className={cn(
94
- 'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all',
95
- 'border border-foreground/20 text-foreground hover:bg-muted',
96
- selectedWeeks.length === 0 && 'opacity-50 pointer-events-none'
97
- )}
98
- >
99
- <Printer className="h-4 w-4" />
100
- Print / PDF
101
- </Link>
102
-
103
- <button
104
- onClick={handleCopy}
105
- disabled={selectedWeeks.length === 0}
106
- className={cn(
107
- 'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all',
108
- copied
109
- ? 'bg-emerald-500 text-white'
110
- : 'bg-foreground text-background hover:bg-foreground/90',
111
- selectedWeeks.length === 0 && 'opacity-50 cursor-not-allowed'
112
- )}
113
- >
114
- {copied ? (
115
- <>
116
- <Check className="h-4 w-4" />
117
- Copied!
118
- </>
119
- ) : (
120
- <>
121
- <Copy className="h-4 w-4" />
122
- Copy
123
- </>
124
- )}
125
- </button>
126
- </div>
127
- </div>
128
- </div>
129
-
130
- {/* Report Preview Card */}
131
- <ReportPreviewCard weekData={weekDataList} />
132
-
133
- {/* Copy Text Preview (collapsed) */}
134
- {selectedWeeks.length > 0 && reportText && (
135
- <details className="group">
136
- <summary className="cursor-pointer text-sm text-muted-foreground hover:text-foreground flex items-center gap-2">
137
- <span className="group-open:rotate-90 transition-transform">▶</span>
138
- View copy text
139
- </summary>
140
- <div className="mt-3 relative overflow-hidden rounded-xl border bg-muted/30 p-4">
141
- <pre className="whitespace-pre-wrap text-sm font-mono text-muted-foreground">
142
- {reportText}
143
- </pre>
144
- </div>
145
- </details>
146
- )}
147
- </div>
148
- )
149
- }
@@ -1,4 +0,0 @@
1
- export { WeeklyReports } from './WeeklyReports'
2
- export { WeekCalendar } from './WeekCalendar'
3
- export { ReportPreviewCard } from './ReportPreviewCard'
4
- export { PrintableReport } from './PrintableReport'
@@ -1,25 +0,0 @@
1
- 'use client'
2
-
3
- import { SparklineChart } from '@/components/SparklineChart'
4
- import type { WeeklySparklineProps } from './WeeklySparkline.types'
5
-
6
- const sizeConfig = {
7
- sm: { width: 'w-full', height: 32 },
8
- md: { width: 'w-full', height: 40 },
9
- lg: { width: 'w-full', height: 56 },
10
- }
11
-
12
- export function WeeklySparkline({ data, size = 'md' }: WeeklySparklineProps) {
13
- const config = sizeConfig[size]
14
-
15
- return (
16
- <div className={`${config.width} min-w-0`}>
17
- <p className="text-xs uppercase tracking-wider text-muted-foreground mb-1 md:text-right">
18
- 7-day activity
19
- </p>
20
- <div className="w-full min-w-0 overflow-hidden">
21
- <SparklineChart data={data} height={config.height} />
22
- </div>
23
- </div>
24
- )
25
- }
@@ -1,4 +0,0 @@
1
- export interface WeeklySparklineProps {
2
- data: number[]
3
- size?: 'sm' | 'md' | 'lg'
4
- }
@@ -1,2 +0,0 @@
1
- export { WeeklySparkline } from './WeeklySparkline'
2
- export type { WeeklySparklineProps } from './WeeklySparkline.types'
@@ -1,175 +0,0 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import { Bar, BarChart, CartesianGrid, XAxis } from 'recharts'
5
- import { useQuery } from '@tanstack/react-query'
6
- import {
7
- Card,
8
- CardContent,
9
- CardDescription,
10
- CardHeader,
11
- CardTitle,
12
- } from '@/components/ui/card'
13
- import {
14
- ChartConfig,
15
- ChartContainer,
16
- ChartTooltip,
17
- ChartTooltipContent,
18
- } from '@/components/ui/chart'
19
-
20
- const chartConfig = {
21
- views: {
22
- label: 'Activity',
23
- },
24
- tasks: {
25
- label: 'Tasks Completed',
26
- color: 'var(--chart-1)',
27
- },
28
- ships: {
29
- label: 'Features Shipped',
30
- color: 'var(--chart-2)',
31
- },
32
- } satisfies ChartConfig
33
-
34
- interface ChartDataPoint {
35
- date: string
36
- tasks: number
37
- ships: number
38
- }
39
-
40
- interface SessionsHistoryResponse {
41
- success: boolean
42
- data: {
43
- chartData: ChartDataPoint[]
44
- totals: {
45
- tasks: number
46
- ships: number
47
- }
48
- dateRange: {
49
- start: string
50
- end: string
51
- }
52
- }
53
- }
54
-
55
- export function SessionsChart() {
56
- const [activeChart, setActiveChart] =
57
- React.useState<'tasks' | 'ships'>('tasks')
58
-
59
- const { data: response, isLoading } = useQuery<SessionsHistoryResponse>({
60
- queryKey: ['sessions-history'],
61
- queryFn: async () => {
62
- const res = await fetch('/api/sessions/history')
63
- return res.json()
64
- },
65
- })
66
-
67
- const chartData = response?.data?.chartData || []
68
- const totals = response?.data?.totals || { tasks: 0, ships: 0 }
69
-
70
- if (isLoading) {
71
- return (
72
- <Card>
73
- <CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
74
- <div className="flex flex-1 flex-col justify-center gap-1 px-4 py-3 sm:px-5 sm:py-4">
75
- <CardTitle>Session Activity</CardTitle>
76
- <CardDescription>Loading...</CardDescription>
77
- </div>
78
- </CardHeader>
79
- <CardContent className="px-2 sm:p-4">
80
- <div className="h-[250px] flex items-center justify-center">
81
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary" />
82
- </div>
83
- </CardContent>
84
- </Card>
85
- )
86
- }
87
-
88
- return (
89
- <Card>
90
- <CardHeader className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row">
91
- <div className="flex flex-1 flex-col justify-center gap-1 px-4 py-3 sm:px-5 sm:py-4">
92
- <CardTitle>Session Activity</CardTitle>
93
- <CardDescription>
94
- Last 30 days across all projects
95
- </CardDescription>
96
- </div>
97
- <div className="flex">
98
- {(['tasks', 'ships'] as const).map((key) => {
99
- return (
100
- <button
101
- key={key}
102
- data-active={activeChart === key}
103
- className="relative z-30 flex flex-1 flex-col justify-center gap-1 border-t px-4 py-3 text-left even:border-l data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-6 sm:py-4"
104
- onClick={() => setActiveChart(key)}
105
- >
106
- <span className="text-xs text-muted-foreground">
107
- {chartConfig[key].label}
108
- </span>
109
- <span className="text-lg font-bold leading-none sm:text-3xl">
110
- {totals[key].toLocaleString()}
111
- </span>
112
- </button>
113
- )
114
- })}
115
- </div>
116
- </CardHeader>
117
- <CardContent className="px-2 sm:p-4">
118
- {chartData.length === 0 ? (
119
- <div className="h-[250px] flex items-center justify-center text-muted-foreground">
120
- No session data yet
121
- </div>
122
- ) : (
123
- <ChartContainer
124
- config={chartConfig}
125
- className="aspect-auto h-[250px] w-full"
126
- >
127
- <BarChart
128
- accessibilityLayer
129
- data={chartData}
130
- margin={{
131
- left: 12,
132
- right: 12,
133
- }}
134
- >
135
- <CartesianGrid vertical={false} />
136
- <XAxis
137
- dataKey="date"
138
- tickLine={false}
139
- axisLine={false}
140
- tickMargin={8}
141
- minTickGap={32}
142
- tickFormatter={(value: string) => {
143
- const date = new Date(value)
144
- return date.toLocaleDateString('en-US', {
145
- month: 'short',
146
- day: 'numeric',
147
- })
148
- }}
149
- />
150
- <ChartTooltip
151
- content={({ active, payload, label }) => (
152
- <ChartTooltipContent
153
- active={active}
154
- payload={payload as Parameters<typeof ChartTooltipContent>[0]['payload']}
155
- label={label as string}
156
- className="w-[150px]"
157
- nameKey="views"
158
- labelFormatter={(value) => {
159
- return new Date(value).toLocaleDateString('en-US', {
160
- month: 'short',
161
- day: 'numeric',
162
- year: 'numeric',
163
- })
164
- }}
165
- />
166
- )}
167
- />
168
- <Bar dataKey={activeChart} fill={`var(--color-${activeChart})`} />
169
- </BarChart>
170
- </ChartContainer>
171
- )}
172
- </CardContent>
173
- </Card>
174
- )
175
- }