boring stuff
This commit is contained in:
		@@ -52,7 +52,7 @@ export const LoginForm = (props: LoginFormProps) => {
 | 
			
		||||
			</Form>
 | 
			
		||||
 | 
			
		||||
			<div style={{ marginTop: "1rem" }}>
 | 
			
		||||
				Or <Link href={Routes.SignupPage()}>Sign Up</Link>
 | 
			
		||||
				Or <Link href={Routes.SignUp()}>Sign Up</Link>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { BlitzPage, useMutation } from "blitz";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes, useMutation } from "blitz";
 | 
			
		||||
 | 
			
		||||
import BaseLayout from "../../core/layouts/base-layout";
 | 
			
		||||
import { LabeledTextField } from "../../core/components/labeled-text-field";
 | 
			
		||||
@@ -44,7 +45,8 @@ const ForgotPasswordPage: BlitzPage = () => {
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ForgotPasswordPage.redirectAuthenticatedTo = "/";
 | 
			
		||||
ForgotPasswordPage.redirectAuthenticatedTo = Routes.Messages();
 | 
			
		||||
 | 
			
		||||
ForgotPasswordPage.getLayout = (page) => (
 | 
			
		||||
	<BaseLayout title="Forgot Your Password?">{page}</BaseLayout>
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { BlitzPage, useRouterQuery, Link, useMutation, Routes } from "blitz";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { useRouterQuery, Link, useMutation, Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import BaseLayout from "../../core/layouts/base-layout";
 | 
			
		||||
import { LabeledTextField } from "../../core/components/labeled-text-field";
 | 
			
		||||
@@ -59,7 +60,8 @@ const ResetPasswordPage: BlitzPage = () => {
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ResetPasswordPage.redirectAuthenticatedTo = "/";
 | 
			
		||||
ResetPasswordPage.redirectAuthenticatedTo = Routes.Messages();
 | 
			
		||||
 | 
			
		||||
ResetPasswordPage.getLayout = (page) => <BaseLayout title="Reset Your Password">{page}</BaseLayout>;
 | 
			
		||||
 | 
			
		||||
export default ResetPasswordPage;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
import { useRouter, BlitzPage } from "blitz";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { useRouter, Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import BaseLayout from "../../core/layouts/base-layout";
 | 
			
		||||
import { LoginForm } from "../components/login-form";
 | 
			
		||||
 | 
			
		||||
const LoginPage: BlitzPage = () => {
 | 
			
		||||
const SignIn: BlitzPage = () => {
 | 
			
		||||
	const router = useRouter();
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
@@ -12,7 +13,7 @@ const LoginPage: BlitzPage = () => {
 | 
			
		||||
				onSuccess={() => {
 | 
			
		||||
					const next = router.query.next
 | 
			
		||||
						? decodeURIComponent(router.query.next as string)
 | 
			
		||||
						: "/";
 | 
			
		||||
						: Routes.Messages();
 | 
			
		||||
					router.push(next);
 | 
			
		||||
				}}
 | 
			
		||||
			/>
 | 
			
		||||
@@ -20,7 +21,8 @@ const LoginPage: BlitzPage = () => {
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
LoginPage.redirectAuthenticatedTo = "/";
 | 
			
		||||
LoginPage.getLayout = (page) => <BaseLayout title="Log In">{page}</BaseLayout>;
 | 
			
		||||
SignIn.redirectAuthenticatedTo = Routes.Messages();
 | 
			
		||||
 | 
			
		||||
export default LoginPage;
 | 
			
		||||
SignIn.getLayout = (page) => <BaseLayout title="Sign In">{page}</BaseLayout>;
 | 
			
		||||
 | 
			
		||||
export default SignIn;
 | 
			
		||||
@@ -1,19 +1,21 @@
 | 
			
		||||
import { useRouter, BlitzPage, Routes } from "blitz";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { useRouter, Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import BaseLayout from "../../core/layouts/base-layout";
 | 
			
		||||
import { SignupForm } from "../components/signup-form";
 | 
			
		||||
 | 
			
		||||
const SignupPage: BlitzPage = () => {
 | 
			
		||||
const SignUp: BlitzPage = () => {
 | 
			
		||||
	const router = useRouter();
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<div>
 | 
			
		||||
			<SignupForm onSuccess={() => router.push(Routes.Home())} />
 | 
			
		||||
			<SignupForm onSuccess={() => router.push(Routes.StepOne())} />
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SignupPage.redirectAuthenticatedTo = "/";
 | 
			
		||||
SignupPage.getLayout = (page) => <BaseLayout title="Sign Up">{page}</BaseLayout>;
 | 
			
		||||
SignUp.redirectAuthenticatedTo = Routes.StepOne();
 | 
			
		||||
 | 
			
		||||
export default SignupPage;
 | 
			
		||||
SignUp.getLayout = (page) => <BaseLayout title="Sign Up">{page}</BaseLayout>;
 | 
			
		||||
 | 
			
		||||
export default SignUp;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes } from "blitz";
 | 
			
		||||
import type { FunctionComponent } from "react";
 | 
			
		||||
import { atom, useAtom } from "jotai";
 | 
			
		||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 | 
			
		||||
@@ -7,71 +8,64 @@ import { faBackspace, faPhoneAlt as faPhone } from "@fortawesome/pro-solid-svg-i
 | 
			
		||||
import Layout from "../../core/layouts/layout";
 | 
			
		||||
import useRequireOnboarding from "../../core/hooks/use-require-onboarding";
 | 
			
		||||
 | 
			
		||||
const pageTitle = "Keypad";
 | 
			
		||||
 | 
			
		||||
const Keypad: BlitzPage = () => {
 | 
			
		||||
	useRequireOnboarding();
 | 
			
		||||
	const phoneNumber = useAtom(phoneNumberAtom)[0];
 | 
			
		||||
	const pressBackspace = useAtom(pressBackspaceAtom)[1];
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<Layout title={pageTitle}>
 | 
			
		||||
			<div className="w-96 h-full flex flex-col justify-around py-5 mx-auto text-center text-black bg-white">
 | 
			
		||||
				<div className="h-16 text-3xl text-gray-700">
 | 
			
		||||
					<span>{phoneNumber}</span>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
				<section>
 | 
			
		||||
					<Row>
 | 
			
		||||
						<Digit digit="1" />
 | 
			
		||||
						<Digit digit="2">
 | 
			
		||||
							<DigitLetters>ABC</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
						<Digit digit="3">
 | 
			
		||||
							<DigitLetters>DEF</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
					</Row>
 | 
			
		||||
					<Row>
 | 
			
		||||
						<Digit digit="4">
 | 
			
		||||
							<DigitLetters>GHI</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
						<Digit digit="5">
 | 
			
		||||
							<DigitLetters>JKL</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
						<Digit digit="6">
 | 
			
		||||
							<DigitLetters>MNO</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
					</Row>
 | 
			
		||||
					<Row>
 | 
			
		||||
						<Digit digit="7">
 | 
			
		||||
							<DigitLetters>PQRS</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
						<Digit digit="8">
 | 
			
		||||
							<DigitLetters>TUV</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
						<Digit digit="9">
 | 
			
		||||
							<DigitLetters>WXYZ</DigitLetters>
 | 
			
		||||
						</Digit>
 | 
			
		||||
					</Row>
 | 
			
		||||
					<Row>
 | 
			
		||||
						<Digit digit="*" />
 | 
			
		||||
						<ZeroDigit />
 | 
			
		||||
						<Digit digit="#" />
 | 
			
		||||
					</Row>
 | 
			
		||||
					<Row>
 | 
			
		||||
						<div className="cursor-pointer select-none col-start-2 h-12 w-12 flex justify-center items-center mx-auto bg-green-800 rounded-full">
 | 
			
		||||
							<FontAwesomeIcon icon={faPhone} color="white" size="lg" />
 | 
			
		||||
						</div>
 | 
			
		||||
						<div
 | 
			
		||||
							className="cursor-pointer select-none my-auto"
 | 
			
		||||
							onClick={pressBackspace}
 | 
			
		||||
						>
 | 
			
		||||
							<FontAwesomeIcon icon={faBackspace} size="lg" />
 | 
			
		||||
						</div>
 | 
			
		||||
					</Row>
 | 
			
		||||
				</section>
 | 
			
		||||
		<div className="w-96 h-full flex flex-col justify-around py-5 mx-auto text-center text-black bg-white">
 | 
			
		||||
			<div className="h-16 text-3xl text-gray-700">
 | 
			
		||||
				<span>{phoneNumber}</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</Layout>
 | 
			
		||||
 | 
			
		||||
			<section>
 | 
			
		||||
				<Row>
 | 
			
		||||
					<Digit digit="1" />
 | 
			
		||||
					<Digit digit="2">
 | 
			
		||||
						<DigitLetters>ABC</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
					<Digit digit="3">
 | 
			
		||||
						<DigitLetters>DEF</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
				</Row>
 | 
			
		||||
				<Row>
 | 
			
		||||
					<Digit digit="4">
 | 
			
		||||
						<DigitLetters>GHI</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
					<Digit digit="5">
 | 
			
		||||
						<DigitLetters>JKL</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
					<Digit digit="6">
 | 
			
		||||
						<DigitLetters>MNO</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
				</Row>
 | 
			
		||||
				<Row>
 | 
			
		||||
					<Digit digit="7">
 | 
			
		||||
						<DigitLetters>PQRS</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
					<Digit digit="8">
 | 
			
		||||
						<DigitLetters>TUV</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
					<Digit digit="9">
 | 
			
		||||
						<DigitLetters>WXYZ</DigitLetters>
 | 
			
		||||
					</Digit>
 | 
			
		||||
				</Row>
 | 
			
		||||
				<Row>
 | 
			
		||||
					<Digit digit="*" />
 | 
			
		||||
					<ZeroDigit />
 | 
			
		||||
					<Digit digit="#" />
 | 
			
		||||
				</Row>
 | 
			
		||||
				<Row>
 | 
			
		||||
					<div className="cursor-pointer select-none col-start-2 h-12 w-12 flex justify-center items-center mx-auto bg-green-800 rounded-full">
 | 
			
		||||
						<FontAwesomeIcon icon={faPhone} color="white" size="lg" />
 | 
			
		||||
					</div>
 | 
			
		||||
					<div className="cursor-pointer select-none my-auto" onClick={pressBackspace}>
 | 
			
		||||
						<FontAwesomeIcon icon={faBackspace} size="lg" />
 | 
			
		||||
					</div>
 | 
			
		||||
				</Row>
 | 
			
		||||
			</section>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -123,4 +117,8 @@ const pressBackspaceAtom = atom(null, (get, set) => {
 | 
			
		||||
	set(phoneNumberAtom, (prevState) => prevState.slice(0, -1));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Keypad.getLayout = (page) => <Layout title="Keypad">{page}</Layout>;
 | 
			
		||||
 | 
			
		||||
Keypad.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default Keypad;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Link, useQuery } from "blitz";
 | 
			
		||||
import { Link, useQuery, Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import getConversationsQuery from "../queries/get-conversations";
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,11 @@ export default function ConversationsList() {
 | 
			
		||||
				const lastMessage = messages[messages.length - 1]!;
 | 
			
		||||
				return (
 | 
			
		||||
					<li key={recipient} className="py-2">
 | 
			
		||||
						<Link href={`/messages/${encodeURIComponent(recipient)}`}>
 | 
			
		||||
						<Link
 | 
			
		||||
							href={Routes.ConversationPage({
 | 
			
		||||
								recipient: encodeURIComponent(recipient),
 | 
			
		||||
							})}
 | 
			
		||||
						>
 | 
			
		||||
							<a className="flex flex-col">
 | 
			
		||||
								<div className="flex flex-row justify-between">
 | 
			
		||||
									<strong>{recipient}</strong>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { Suspense } from "react";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import Layout from "../../core/layouts/layout";
 | 
			
		||||
import ConversationsList from "../components/conversations-list";
 | 
			
		||||
@@ -9,17 +10,19 @@ const Messages: BlitzPage = () => {
 | 
			
		||||
	useRequireOnboarding();
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<Layout title="Messages">
 | 
			
		||||
		<>
 | 
			
		||||
			<div className="flex flex-col space-y-6 p-6">
 | 
			
		||||
				<p>Messages page</p>
 | 
			
		||||
			</div>
 | 
			
		||||
			<Suspense fallback="Loading...">
 | 
			
		||||
				<ConversationsList />
 | 
			
		||||
			</Suspense>
 | 
			
		||||
		</Layout>
 | 
			
		||||
		</>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Messages.authenticate = true;
 | 
			
		||||
Messages.getLayout = (page) => <Layout title="Messages">{page}</Layout>;
 | 
			
		||||
 | 
			
		||||
Messages.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default Messages;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { Suspense } from "react";
 | 
			
		||||
import { BlitzPage, useRouter } from "blitz";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes, useRouter } from "blitz";
 | 
			
		||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 | 
			
		||||
import {
 | 
			
		||||
	faLongArrowLeft,
 | 
			
		||||
@@ -16,10 +17,9 @@ const ConversationPage: BlitzPage = () => {
 | 
			
		||||
 | 
			
		||||
	const router = useRouter();
 | 
			
		||||
	const recipient = router.params.recipient;
 | 
			
		||||
	const pageTitle = `Messages with ${recipient}`;
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<Layout title={pageTitle} hideFooter>
 | 
			
		||||
		<>
 | 
			
		||||
			<header className="absolute top-0 w-screen h-12 backdrop-filter backdrop-blur-sm bg-white bg-opacity-75 border-b grid grid-cols-3 items-center">
 | 
			
		||||
				<span className="col-start-1 col-span-1 pl-2 cursor-pointer" onClick={router.back}>
 | 
			
		||||
					<FontAwesomeIcon size="lg" className="h-8 w-8" icon={faLongArrowLeft} />
 | 
			
		||||
@@ -33,10 +33,22 @@ const ConversationPage: BlitzPage = () => {
 | 
			
		||||
			<Suspense fallback={<div className="pt-12">Loading messages with {recipient}</div>}>
 | 
			
		||||
				<Conversation />
 | 
			
		||||
			</Suspense>
 | 
			
		||||
		</>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConversationPage.getLayout = function ConversationLayout(page) {
 | 
			
		||||
	const router = useRouter();
 | 
			
		||||
	const recipient = router.params.recipient;
 | 
			
		||||
	const pageTitle = `Messages with ${recipient}`;
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<Layout title={pageTitle} hideFooter>
 | 
			
		||||
			{page}
 | 
			
		||||
		</Layout>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConversationPage.authenticate = true;
 | 
			
		||||
ConversationPage.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default ConversationPage;
 | 
			
		||||
 
 | 
			
		||||
@@ -9,18 +9,22 @@ const StepOne: BlitzPage = () => {
 | 
			
		||||
	useCurrentCustomer(); // preload for step two
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<OnboardingLayout
 | 
			
		||||
			currentStep={1}
 | 
			
		||||
			next={{ href: "/welcome/step-two", label: "Set up your phone number" }}
 | 
			
		||||
		>
 | 
			
		||||
			<div className="flex flex-col space-y-4 items-center">
 | 
			
		||||
				<span>Welcome, let’s set up your virtual phone!</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</OnboardingLayout>
 | 
			
		||||
		<div className="flex flex-col space-y-4 items-center">
 | 
			
		||||
			<span>Welcome, let’s set up your virtual phone!</span>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
StepOne.authenticate = true;
 | 
			
		||||
StepOne.getLayout = (page) => (
 | 
			
		||||
	<OnboardingLayout
 | 
			
		||||
		currentStep={1}
 | 
			
		||||
		next={{ href: Routes.StepTwo().pathname, label: "Set up your phone number" }}
 | 
			
		||||
	>
 | 
			
		||||
		{page}
 | 
			
		||||
	</OnboardingLayout>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
StepOne.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
 | 
			
		||||
	const session = await getSession(req, res);
 | 
			
		||||
 
 | 
			
		||||
@@ -48,44 +48,45 @@ const StepThree: BlitzPage<Props> = ({ availablePhoneNumbers }) => {
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<OnboardingLayout currentStep={3} previous={{ href: "/welcome/step-two", label: "Back" }}>
 | 
			
		||||
			<div className="flex flex-col space-y-4 items-center">
 | 
			
		||||
				<form onSubmit={onSubmit}>
 | 
			
		||||
					<label
 | 
			
		||||
						htmlFor="phoneNumberSid"
 | 
			
		||||
						className="block text-sm font-medium text-gray-700"
 | 
			
		||||
					>
 | 
			
		||||
						Phone number
 | 
			
		||||
					</label>
 | 
			
		||||
					<select
 | 
			
		||||
						id="phoneNumberSid"
 | 
			
		||||
						className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
 | 
			
		||||
						{...register("phoneNumberSid")}
 | 
			
		||||
					>
 | 
			
		||||
						{availablePhoneNumbers.map(({ sid, phoneNumber }) => (
 | 
			
		||||
							<option value={sid} key={sid}>
 | 
			
		||||
								{phoneNumber}
 | 
			
		||||
							</option>
 | 
			
		||||
						))}
 | 
			
		||||
					</select>
 | 
			
		||||
		<div className="flex flex-col space-y-4 items-center">
 | 
			
		||||
			<form onSubmit={onSubmit}>
 | 
			
		||||
				<label htmlFor="phoneNumberSid" className="block text-sm font-medium text-gray-700">
 | 
			
		||||
					Phone number
 | 
			
		||||
				</label>
 | 
			
		||||
				<select
 | 
			
		||||
					id="phoneNumberSid"
 | 
			
		||||
					className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md"
 | 
			
		||||
					{...register("phoneNumberSid")}
 | 
			
		||||
				>
 | 
			
		||||
					{availablePhoneNumbers.map(({ sid, phoneNumber }) => (
 | 
			
		||||
						<option value={sid} key={sid}>
 | 
			
		||||
							{phoneNumber}
 | 
			
		||||
						</option>
 | 
			
		||||
					))}
 | 
			
		||||
				</select>
 | 
			
		||||
 | 
			
		||||
					<button
 | 
			
		||||
						type="submit"
 | 
			
		||||
						className={clsx(
 | 
			
		||||
							"max-w-[240px] mt-6 mx-auto w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:text-sm",
 | 
			
		||||
							!isSubmitting && "bg-primary-600 hover:bg-primary-700",
 | 
			
		||||
							isSubmitting && "bg-primary-400 cursor-not-allowed"
 | 
			
		||||
						)}
 | 
			
		||||
					>
 | 
			
		||||
						Save
 | 
			
		||||
					</button>
 | 
			
		||||
				</form>
 | 
			
		||||
			</div>
 | 
			
		||||
		</OnboardingLayout>
 | 
			
		||||
				<button
 | 
			
		||||
					type="submit"
 | 
			
		||||
					className={clsx(
 | 
			
		||||
						"max-w-[240px] mt-6 mx-auto w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:text-sm",
 | 
			
		||||
						!isSubmitting && "bg-primary-600 hover:bg-primary-700",
 | 
			
		||||
						isSubmitting && "bg-primary-400 cursor-not-allowed"
 | 
			
		||||
					)}
 | 
			
		||||
				>
 | 
			
		||||
					Save
 | 
			
		||||
				</button>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
StepThree.authenticate = true;
 | 
			
		||||
StepThree.getLayout = (page) => (
 | 
			
		||||
	<OnboardingLayout currentStep={3} previous={{ href: Routes.StepTwo().pathname, label: "Back" }}>
 | 
			
		||||
		{page}
 | 
			
		||||
	</OnboardingLayout>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
StepThree.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }) => {
 | 
			
		||||
	const session = await getSession(req, res);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,13 +25,10 @@ const StepTwo: BlitzPage = () => {
 | 
			
		||||
	const { customer } = useCurrentCustomer();
 | 
			
		||||
	const [setTwilioApiFieldsMutation] = useMutation(setTwilioApiFields);
 | 
			
		||||
 | 
			
		||||
	const initialAuthToken = customer?.authToken ?? "";
 | 
			
		||||
	const initialAccountSid = customer?.accountSid ?? "";
 | 
			
		||||
	const hasTwilioCredentials = initialAccountSid.length > 0 && initialAuthToken.length > 0;
 | 
			
		||||
	useEffect(() => {
 | 
			
		||||
		setValue("twilioAuthToken", initialAuthToken);
 | 
			
		||||
		setValue("twilioAccountSid", initialAccountSid);
 | 
			
		||||
	}, [initialAuthToken, initialAccountSid]);
 | 
			
		||||
		setValue("twilioAuthToken", customer?.authToken ?? "");
 | 
			
		||||
		setValue("twilioAccountSid", customer?.accountSid ?? "");
 | 
			
		||||
	}, [setValue, customer?.authToken, customer?.accountSid]);
 | 
			
		||||
 | 
			
		||||
	const onSubmit = handleSubmit(async ({ twilioAccountSid, twilioAuthToken }) => {
 | 
			
		||||
		if (isSubmitting) {
 | 
			
		||||
@@ -46,60 +43,75 @@ const StepTwo: BlitzPage = () => {
 | 
			
		||||
		await router.push(Routes.StepThree());
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<div className="flex flex-col space-y-4 items-center">
 | 
			
		||||
			<form onSubmit={onSubmit} className="flex flex-col gap-6">
 | 
			
		||||
				<div className="w-full">
 | 
			
		||||
					<label
 | 
			
		||||
						htmlFor="twilioAccountSid"
 | 
			
		||||
						className="block text-sm font-medium text-gray-700"
 | 
			
		||||
					>
 | 
			
		||||
						Twilio Account SID
 | 
			
		||||
					</label>
 | 
			
		||||
					<input
 | 
			
		||||
						type="text"
 | 
			
		||||
						id="twilioAccountSid"
 | 
			
		||||
						className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
 | 
			
		||||
						{...register("twilioAccountSid", { required: true })}
 | 
			
		||||
					/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div className="w-full">
 | 
			
		||||
					<label
 | 
			
		||||
						htmlFor="twilioAuthToken"
 | 
			
		||||
						className="block text-sm font-medium text-gray-700"
 | 
			
		||||
					>
 | 
			
		||||
						Twilio Auth Token
 | 
			
		||||
					</label>
 | 
			
		||||
					<input
 | 
			
		||||
						type="text"
 | 
			
		||||
						id="twilioAuthToken"
 | 
			
		||||
						className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
 | 
			
		||||
						{...register("twilioAuthToken", { required: true })}
 | 
			
		||||
					/>
 | 
			
		||||
				</div>
 | 
			
		||||
 | 
			
		||||
				<button
 | 
			
		||||
					type="submit"
 | 
			
		||||
					className={clsx(
 | 
			
		||||
						"max-w-[240px] mx-auto w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:text-sm",
 | 
			
		||||
						!isSubmitting && "bg-primary-600 hover:bg-primary-700",
 | 
			
		||||
						isSubmitting && "bg-primary-400 cursor-not-allowed"
 | 
			
		||||
					)}
 | 
			
		||||
				>
 | 
			
		||||
					Save
 | 
			
		||||
				</button>
 | 
			
		||||
			</form>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
StepTwo.getLayout = function StepTwoLayout(page) {
 | 
			
		||||
	const { customer } = useCurrentCustomer();
 | 
			
		||||
	const initialAuthToken = customer?.authToken ?? "";
 | 
			
		||||
	const initialAccountSid = customer?.accountSid ?? "";
 | 
			
		||||
	const hasTwilioCredentials = initialAccountSid.length > 0 && initialAuthToken.length > 0;
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<OnboardingLayout
 | 
			
		||||
			currentStep={2}
 | 
			
		||||
			next={hasTwilioCredentials ? { href: "/welcome/step-three", label: "Next" } : undefined}
 | 
			
		||||
			previous={{ href: "/welcome/step-one", label: "Back" }}
 | 
			
		||||
			next={
 | 
			
		||||
				hasTwilioCredentials
 | 
			
		||||
					? { href: Routes.StepThree().pathname, label: "Next" }
 | 
			
		||||
					: undefined
 | 
			
		||||
			}
 | 
			
		||||
			previous={{ href: Routes.StepOne().pathname, label: "Back" }}
 | 
			
		||||
		>
 | 
			
		||||
			<div className="flex flex-col space-y-4 items-center">
 | 
			
		||||
				<form onSubmit={onSubmit} className="flex flex-col gap-6">
 | 
			
		||||
					<div className="w-full">
 | 
			
		||||
						<label
 | 
			
		||||
							htmlFor="twilioAccountSid"
 | 
			
		||||
							className="block text-sm font-medium text-gray-700"
 | 
			
		||||
						>
 | 
			
		||||
							Twilio Account SID
 | 
			
		||||
						</label>
 | 
			
		||||
						<input
 | 
			
		||||
							type="text"
 | 
			
		||||
							id="twilioAccountSid"
 | 
			
		||||
							className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
 | 
			
		||||
							{...register("twilioAccountSid", { required: true })}
 | 
			
		||||
						/>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div className="w-full">
 | 
			
		||||
						<label
 | 
			
		||||
							htmlFor="twilioAuthToken"
 | 
			
		||||
							className="block text-sm font-medium text-gray-700"
 | 
			
		||||
						>
 | 
			
		||||
							Twilio Auth Token
 | 
			
		||||
						</label>
 | 
			
		||||
						<input
 | 
			
		||||
							type="text"
 | 
			
		||||
							id="twilioAuthToken"
 | 
			
		||||
							className="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
 | 
			
		||||
							{...register("twilioAuthToken", { required: true })}
 | 
			
		||||
						/>
 | 
			
		||||
					</div>
 | 
			
		||||
 | 
			
		||||
					<button
 | 
			
		||||
						type="submit"
 | 
			
		||||
						className={clsx(
 | 
			
		||||
							"max-w-[240px] mx-auto w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:text-sm",
 | 
			
		||||
							!isSubmitting && "bg-primary-600 hover:bg-primary-700",
 | 
			
		||||
							isSubmitting && "bg-primary-400 cursor-not-allowed"
 | 
			
		||||
						)}
 | 
			
		||||
					>
 | 
			
		||||
						Save
 | 
			
		||||
					</button>
 | 
			
		||||
				</form>
 | 
			
		||||
			</div>
 | 
			
		||||
			{page}
 | 
			
		||||
		</OnboardingLayout>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
StepTwo.authenticate = true;
 | 
			
		||||
StepTwo.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
 | 
			
		||||
	const session = await getSession(req, res);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { Suspense } from "react";
 | 
			
		||||
import { Link, BlitzPage, useMutation, Routes } from "blitz";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Link, useMutation, Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import BaseLayout from "../core/layouts/base-layout";
 | 
			
		||||
import logout from "../auth/mutations/logout";
 | 
			
		||||
@@ -35,12 +36,12 @@ const UserInfo = () => {
 | 
			
		||||
	} else {
 | 
			
		||||
		return (
 | 
			
		||||
			<>
 | 
			
		||||
				<Link href={Routes.SignupPage()}>
 | 
			
		||||
				<Link href={Routes.SignUp()}>
 | 
			
		||||
					<a className="button small">
 | 
			
		||||
						<strong>Sign Up</strong>
 | 
			
		||||
					</a>
 | 
			
		||||
				</Link>
 | 
			
		||||
				<Link href={Routes.LoginPage()}>
 | 
			
		||||
				<Link href={Routes.SignIn()}>
 | 
			
		||||
					<a className="button small">
 | 
			
		||||
						<strong>Login</strong>
 | 
			
		||||
					</a>
 | 
			
		||||
@@ -268,6 +269,7 @@ const Home: BlitzPage = () => {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Home.suppressFirstRenderFlicker = true;
 | 
			
		||||
 | 
			
		||||
Home.getLayout = (page) => <BaseLayout title="Home">{page}</BaseLayout>;
 | 
			
		||||
 | 
			
		||||
export default Home;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { Suspense } from "react";
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import Layout from "../../core/layouts/layout";
 | 
			
		||||
import PhoneCallsList from "../components/phone-calls-list";
 | 
			
		||||
@@ -9,17 +10,19 @@ const PhoneCalls: BlitzPage = () => {
 | 
			
		||||
	useRequireOnboarding();
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<Layout title="Calls">
 | 
			
		||||
		<>
 | 
			
		||||
			<div className="flex flex-col space-y-6 p-6">
 | 
			
		||||
				<p>Calls page</p>
 | 
			
		||||
			</div>
 | 
			
		||||
			<Suspense fallback="Loading...">
 | 
			
		||||
				<PhoneCallsList />
 | 
			
		||||
			</Suspense>
 | 
			
		||||
		</Layout>
 | 
			
		||||
		</>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PhoneCalls.authenticate = true;
 | 
			
		||||
PhoneCalls.getLayout = (page) => <Layout title="Calls">{page}</Layout>;
 | 
			
		||||
 | 
			
		||||
PhoneCalls.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default PhoneCalls;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes } from "blitz";
 | 
			
		||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 | 
			
		||||
import { faCreditCard, faUserCircle } from "@fortawesome/pro-regular-svg-icons";
 | 
			
		||||
 | 
			
		||||
@@ -32,27 +33,27 @@ const Settings: BlitzPage = () => {
 | 
			
		||||
	useRequireOnboarding();
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		<Layout title="Settings">
 | 
			
		||||
			<div className="flex flex-col space-y-6 p-6">
 | 
			
		||||
				<aside className="py-6 lg:col-span-3">
 | 
			
		||||
					<nav className="space-y-1">
 | 
			
		||||
						{navigation.map((item) => (
 | 
			
		||||
							<a
 | 
			
		||||
								key={item.name}
 | 
			
		||||
								href={item.href}
 | 
			
		||||
								className="border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900 group border-l-4 px-3 py-2 flex items-center text-sm font-medium"
 | 
			
		||||
							>
 | 
			
		||||
								<item.icon className="text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6" />
 | 
			
		||||
								<span className="truncate">{item.name}</span>
 | 
			
		||||
							</a>
 | 
			
		||||
						))}
 | 
			
		||||
					</nav>
 | 
			
		||||
				</aside>
 | 
			
		||||
			</div>
 | 
			
		||||
		</Layout>
 | 
			
		||||
		<div className="flex flex-col space-y-6 p-6">
 | 
			
		||||
			<aside className="py-6 lg:col-span-3">
 | 
			
		||||
				<nav className="space-y-1">
 | 
			
		||||
					{navigation.map((item) => (
 | 
			
		||||
						<a
 | 
			
		||||
							key={item.name}
 | 
			
		||||
							href={item.href}
 | 
			
		||||
							className="border-transparent text-gray-900 hover:bg-gray-50 hover:text-gray-900 group border-l-4 px-3 py-2 flex items-center text-sm font-medium"
 | 
			
		||||
						>
 | 
			
		||||
							<item.icon className="text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6" />
 | 
			
		||||
							<span className="truncate">{item.name}</span>
 | 
			
		||||
						</a>
 | 
			
		||||
					))}
 | 
			
		||||
				</nav>
 | 
			
		||||
			</aside>
 | 
			
		||||
		</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Settings.authenticate = true;
 | 
			
		||||
Settings.getLayout = (page) => <Layout title="Settings">{page}</Layout>;
 | 
			
		||||
 | 
			
		||||
Settings.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default Settings;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes } from "blitz";
 | 
			
		||||
 | 
			
		||||
import SettingsLayout from "../../components/settings-layout";
 | 
			
		||||
import ProfileInformations from "../../components/profile-informations";
 | 
			
		||||
@@ -31,6 +32,8 @@ const Account: BlitzPage = () => {
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Account.authenticate = true;
 | 
			
		||||
Account.getLayout = (page) => <SettingsLayout>{page}</SettingsLayout>;
 | 
			
		||||
 | 
			
		||||
Account.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default Account;
 | 
			
		||||
 
 | 
			
		||||
@@ -111,10 +111,12 @@ export const getServerSideProps = withPageOnboardingRequired<Props>(
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import type { BlitzPage } from "blitz";
 | 
			
		||||
import { Routes } from "blitz";
 | 
			
		||||
import { useRouter } from "blitz";
 | 
			
		||||
import { useEffect } from "react";
 | 
			
		||||
 | 
			
		||||
import useRequireOnboarding from "../../../core/hooks/use-require-onboarding";
 | 
			
		||||
import SettingsLayout from "../../components/settings-layout";
 | 
			
		||||
 | 
			
		||||
const Billing: BlitzPage = () => {
 | 
			
		||||
	useRequireOnboarding();
 | 
			
		||||
@@ -127,6 +129,8 @@ const Billing: BlitzPage = () => {
 | 
			
		||||
	return null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Billing.authenticate = true;
 | 
			
		||||
Billing.getLayout = (page) => <SettingsLayout>{page}</SettingsLayout>;
 | 
			
		||||
 | 
			
		||||
Billing.authenticate = { redirectTo: Routes.SignIn() };
 | 
			
		||||
 | 
			
		||||
export default Billing;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user