1- const preregisterButton = document . getElementById ( 'google-preregister-button' ) ;
2- const signOutButton = document . getElementById ( 'google-signout-button' ) ;
3- const statusText = document . getElementById ( 'preregister-status' ) ;
4- const resultBanner = document . getElementById ( 'preregister-result' ) ;
5- const preregisterModal = document . getElementById ( 'preregister-modal' ) ;
6- const preregisterModalClose = document . getElementById ( 'preregister-modal-close' ) ;
1+ import { createClient } from "https://esm.sh/@supabase/supabase-js@2" ;
2+
3+ const SUPABASE_URL = "https://kvezyhwbkvpyndkaemsw.supabase.co" ;
4+ const SUPABASE_ANON_KEY = "sb_publishable_MWtbxI1_Gm_yYTSxHofq2Q_z0nGwItH" ;
5+
6+ const supabase = createClient ( SUPABASE_URL , SUPABASE_ANON_KEY , {
7+ auth : {
8+ autoRefreshToken : true ,
9+ detectSessionInUrl : true ,
10+ persistSession : true ,
11+ flowType : "pkce" ,
12+ } ,
13+ } ) ;
14+
15+ const preregisterButton = document . getElementById ( "google-preregister-button" ) ;
16+ const signOutButton = document . getElementById ( "google-signout-button" ) ;
17+ const statusText = document . getElementById ( "preregister-status" ) ;
18+ const resultBanner = document . getElementById ( "preregister-result" ) ;
19+ const preregisterModal = document . getElementById ( "preregister-modal" ) ;
20+ const preregisterModalClose = document . getElementById ( "preregister-modal-close" ) ;
721const watchModelInputs = Array . from ( document . querySelectorAll ( 'input[name="watch-model"]' ) ) ;
8- const feedbackInput = document . getElementById ( 'preregister-feedback' ) ;
9-
10- const PREREG_DRAFT_KEY = 'clawwatch-preregister-draft' ;
11- const PREREG_PENDING_KEY = 'clawwatch-preregister-pending' ;
12- const XFOR_PREREGISTER_START_URL = 'https://xfor.bot/api/v1/clawwatch/start' ;
22+ const feedbackInput = document . getElementById ( "preregister-feedback" ) ;
1323
1424let hasShownThankYou = false ;
1525
1626function showThankYouModal ( ) {
1727 if ( ! preregisterModal || hasShownThankYou ) return ;
1828 hasShownThankYou = true ;
1929 preregisterModal . hidden = false ;
20- document . body . style . overflow = ' hidden' ;
30+ document . body . style . overflow = " hidden" ;
2131}
2232
2333function closeThankYouModal ( ) {
2434 if ( ! preregisterModal ) return ;
2535 preregisterModal . hidden = true ;
26- document . body . style . overflow = '' ;
36+ document . body . style . overflow = "" ;
2737}
2838
2939function track ( eventName , params = { } ) {
30- if ( typeof window . gtag === ' function' ) {
31- window . gtag ( ' event' , eventName , params ) ;
40+ if ( typeof window . gtag === " function" ) {
41+ window . gtag ( " event" , eventName , params ) ;
3242 }
3343}
3444
35- function setBanner ( message , tone = ' info' ) {
45+ function setBanner ( message , tone = " info" ) {
3646 if ( ! resultBanner ) return ;
3747 resultBanner . hidden = false ;
3848 resultBanner . dataset . tone = tone ;
@@ -42,144 +52,150 @@ function setBanner(message, tone = 'info') {
4252function clearBanner ( ) {
4353 if ( ! resultBanner ) return ;
4454 resultBanner . hidden = true ;
45- resultBanner . textContent = '' ;
55+ resultBanner . textContent = "" ;
4656 delete resultBanner . dataset . tone ;
4757}
4858
4959function getFormState ( ) {
5060 return {
51- watchModels : watchModelInputs . filter ( ( input ) => input . checked ) . map ( ( input ) => input . value ) ,
52- feedback : feedbackInput ?. value . trim ( ) || '' ,
61+ watchModels : watchModelInputs . filter ( ( i ) => i . checked ) . map ( ( i ) => i . value ) ,
62+ feedback : feedbackInput ?. value . trim ( ) || "" ,
5363 } ;
5464}
5565
56- function saveDraftState ( ) {
57- try {
58- window . localStorage . setItem ( PREREG_DRAFT_KEY , JSON . stringify ( getFormState ( ) ) ) ;
59- } catch { }
60- }
61-
62- function loadDraftState ( ) {
63- try {
64- const raw = window . localStorage . getItem ( PREREG_DRAFT_KEY ) ;
65- if ( ! raw ) return null ;
66- return JSON . parse ( raw ) ;
67- } catch {
68- return null ;
69- }
70- }
71-
72- function clearDraftState ( ) {
73- try {
74- window . localStorage . removeItem ( PREREG_DRAFT_KEY ) ;
75- } catch { }
76- }
77-
78- function setPendingPreregistration ( isPending ) {
79- try {
80- if ( isPending ) {
81- window . localStorage . setItem ( PREREG_PENDING_KEY , '1' ) ;
82- } else {
83- window . localStorage . removeItem ( PREREG_PENDING_KEY ) ;
84- }
85- } catch { }
86- }
87-
88- function applyDraftState ( draft = { } ) {
89- const selected = Array . isArray ( draft . watchModels ) ? draft . watchModels : [ ] ;
90- const selectedSet = new Set ( selected ) ;
91- watchModelInputs . forEach ( ( input ) => {
92- input . checked = selectedSet . has ( input . value ) ;
93- } ) ;
94- if ( feedbackInput ) {
95- feedbackInput . value = typeof draft . feedback === 'string' ? draft . feedback : '' ;
96- }
66+ function applyFormState ( watchModels = [ ] , feedback = "" ) {
67+ const selected = new Set ( watchModels ) ;
68+ watchModelInputs . forEach ( ( i ) => { i . checked = selected . has ( i . value ) ; } ) ;
69+ if ( feedbackInput ) feedbackInput . value = feedback ;
9770}
9871
9972function renderReady ( ) {
10073 clearBanner ( ) ;
101- if ( statusText ) {
102- statusText . textContent = 'Sign in with Google and we will mark your account for the future install-ready ClawWatch release.' ;
103- }
74+ if ( statusText ) statusText . textContent = "Sign in with Google and we will mark your account for the future install-ready ClawWatch release." ;
10475 if ( preregisterButton ) {
10576 preregisterButton . disabled = false ;
10677 preregisterButton . innerHTML = '<span class="google-mark">G</span><span>Continue with Google</span>' ;
10778 }
108- if ( signOutButton ) {
109- signOutButton . hidden = true ;
110- }
79+ if ( signOutButton ) signOutButton . hidden = true ;
11180}
11281
11382function renderBusy ( message ) {
11483 clearBanner ( ) ;
115- if ( statusText ) {
116- statusText . textContent = message ;
117- }
84+ if ( statusText ) statusText . textContent = message ;
11885 if ( preregisterButton ) {
11986 preregisterButton . disabled = true ;
120- preregisterButton . innerHTML = '<span class="google-mark">… </span><span>Working… </span>' ;
87+ preregisterButton . innerHTML = '<span class="google-mark">\u2026 </span><span>Working\u2026 </span>' ;
12188 }
122- if ( signOutButton ) {
123- signOutButton . hidden = true ;
89+ if ( signOutButton ) signOutButton . hidden = true ;
90+ }
91+
92+ function renderRegistered ( email ) {
93+ if ( statusText ) statusText . textContent = "Registered as " + email + ". Update your details below any time." ;
94+ if ( preregisterButton ) {
95+ preregisterButton . disabled = false ;
96+ preregisterButton . innerHTML = '<span class="google-mark">\u2713</span><span>Update my details</span>' ;
12497 }
98+ if ( signOutButton ) signOutButton . hidden = false ;
99+ setBanner ( "Thank you for your interest! We will notify you when the easy-install ClawWatch is ready." , "success" ) ;
125100}
126101
127- function buildSharedAuthRedirectUrl ( ) {
102+ async function savePreregistration ( user ) {
128103 const { watchModels, feedback } = getFormState ( ) ;
129- const params = new URLSearchParams ( {
130- return_to : `${ window . location . origin } ${ window . location . pathname } ?preregister=complete#preregister` ,
131- watch_models : watchModels . join ( ',' ) ,
132- feedback,
104+ renderBusy ( "Saving your preregistration..." ) ;
105+
106+ const row = {
107+ user_id : user . id ,
108+ email : user . email ,
109+ watch_models : watchModels ,
110+ has_watch : watchModels . length > 0 && ! watchModels . includes ( "no-watch-yet" ) ,
111+ feedback : feedback ,
112+ source : window . location . host ,
113+ registered_at : new Date ( ) . toISOString ( ) ,
114+ updated_at : new Date ( ) . toISOString ( ) ,
115+ } ;
116+
117+ const { error } = await supabase . from ( "preregistrations" ) . upsert ( row , { onConflict : "user_id" } ) ;
118+
119+ if ( error ) {
120+ console . error ( "preregistrations upsert failed:" , error ) ;
121+ setBanner ( "Sign-in worked but saving failed: " + error . message , "error" ) ;
122+ renderReady ( ) ;
123+ return ;
124+ }
125+
126+ track ( "clawwatch_preregister_complete" ) ;
127+ applyFormState ( watchModels , feedback ) ;
128+ renderRegistered ( user . email || "your account" ) ;
129+ showThankYouModal ( ) ;
130+ }
131+
132+ async function startGoogleSignIn ( ) {
133+ clearBanner ( ) ;
134+ renderBusy ( "Redirecting to Google sign-in..." ) ;
135+ track ( "clawwatch_preregister_start" ) ;
136+
137+ const redirectTo = window . location . origin + window . location . pathname + "?preregister=complete#preregister" ;
138+ const { data, error } = await supabase . auth . signInWithOAuth ( {
139+ provider : "google" ,
140+ options : { redirectTo : redirectTo , queryParams : { prompt : "select_account" } } ,
133141 } ) ;
134- return `${ XFOR_PREREGISTER_START_URL } ?${ params . toString ( ) } ` ;
142+
143+ if ( error ) {
144+ renderReady ( ) ;
145+ setBanner ( error . message , "error" ) ;
146+ }
135147}
136148
137- function startGoogleSignIn ( ) {
138- renderBusy ( 'Redirecting to Google sign-in…' ) ;
139- track ( 'clawwatch_preregister_start' ) ;
140- saveDraftState ( ) ;
141- setPendingPreregistration ( true ) ;
142- window . location . assign ( buildSharedAuthRedirectUrl ( ) ) ;
149+ async function signOut ( ) {
150+ await supabase . auth . signOut ( ) ;
151+ renderReady ( ) ;
143152}
144153
145- function init ( ) {
146- preregisterButton ?. addEventListener ( 'click' , startGoogleSignIn ) ;
147- preregisterModalClose ?. addEventListener ( 'click' , closeThankYouModal ) ;
148- preregisterModal ?. addEventListener ( 'click' , ( event ) => {
149- if ( event . target ?. dataset ?. closeModal === 'true' ) {
150- closeThankYouModal ( ) ;
154+ async function init ( ) {
155+ preregisterButton ?. addEventListener ( "click" , async function ( ) {
156+ const { data } = await supabase . auth . getSession ( ) ;
157+ if ( data . session ?. user ) {
158+ await savePreregistration ( data . session . user ) ;
159+ } else {
160+ await startGoogleSignIn ( ) ;
151161 }
152162 } ) ;
163+ signOutButton ?. addEventListener ( "click" , signOut ) ;
164+ preregisterModalClose ?. addEventListener ( "click" , closeThankYouModal ) ;
165+ preregisterModal ?. addEventListener ( "click" , function ( e ) {
166+ if ( e . target ?. dataset ?. closeModal === "true" ) closeThankYouModal ( ) ;
167+ } ) ;
153168
154- const savedDraft = loadDraftState ( ) ;
155- if ( savedDraft ) {
156- applyDraftState ( savedDraft ) ;
157- }
169+ const { data, error } = await supabase . auth . getSession ( ) ;
170+ if ( error ) { setBanner ( error . message , "error" ) ; return ; }
158171
159- const url = new URL ( window . location . href ) ;
160- const preregStatus = url . searchParams . get ( 'preregister' ) ;
172+ const isReturning = new URLSearchParams ( window . location . search ) . get ( "preregister" ) === "complete" ;
161173
162- if ( preregStatus === 'complete' ) {
163- if ( savedDraft ) {
164- applyDraftState ( savedDraft ) ;
174+ if ( data . session ?. user ) {
175+ if ( isReturning ) {
176+ await savePreregistration ( data . session . user ) ;
177+ } else {
178+ renderRegistered ( data . session . user . email || "your account" ) ;
165179 }
166- clearDraftState ( ) ;
167- setPendingPreregistration ( false ) ;
168- renderReady ( ) ;
169- setBanner ( "Thank you for your interest! We'll be back when the easy-to-install ClawWatch is here." , 'success' ) ;
170- showThankYouModal ( ) ;
171- } else if ( preregStatus === 'error' ) {
172- renderReady ( ) ;
173- setPendingPreregistration ( false ) ;
174- setBanner ( 'Google sign-in finished, but ClawWatch preregistration was not stored. Please try again.' , 'error' ) ;
175180 } else {
176181 renderReady ( ) ;
177182 }
178183
179- if ( preregStatus ) {
180- const cleanUrl = ` ${ window . location . origin } ${ window . location . pathname } ${ window . location . hash || '' } ` ;
181- window . history . replaceState ( { } , document . title , cleanUrl ) ;
184+ if ( isReturning ) {
185+ var clean = window . location . origin + window . location . pathname + ( window . location . hash || "" ) ;
186+ window . history . replaceState ( { } , document . title , clean ) ;
182187 }
188+
189+ supabase . auth . onAuthStateChange ( async function ( event , session ) {
190+ if ( event === "SIGNED_IN" && session ?. user ) {
191+ await savePreregistration ( session . user ) ;
192+ } else if ( event === "SIGNED_OUT" ) {
193+ renderReady ( ) ;
194+ }
195+ } ) ;
183196}
184197
185- init ( ) ;
198+ init ( ) . catch ( function ( err ) {
199+ renderReady ( ) ;
200+ setBanner ( err . message || "Failed to initialize." , "error" ) ;
201+ } ) ;
0 commit comments