1- import React , { useState } from 'react'
1+ import React , { useState , useRef , useEffect } from 'react'
22import { Card } from '@/components/ui/card'
33import { Badge } from '@/components/ui/badge'
44import { Button } from '@/components/ui/button'
@@ -7,13 +7,6 @@ import { TaskCard } from './TaskCard'
77import { Plus , Move , Edit2 , Trash2 , Check , X } from 'lucide-react'
88import { Input } from '@/components/ui/input'
99import { Textarea } from '@/components/ui/textarea'
10- import {
11- Select ,
12- SelectContent ,
13- SelectItem ,
14- SelectTrigger ,
15- SelectValue ,
16- } from '@/components/ui/select'
1710import { DashboardHero } from '../Heros/DashboardHero/DashboardHero'
1811
1912interface EpicsViewProps {
@@ -42,6 +35,51 @@ export const EpicsView: React.FC<EpicsViewProps> = ({
4235 const [ draggedTask , setDraggedTask ] = useState < Task | null > ( null )
4336 const [ editingEpic , setEditingEpic ] = useState < string | null > ( null )
4437 const [ editForm , setEditForm ] = useState < Partial < Epic > > ( { } )
38+ const [ heroHeight , setHeroHeight ] = useState ( 0 )
39+
40+ const heroRef = useRef < HTMLDivElement > ( null )
41+ const containerRef = useRef < HTMLDivElement > ( null )
42+
43+ // Measure hero height and adjust when it changes
44+ useEffect ( ( ) => {
45+ const measureHeroHeight = ( ) => {
46+ if ( heroRef . current ) {
47+ const height = heroRef . current . offsetHeight
48+ setHeroHeight ( height )
49+ }
50+ }
51+
52+ // Initial measurement
53+ measureHeroHeight ( )
54+
55+ // Set up ResizeObserver to watch for changes in hero height
56+ const resizeObserver = new ResizeObserver ( measureHeroHeight )
57+ if ( heroRef . current ) {
58+ resizeObserver . observe ( heroRef . current )
59+ }
60+
61+ // Also listen for storage events in case the minimized state changes in another tab
62+ const handleStorageChange = ( ) => {
63+ setTimeout ( measureHeroHeight , 100 ) // Small delay to let the animation complete
64+ }
65+ window . addEventListener ( 'storage' , handleStorageChange )
66+
67+ return ( ) => {
68+ resizeObserver . disconnect ( )
69+ window . removeEventListener ( 'storage' , handleStorageChange )
70+ }
71+ } , [ ] )
72+
73+ // Re-measure when the hero content might change
74+ useEffect ( ( ) => {
75+ const timer = setTimeout ( ( ) => {
76+ if ( heroRef . current ) {
77+ setHeroHeight ( heroRef . current . offsetHeight )
78+ }
79+ } , 300 ) // Wait for any animations to complete
80+
81+ return ( ) => clearTimeout ( timer )
82+ } , [ editingEpic ] ) // Re-measure when editing states change
4583
4684 const handleDragStart = ( task : Task ) => {
4785 setDraggedTask ( task )
@@ -128,30 +166,44 @@ export const EpicsView: React.FC<EpicsViewProps> = ({
128166 }
129167
130168 return (
131- < div className = "px-2 md:px-4 py-4 space-y-8" >
132- < DashboardHero
133- title = "Epic Planning"
134- description = "Organize and manage your project epics, track progress, and assign tasks."
135- gradient = "bg-gradient-to-r from-rose-600 via-pink-600 to-fuchsia-600"
136- primaryAction = { {
137- label : 'Add epic' ,
138- onClick : onAddEpic ,
139- } }
140- />
141- < div className = "flex-1" >
142- < div className = "w-full" >
169+ < div ref = { containerRef } className = "h-full flex flex-col px-2 md:px-4 py-4" >
170+ < div ref = { heroRef } className = "flex-shrink-0" >
171+ < DashboardHero
172+ title = "Epic Planning"
173+ description = "Organize and manage your project epics, track progress, and assign tasks."
174+ gradient = "bg-gradient-to-r from-rose-600 via-pink-600 to-fuchsia-600"
175+ primaryAction = { {
176+ label : 'Add epic' ,
177+ onClick : onAddEpic ,
178+ } }
179+ />
180+ </ div >
181+ < div className = "flex-1 min-h-0 mt-8" >
182+ < div
183+ className = "h-full overflow-y-auto"
184+ style = { {
185+ height : heroHeight > 0 ? `calc(100vh - ${ heroHeight + 120 } px)` : 'calc(100vh - 12rem)'
186+ } }
187+ >
143188 { /* Epics Grid */ }
144- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6" >
189+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-max " >
145190 { epics . map ( ( epic ) => {
146191 const epicTasks = getTasksByEpic ( epic . id )
147192 const totalPoints = epicTasks . reduce ( ( sum , task ) => sum + task . points , 0 )
193+
194+ // Calculate dynamic height based on available space
195+ const availableHeight = heroHeight > 0 ? `calc(100vh - ${ heroHeight + 200 } px)` : 'calc(100vh - 14rem)'
148196
149197 return (
150198 < Card
151199 key = { epic . id }
152200 className = { `flex flex-col ${
153- editingEpic === epic . id ? 'h-auto' : 'h-[600px] '
201+ editingEpic === epic . id ? 'h-auto' : ''
154202 } bg-card shadow-sm`}
203+ style = { {
204+ height : editingEpic === epic . id ? 'auto' : availableHeight ,
205+ minHeight : '400px'
206+ } }
155207 onDragOver = { handleDragOver }
156208 onDrop = { ( e ) => handleDrop ( e , epic . id ) }
157209 >
@@ -199,44 +251,36 @@ export const EpicsView: React.FC<EpicsViewProps> = ({
199251 < div className = "grid grid-cols-2 gap-2" >
200252 < div >
201253 < label className = "text-xs text-muted-foreground" > Confidence</ label >
202- < Select
254+ < select
203255 value = { editForm . confidence || epic . confidence }
204- onValueChange = { ( value ) =>
256+ onChange = { ( e ) =>
205257 setEditForm ( {
206258 ...editForm ,
207- confidence : value as Epic [ 'confidence' ] ,
259+ confidence : e . target . value as Epic [ 'confidence' ] ,
208260 } )
209261 }
262+ className = "flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
210263 >
211- < SelectTrigger className = "text-xs" >
212- < SelectValue />
213- </ SelectTrigger >
214- < SelectContent >
215- < SelectItem value = "low" > Low</ SelectItem >
216- < SelectItem value = "medium" > Medium</ SelectItem >
217- < SelectItem value = "high" > High</ SelectItem >
218- </ SelectContent >
219- </ Select >
264+ < option value = "low" > Low</ option >
265+ < option value = "medium" > Medium</ option >
266+ < option value = "high" > High</ option >
267+ </ select >
220268 </ div >
221269 < div >
222270 < label className = "text-xs text-muted-foreground" > Phase</ label >
223- < Select
271+ < select
224272 value = { editForm . phase ?. toString ( ) || epic . phase . toString ( ) }
225- onValueChange = { ( value ) =>
226- setEditForm ( { ...editForm , phase : parseInt ( value ) } )
273+ onChange = { ( e ) =>
274+ setEditForm ( { ...editForm , phase : parseInt ( e . target . value ) } )
227275 }
276+ className = "flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
228277 >
229- < SelectTrigger className = "text-xs" >
230- < SelectValue />
231- </ SelectTrigger >
232- < SelectContent >
233- { [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] . map ( ( phase ) => (
234- < SelectItem key = { phase } value = { phase . toString ( ) } >
235- Phase { phase }
236- </ SelectItem >
237- ) ) }
238- </ SelectContent >
239- </ Select >
278+ { [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] . map ( ( phase ) => (
279+ < option key = { phase } value = { phase . toString ( ) } >
280+ Phase { phase }
281+ </ option >
282+ ) ) }
283+ </ select >
240284 </ div >
241285 </ div >
242286
0 commit comments