@@ -10,6 +10,9 @@ import { useEffect, useState } from 'react'
1010import { IconCircleCheck } from '@tabler/icons-react'
1111import { SystemUpdateStatus } from '../../../types/system'
1212import api from '~/lib/api'
13+ import Input from '~/components/inputs/Input'
14+ import { useMutation } from '@tanstack/react-query'
15+ import { useNotifications } from '~/context/NotificationContext'
1316
1417export default function SystemUpdatePage ( props : {
1518 system : {
@@ -18,11 +21,14 @@ export default function SystemUpdatePage(props: {
1821 currentVersion : string
1922 }
2023} ) {
24+ const { addNotification } = useNotifications ( )
25+
2126 const [ isUpdating , setIsUpdating ] = useState ( false )
2227 const [ updateStatus , setUpdateStatus ] = useState < SystemUpdateStatus | null > ( null )
2328 const [ error , setError ] = useState < string | null > ( null )
2429 const [ showLogs , setShowLogs ] = useState ( false )
2530 const [ logs , setLogs ] = useState < string > ( '' )
31+ const [ email , setEmail ] = useState ( '' )
2632
2733 useEffect ( ( ) => {
2834 if ( ! isUpdating ) return
@@ -116,10 +122,33 @@ export default function SystemUpdatePage(props: {
116122 if ( updateStatus ?. stage === 'error' )
117123 return < IconAlertCircle className = "h-12 w-12 text-desert-red" />
118124 if ( isUpdating ) return < IconRefresh className = "h-12 w-12 text-desert-green animate-spin" />
119- if ( props . system . updateAvailable ) return < IconArrowBigUpLines className = "h-16 w-16 text-desert-green" />
125+ if ( props . system . updateAvailable )
126+ return < IconArrowBigUpLines className = "h-16 w-16 text-desert-green" />
120127 return < IconCircleCheck className = "h-16 w-16 text-desert-olive" />
121128 }
122129
130+ const subscribeToReleaseNotesMutation = useMutation ( {
131+ mutationKey : [ 'subscribeToReleaseNotes' ] ,
132+ mutationFn : ( email : string ) => api . subscribeToReleaseNotes ( email ) ,
133+ onSuccess : ( data ) => {
134+ if ( data && data . success ) {
135+ addNotification ( { type : 'success' , message : 'Successfully subscribed to release notes!' } )
136+ setEmail ( '' )
137+ } else {
138+ addNotification ( {
139+ type : 'error' ,
140+ message : `Failed to subscribe: ${ data ?. message || 'Unknown error' } ` ,
141+ } )
142+ }
143+ } ,
144+ onError : ( error : any ) => {
145+ addNotification ( {
146+ type : 'error' ,
147+ message : `Error subscribing to release notes: ${ error . message || 'Unknown error' } ` ,
148+ } )
149+ } ,
150+ } )
151+
123152 return (
124153 < SettingsLayout >
125154 < Head title = "System Update" />
@@ -128,7 +157,8 @@ export default function SystemUpdatePage(props: {
128157 < div className = "mb-8" >
129158 < h1 className = "text-4xl font-bold text-desert-green mb-2" > System Update</ h1 >
130159 < p className = "text-desert-stone-dark" >
131- Keep your Project N.O.M.A.D. instance up to date with the latest features and improvements.
160+ Keep your Project N.O.M.A.D. instance up to date with the latest features and
161+ improvements.
132162 </ p >
133163 </ div >
134164
@@ -161,9 +191,7 @@ export default function SystemUpdatePage(props: {
161191 { ! isUpdating && (
162192 < >
163193 < h2 className = "text-2xl font-bold text-desert-green mb-2" >
164- { props . system . updateAvailable
165- ? 'Update Available'
166- : 'System Up to Date' }
194+ { props . system . updateAvailable ? 'Update Available' : 'System Up to Date' }
167195 </ h2 >
168196 < p className = "text-desert-stone-dark mb-6" >
169197 { props . system . updateAvailable
@@ -305,6 +333,43 @@ export default function SystemUpdatePage(props: {
305333 ) }
306334 </ div >
307335 </ div >
336+ < div className = "bg-white rounded-lg border shadow-md overflow-hidden py-6 mt-6" >
337+ < div className = "flex flex-col md:flex-row justify-between items-center p-8 gap-y-8 md:gap-y-0 gap-x-8" >
338+ < div >
339+ < h2 className = "max-w-xl text-lg font-bold text-desert-green sm:text-xl lg:col-span-7" >
340+ Want to stay updated with the latest from Project N.O.M.A.D.? Subscribe to receive
341+ release notes directly to your inbox. Unsubscribe anytime.
342+ </ h2 >
343+ </ div >
344+ < div className = "flex flex-col" >
345+ < div className = "flex gap-x-3" >
346+ < Input
347+ name = "email"
348+ label = ""
349+ type = "email"
350+ placeholder = "Your email address"
351+ disabled = { false }
352+ value = { email }
353+ onChange = { ( e ) => setEmail ( e . target . value ) }
354+ className = "w-full"
355+ containerClassName = "!mt-0"
356+ />
357+ < StyledButton
358+ variant = "primary"
359+ disabled = { ! email }
360+ onClick = { ( ) => subscribeToReleaseNotesMutation . mutateAsync ( email ) }
361+ loading = { subscribeToReleaseNotesMutation . isPending }
362+ >
363+ Subscribe
364+ </ StyledButton >
365+ </ div >
366+ < p className = "mt-2 text-sm text-desert-stone-dark" >
367+ We care about your privacy. Project N.O.M.A.D. will never share your email with
368+ third parties or send you spam.
369+ </ p >
370+ </ div >
371+ </ div >
372+ </ div >
308373 < div className = "mt-6 grid grid-cols-1 md:grid-cols-2 gap-4" >
309374 < Alert
310375 type = "info"
0 commit comments