11'use client' ;
22
3- import { Menubar , MenubarMenu , MenubarTrigger } from '@/components/ui/menubar' ;
3+ import { Menubar } from '@/components/ui/menubar' ;
4+ import { Plus } from 'lucide-react' ;
45import { ProgressBar } from '@/components/ui/progressbar' ;
56import { Button } from '@/components/ui/button' ;
67import { useGlobalContext } from '@/context/GlobalContext' ;
@@ -40,6 +41,7 @@ export default function SettingsBar() {
4041 const [ leftTimerSeconds , setLeftTimerSeconds ] = useState ( 0 ) ;
4142 const intervalRef = useRef < NodeJS . Timeout | null > ( null ) ;
4243 const [ isResetDialogOpen , setIsResetDialogOpen ] = useState ( false ) ;
44+ const [ isNewDialogOpen , setIsNewDialogOpen ] = useState ( false ) ;
4345 const [ isSessionModalOpen , setIsSessionModalOpen ] = useState ( false ) ;
4446 const [ sessionModalMode , setSessionModalMode ] = useState < 'save' | 'load' > (
4547 'save'
@@ -49,37 +51,19 @@ export default function SettingsBar() {
4951 const [ fetchingFor , setFetchingFor ] = useState < 'save' | 'load' | null > ( null ) ;
5052 const [ isSaving , setIsSaving ] = useState ( false ) ;
5153 const [ isLoading , setIsLoading ] = useState ( false ) ;
54+ const [ isDirty , setIsDirty ] = useState ( false ) ;
5255
53- const [ sessionId , setSessionId ] = useState < number | null > ( null ) ;
54-
56+ // Track unsaved canvas changes
5557 useEffect ( ( ) => {
56- async function fetchOrCreateSession ( ) {
57- try {
58- const res = await fetch ( '/api/sessions' ) ;
59- const sessions = await res . json ( ) ;
60-
61- if ( sessions . length > 0 ) {
62- setSessionId ( sessions [ 0 ] . id ) ;
63- } else {
64- const created = await fetch ( '/api/sessions' , {
65- method : 'POST' ,
66- headers : { 'Content-Type' : 'application/json' } ,
67- body : JSON . stringify ( 'New Session' ) ,
68- } ) ;
69- const session = await created . json ( ) ;
70- setSessionId ( session . id ) ;
71- }
72- } catch ( err ) {
73- console . error ( 'Failed to fetch or create session' , err ) ;
74- }
75- }
76-
77- fetchOrCreateSession ( ) ;
58+ const handler = ( ) => setIsDirty ( true ) ;
59+ window . addEventListener ( 'canvas-changed' , handler ) ;
60+ window . addEventListener ( 'reactflow-edges-changed' , handler ) ;
61+ return ( ) => {
62+ window . removeEventListener ( 'canvas-changed' , handler ) ;
63+ window . removeEventListener ( 'reactflow-edges-changed' , handler ) ;
64+ } ;
7865 } , [ ] ) ;
79-
80- // useEffect(() => {
81- // console.log('dataStreaming:', dataStreaming);
82- // });
66+
8367 // Timer effect - starts/stops based on dataStreaming state
8468 useEffect ( ( ) => {
8569 if ( dataStreaming ) {
@@ -171,6 +155,7 @@ export default function SettingsBar() {
171155 setIsSaving ( true ) ;
172156 try {
173157 await handleSaveToExistingSession ( activeSessionId ) ;
158+ setIsDirty ( false ) ;
174159 notifications . success ( { title : 'Session saved successfully' } ) ;
175160 } catch ( error ) {
176161 notifications . error ( {
@@ -224,13 +209,35 @@ export default function SettingsBar() {
224209 }
225210 } ;
226211
212+ const handleNewClick = ( ) => {
213+ if ( isSaving || isLoading || isFetchingSessions ) {
214+ return ;
215+ }
216+ if ( isDirty ) {
217+ setIsNewDialogOpen ( true ) ;
218+ } else {
219+ handleConfirmNew ( ) ;
220+ }
221+ } ;
222+
223+ const handleConfirmNew = ( ) => {
224+ setActiveSessionId ( null ) ;
225+ setIsDirty ( false ) ;
226+ setDataStreaming ( false ) ;
227+ setLeftTimerSeconds ( 0 ) ;
228+ window . dispatchEvent ( new Event ( 'pipeline-reset' ) ) ;
229+ setIsNewDialogOpen ( false ) ;
230+ notifications . success ( { title : 'New session started' } ) ;
231+ } ;
232+
227233 const handleCreateAndSaveSession = async ( sessionName : string ) => {
228234 setIsSaving ( true ) ;
229235 try {
230236 const state = await requestFrontendState ( ) ;
231237 const createdSession = await createSession ( sessionName ) ;
232238 await saveFrontendState ( createdSession . id , state ) ;
233239 setActiveSessionId ( createdSession . id ) ;
240+ setIsDirty ( false ) ;
234241 setIsSessionModalOpen ( false ) ;
235242 notifications . success ( { title : 'Session saved successfully' } ) ;
236243 } catch ( error ) {
@@ -278,16 +285,27 @@ export default function SettingsBar() {
278285
279286 return (
280287 < div className = "flex justify-between items-center p-4 bg-white border-b" >
281- { /* Session ID, Tutorial */ }
288+ { /* Session ID, Tutorials */ }
282289 < Menubar >
283290 < span className = "px-3 py-1 text-sm" >
284- Session { sessionId ?? 'ID' }
291+ Session { activeSessionId ?? 'ID' }
285292 </ span >
286- < MenubarMenu >
287- < MenubarTrigger className = "hover:cursor-pointer hover:underline" > Tutorials</ MenubarTrigger >
288- </ MenubarMenu >
293+ < button className = "px-3 py-1 text-sm rounded-sm hover:bg-accent hover:text-accent-foreground hover:underline" >
294+ Tutorials
295+ </ button >
289296 </ Menubar >
290297
298+ { /* New session button */ }
299+ < Button
300+ variant = "outline"
301+ onClick = { handleNewClick }
302+ disabled = { isSaving || isLoading || isFetchingSessions }
303+ className = "ml-2 flex items-center gap-1"
304+ >
305+ < Plus size = { 14 } />
306+ New
307+ </ Button >
308+
291309 { /* slider */ }
292310 < div className = "flex-1 mx-4" >
293311 < ProgressBar value = { ( leftTimerSeconds / 300 ) * 100 } />
@@ -353,6 +371,23 @@ export default function SettingsBar() {
353371 </ Button >
354372 </ div >
355373
374+ < Dialog open = { isNewDialogOpen } onOpenChange = { setIsNewDialogOpen } >
375+ < DialogContent >
376+ < DialogHeader >
377+ < DialogTitle > Start a new session?</ DialogTitle >
378+ < DialogDescription >
379+ Your current session is unsaved. Hitting confirm will clear the current pipeline. Any unsaved changes will be lost.
380+ </ DialogDescription >
381+ </ DialogHeader >
382+ < div className = "flex justify-end gap-2 mt-4" >
383+ < DialogClose asChild >
384+ < Button variant = "outline" > Cancel</ Button >
385+ </ DialogClose >
386+ < Button className = "bg-red-500" onClick = { handleConfirmNew } > Confirm</ Button >
387+ </ div >
388+ </ DialogContent >
389+ </ Dialog >
390+
356391 < SessionModal
357392 open = { isSessionModalOpen }
358393 mode = { sessionModalMode }
0 commit comments