@@ -63,6 +63,7 @@ import config from '../../config';
6363import { AppTemplateId } from '../../types' ;
6464import { errorFrom } from '../../utils/errors' ;
6565import { sendAppCreatedEvent } from '../../utils/ga' ;
66+ import { LatestStoredAppValue , TOOLPAD_LATEST_APP_KEY } from '../../storageKeys' ;
6667
6768export const APP_TEMPLATE_OPTIONS : Map <
6869 AppTemplateId ,
@@ -105,7 +106,9 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
105106 const [ name , setName ] = React . useState ( '' ) ;
106107 const [ appTemplateId , setAppTemplateId ] = React . useState < AppTemplateId > ( 'blank' ) ;
107108 const [ dom , setDom ] = React . useState ( '' ) ;
108- const [ isNavigating , setIsNavigating ] = React . useState ( false ) ;
109+
110+ const [ isNavigatingToNewApp , setIsNavigatingToNewApp ] = React . useState ( false ) ;
111+ const [ isNavigatingToExistingApp , setIsNavigatingToExistingApp ] = React . useState ( false ) ;
109112
110113 const handleAppTemplateChange = React . useCallback (
111114 ( event : React . ChangeEvent < HTMLInputElement > ) => {
@@ -122,12 +125,24 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
122125 const createAppMutation = client . useMutation ( 'createApp' , {
123126 onSuccess : ( app ) => {
124127 window . location . href = `/_toolpad/app/${ app . id } ` ;
125- setIsNavigating ( true ) ;
128+ setIsNavigatingToNewApp ( true ) ;
126129 } ,
127130 } ) ;
128131
132+ const handleContinueButtonClick = React . useCallback ( ( ) => {
133+ setIsNavigatingToExistingApp ( true ) ;
134+ } , [ ] ) ;
135+
136+ const [ latestStoredApp , setLatestStoredApp ] = useLocalStorageState < LatestStoredAppValue > (
137+ TOOLPAD_LATEST_APP_KEY ,
138+ null ,
139+ ) ;
140+
129141 const isFormValid = Boolean ( name ) ;
130142
143+ const isSubmitting =
144+ createAppMutation . isLoading || isNavigatingToNewApp || isNavigatingToExistingApp ;
145+
131146 return (
132147 < Dialog { ...props } onClose = { config . isDemo ? NO_OP : onClose } maxWidth = "xs" >
133148 < DialogForm
@@ -146,7 +161,7 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
146161 }
147162
148163 const appDom = dom . trim ( ) ? JSON . parse ( dom ) : null ;
149- await createAppMutation . mutateAsync ( [
164+ const app = await createAppMutation . mutateAsync ( [
150165 name ,
151166 {
152167 from : {
@@ -158,13 +173,18 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
158173 } ,
159174 ] ) ;
160175
161- sendAppCreatedEvent ( name , appTemplateId ) ;
176+ setLatestStoredApp ( {
177+ appId : app . id ,
178+ appName : app . name ,
179+ } ) ;
180+
181+ sendAppCreatedEvent ( app . name , appTemplateId ) ;
162182 } }
163183 >
164184 < DialogTitle > Create a new App</ DialogTitle >
165185 < DialogContent >
166186 { config . isDemo ? (
167- < Alert severity = "warning" sx = { { mb : 2 } } >
187+ < Alert severity = "warning" sx = { { mb : 1 } } >
168188 < AlertTitle > For demo purposes only!</ AlertTitle >
169189 Your application will be ephemeral and may be deleted at any time.
170190 </ Alert >
@@ -182,6 +202,7 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
182202 createAppMutation . reset ( ) ;
183203 setName ( event . target . value ) ;
184204 } }
205+ disabled = { isSubmitting }
185206 />
186207
187208 < TextField
@@ -191,12 +212,15 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
191212 fullWidth
192213 value = { appTemplateId }
193214 onChange = { handleAppTemplateChange }
215+ disabled = { isSubmitting }
194216 >
195217 { Array . from ( APP_TEMPLATE_OPTIONS ) . map ( ( [ value , { label, description } ] ) => (
196218 < MenuItem key = { value } value = { value } >
197219 < span >
198220 < Typography > { label } </ Typography >
199- < Typography variant = "caption" > { description || '' } </ Typography >
221+ < Typography variant = "caption" sx = { { fontWeight : 'normal' } } >
222+ { description || '' }
223+ </ Typography >
200224 </ span >
201225 </ MenuItem >
202226 ) ) }
@@ -211,30 +235,53 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
211235 maxRows = { 10 }
212236 value = { dom }
213237 onChange = { handleDomChange }
238+ disabled = { isSubmitting }
214239 />
215240 ) : null }
216- { config . recaptchaSiteKey ? (
217- < Typography variant = "caption" color = "text.secondary" >
218- This site is protected by reCAPTCHA and the Google{ ' ' }
219- < Link
220- href = "https://policies.google.com/privacy"
221- underline = "none"
222- target = "_blank"
223- rel = "noopener noreferrer"
241+ { config . isDemo && latestStoredApp ? (
242+ < Box sx = { { display : 'flex' , flexDirection : 'column' , alignItems : 'center' } } >
243+ < Typography variant = "subtitle2" color = "text.secondary" textAlign = "center" >
244+ or
245+ </ Typography >
246+ < LoadingButton
247+ variant = "outlined"
248+ size = "medium"
249+ component = "a"
250+ href = { `/_toolpad/app/${ latestStoredApp . appId } ` }
251+ sx = { { mt : 0.5 } }
252+ loading = { isNavigatingToExistingApp }
253+ onClick = { handleContinueButtonClick }
254+ disabled = { isSubmitting }
224255 >
225- Privacy Policy
226- </ Link > { ' ' }
227- and{ ' ' }
228- < Link
229- href = "https://policies.google.com/terms"
230- underline = "none"
231- target = "_blank"
232- rel = "noopener noreferrer"
233- >
234- Terms of Service
235- </ Link > { ' ' }
236- apply.
237- </ Typography >
256+ Continue working on “{ latestStoredApp . appName } ”
257+ </ LoadingButton >
258+ </ Box >
259+ ) : null }
260+ { config . recaptchaSiteKey ? (
261+ < Box mt = { 2 } >
262+ < Divider sx = { { mb : 1 } } />
263+ < Typography variant = "caption" color = "text.secondary" sx = { { fontWeight : 'normal' } } >
264+ This site is protected by reCAPTCHA and the Google{ ' ' }
265+ < Link
266+ href = "https://policies.google.com/privacy"
267+ underline = "none"
268+ target = "_blank"
269+ rel = "noopener noreferrer"
270+ >
271+ Privacy Policy
272+ </ Link > { ' ' }
273+ and{ ' ' }
274+ < Link
275+ href = "https://policies.google.com/terms"
276+ underline = "none"
277+ target = "_blank"
278+ rel = "noopener noreferrer"
279+ >
280+ Terms of Service
281+ </ Link > { ' ' }
282+ apply.
283+ </ Typography >
284+ </ Box >
238285 ) : null }
239286 </ DialogContent >
240287 < DialogActions >
@@ -252,8 +299,8 @@ function CreateAppDialog({ onClose, ...props }: CreateAppDialogProps) {
252299 </ Button >
253300 < LoadingButton
254301 type = "submit"
255- loading = { createAppMutation . isLoading || isNavigating }
256- disabled = { ! isFormValid }
302+ loading = { createAppMutation . isLoading || isNavigatingToNewApp }
303+ disabled = { ! isFormValid || isSubmitting }
257304 >
258305 Create
259306 </ LoadingButton >
0 commit comments