added signup callback page that runs an api call to finish signup

This commit is contained in:
Carter 2025-07-03 13:41:09 -06:00
parent e654aa002b
commit 24d27121d1
5 changed files with 142 additions and 21 deletions

View file

@ -19,7 +19,7 @@ if ('serviceWorker' in navigator) {
} }
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> // <StrictMode>
<App /> <App />
</StrictMode>, // </StrictMode>,
) )

View file

@ -9,6 +9,7 @@ import { getWhitelabel } from "services/whiteLabel";
import Ventilation from "pages/VentEditor"; import Ventilation from "pages/VentEditor";
import { useGlobalState } from "providers"; import { useGlobalState } from "providers";
import DeviceSupport from "pages/DeviceSupport"; import DeviceSupport from "pages/DeviceSupport";
import SignupCallback from "pages/SignupCallback";
const DeviceHistory = lazy(() => import("pages/DeviceHistory")); const DeviceHistory = lazy(() => import("pages/DeviceHistory"));
const DevicePage = lazy(() => import("pages/Device")); const DevicePage = lazy(() => import("pages/Device"));
@ -302,6 +303,8 @@ export default function Router() {
{/* Redirects */} {/* Redirects */}
<Route path="/callback" element={<Navigate to="/" />} /> <Route path="/callback" element={<Navigate to="/" />} />
{/* <Route path="/signupCallback" element={<Navigate to="/" />} /> */}
<Route path="/signupCallback" element={<SignupCallback />} />
{/* <Route path="/login" element={<Login />} /> */} {/* <Route path="/login" element={<Login />} /> */}
{/* <Route path="/team/:teamID" element={<Navigate to="/teams/:teamID" />} /> */} {/* <Route path="/team/:teamID" element={<Navigate to="/teams/:teamID" />} /> */}
{/* <Route path="/device/:deviceID" element={<Navigate to="/devices/:devicesID" />} /> */} {/* <Route path="/device/:deviceID" element={<Navigate to="/devices/:devicesID" />} /> */}

View file

@ -0,0 +1,87 @@
import { useDeviceAPI, useUserAPI } from "hooks";
import PageContainer from "./PageContainer";
import { useEffect, useState } from "react";
import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider } from "@mui/material";
import { useBinAPI, useTeamAPI } from "providers";
import { CheckCircleOutline } from "@mui/icons-material";
import { green } from "@mui/material/colors";
interface Props {
}
export default function SignupCallback (props: Props) {
const userAPI = useUserAPI();
const teamAPI = useTeamAPI()
const deviceAPI = useDeviceAPI();
const binAPI = useBinAPI();
const [loading, setLoading] = useState(false)
const [signupDialogOpen, setSignupDialogOpen] = useState(true)
const [hasTeams, setHasTeams] = useState(false)
const [hasDevices, setHasDevices] = useState(false)
const [hasBins, setHasBins] = useState(false)
const [doneBins, setDoneBins] = useState(false)
const [doneTeams, setDoneTeams] = useState(false)
const [doneDevices, setDoneDevices] = useState(false)
useEffect(() => {
console.log(hasTeams)
}, [hasTeams])
useEffect(() => {
setLoading(true)
userAPI.completeSignup().then(resp => {
console.log(resp)
teamAPI.listTeams(1, 0).then(resp => {
setHasTeams(resp.data.teams.length > 0)
}).finally(() => setDoneTeams(true))
deviceAPI.list(1, 0).then(resp => {
setHasDevices(resp.data.devices.length > 0)
}).finally(() => setDoneDevices(true))
binAPI.listBins(1, 0).then(resp => {
setHasBins(resp.data.bins.length > 0)
}).finally(() => setDoneBins(true))
}).finally(() => setLoading(false))
}, [])
const loadingComplete = () => {
return loading === false && doneBins && doneTeams && doneDevices
}
const content = () => {
return (
<DialogContent sx={{ margin: "auto", textAlign: "center" }}>
{ loadingComplete() ?
<CheckCircleOutline sx={{ fontSize: 64, color: green[500] }}/>
:
<CircularProgress size={64}/>
}
<DialogContentText sx={{marginTop: 2}}>
{hasTeams && "Teams found!"}
{hasDevices && "Devices found!"}
{hasBins && "Bins found!"}
</DialogContentText>
</DialogContent>
)
}
return (
<PageContainer spacing={1}>
<Dialog open={signupDialogOpen} >
<DialogTitle>
Finishing Signup
</DialogTitle>
<Divider sx={{ marginBottom: 2 }}/>
{ content() }
<DialogActions>
{loadingComplete() &&
<Button onClick={() => setSignupDialogOpen(false)}>
Done
</Button>
}
</DialogActions>
</Dialog>
</PageContainer>
)
}

View file

@ -9,28 +9,30 @@ interface Props extends PropsWithChildren<any>{
export default function AuthWrapper(props: Props) { export default function AuthWrapper(props: Props) {
const { children, setToken } = props; const { children, setToken } = props;
const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently } = useAuth0(); const { isLoading, loginWithRedirect, isAuthenticated, getAccessTokenSilently, getIdTokenClaims } = useAuth0();
useEffect(() => { useEffect(() => {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const isManualLoginPage = url.pathname.includes("/login"); const isManualLoginPage = url.pathname.includes("/login");
const options: RedirectLoginOptions = {
authorizationParams: {} // Initialize here
};
if (isManualLoginPage) { if (isManualLoginPage && options.authorizationParams) {
const options: RedirectLoginOptions = {
authorizationParams: {} // Initialize here
};
let parsed = queryString.parse(url.search); let parsed = queryString.parse(url.search);
if (parsed.signup === "true") { if (parsed.signup === "true") {
options.authorizationParams!.screen_hint = "signup"; options.authorizationParams.screen_hint = "signUp";
options.authorizationParams!.prompt = "login"; // options.authorizationParams.m
// options.authorizationParams.prompt = "login";
} }
if (parsed.email && parsed.email !== "") { if (parsed.email && parsed.email !== "") {
options.authorizationParams!.login_hint = parsed.email.toString(); // prefill email options.authorizationParams.login_hint = parsed.email.toString(); // prefill email
} }
options.authorizationParams.redirect_uri = "http://localhost:5173/signupCallback"
loginWithRedirect(options) loginWithRedirect(options)
@ -40,15 +42,28 @@ export default function AuthWrapper(props: Props) {
}, [isAuthenticated, loginWithRedirect, isLoading]); }, [isAuthenticated, loginWithRedirect, isLoading]);
useEffect(() => { useEffect(() => {
if (isAuthenticated) getAccessTokenSilently().then(t => { const url = new URL(window.location.href);
if (t.length < 1) { const isSignupCallback = url.pathname.includes("/signupCallback");
if (isAuthenticated) {
getAccessTokenSilently().then(t => {
if (t.length < 1) {
loginWithRedirect()
}
setToken(t)
}).catch(() => {
// No token, go back to login
loginWithRedirect() loginWithRedirect()
})
if (isSignupCallback) {
console.log("IS SIGNUP")
getIdTokenClaims().then(id_claims => {
console.log(id_claims)
console.log(id_claims?.nickname)
if (id_claims) console.log(Object.keys(id_claims))
})
} }
setToken(t)
}).catch(() => { }
// No token, go back to login
loginWithRedirect()
})
}, [setToken, isAuthenticated]) }, [setToken, isAuthenticated])
return children; return children;

View file

@ -28,6 +28,7 @@ export interface IUserAPIContext {
prefixSearch?: string, prefixSearch?: string,
) => Promise<ListUsersResponse>; ) => Promise<ListUsersResponse>;
listProfiles: () => Promise<pond.UserProfile[]>; listProfiles: () => Promise<pond.UserProfile[]>;
completeSignup: () => Promise<pond.CompleteSignupUserResponse>;
listObjectUsers: (scope: Scope) => Promise<any>; listObjectUsers: (scope: Scope) => Promise<any>;
updateObjectUsers: (scope: Scope, users: pond.IUser[]) => Promise<any>; updateObjectUsers: (scope: Scope, users: pond.IUser[]) => Promise<any>;
getUser: (id: string, scope?: Scope) => Promise<User>; getUser: (id: string, scope?: Scope) => Promise<User>;
@ -55,8 +56,22 @@ export const UserAPIContext = createContext<IUserAPIContext>({} as IUserAPIConte
export default function UserProvider(props: PropsWithChildren<any>) { export default function UserProvider(props: PropsWithChildren<any>) {
const { children } = props; const { children } = props;
const { get, put } = useHTTP(); const { get, put, post } = useHTTP();
const [{ as }] = useGlobalState(); const [{ as, user }] = useGlobalState();
const completeSignup = (): Promise<pond.CompleteSignupUserResponse> => {
return new Promise((resolve, reject) => {
let request = pond.CompleteSignupUserRequest.create();
request.avatar = user.settings.avatar
request.email = user.settings.email
request.id = user.id()
request.name = user.name()
post(pondURL("/signups"), request).then(res => {
console.log(res)
resolve(pond.CompleteSignupUserResponse.create())
})
})
}
const listUsers = ( const listUsers = (
limit: number, limit: number,
@ -234,6 +249,7 @@ export default function UserProvider(props: PropsWithChildren<any>) {
return ( return (
<UserAPIContext.Provider <UserAPIContext.Provider
value={{ value={{
completeSignup,
listUsers, listUsers,
listProfiles, listProfiles,
listObjectUsers, listObjectUsers,