session thingy for onboarding checks

This commit is contained in:
m5r 2021-08-01 22:01:51 +08:00
parent 7acbca65ce
commit 7d34fcd48f
15 changed files with 85 additions and 102 deletions

View File

@ -4,14 +4,14 @@ import db from "db";
import twilio from "twilio";
export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
await Promise.all([
/*await Promise.all([
db.message.deleteMany(),
db.phoneCall.deleteMany(),
db.phoneNumber.deleteMany(),
db.customer.deleteMany(),
]);
await db.customer.deleteMany();
await db.user.deleteMany();*/
await db.user.deleteMany();
const accountSid = "ACa886d066be0832990d1cf43fb1d53362";
const authToken = "8696a59a64b94bb4eba3548ed815953b";
/*const ddd = await twilio(accountSid, authToken)
@ -37,6 +37,17 @@ export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
to: "+33757592025",
from: "+33757592722",
});*/
/*const [messagesSent, messagesReceived] = await Promise.all([
twilio(accountSid, authToken).messages.list({
from: "+33757592025",
}),
twilio(accountSid, authToken).messages.list({
to: "+33757592025",
}),
]);
console.log("messagesReceived", messagesReceived.sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()));
// console.log("messagesReceived", messagesReceived);*/
res.status(200).end();
}

View File

@ -1,11 +1,13 @@
import { useQuery } from "blitz";
import { useAuthenticatedSession, useQuery } from "blitz";
import getCurrentCustomer from "../../customers/queries/get-current-customer";
export default function useCurrentCustomer() {
const session = useAuthenticatedSession();
const [customer] = useQuery(getCurrentCustomer, null);
return {
customer,
hasCompletedOnboarding: Boolean(!!customer && customer.accountSid && customer.authToken),
hasFilledTwilioCredentials: Boolean(customer && customer.accountSid && customer.authToken),
hasCompletedOnboarding: session.hasCompletedOnboarding,
};
}

View File

@ -4,12 +4,9 @@ import getCurrentCustomerPhoneNumber from "../../phone-numbers/queries/get-curre
import useCurrentCustomer from "./use-current-customer";
export default function useCustomerPhoneNumber() {
const { hasCompletedOnboarding } = useCurrentCustomer();
const [customerPhoneNumber] = useQuery(
getCurrentCustomerPhoneNumber,
{},
{ enabled: hasCompletedOnboarding },
);
const { customer } = useCurrentCustomer();
const hasFilledTwilioCredentials = Boolean(customer && customer.accountSid && customer.authToken);
const [customerPhoneNumber] = useQuery(getCurrentCustomerPhoneNumber, {}, { enabled: hasFilledTwilioCredentials });
return customerPhoneNumber;
}

View File

@ -5,10 +5,14 @@ import useCustomerPhoneNumber from "./use-customer-phone-number";
export default function useRequireOnboarding() {
const router = useRouter();
const { customer, hasCompletedOnboarding } = useCurrentCustomer();
const { hasFilledTwilioCredentials, hasCompletedOnboarding } = useCurrentCustomer();
const customerPhoneNumber = useCustomerPhoneNumber();
if (!hasCompletedOnboarding) {
if (hasCompletedOnboarding) {
return;
}
if (!hasFilledTwilioCredentials) {
throw router.push(Routes.StepTwo());
}
@ -17,7 +21,6 @@ export default function useRequireOnboarding() {
return;
}*/
console.log("customerPhoneNumber", customerPhoneNumber);
if (!customerPhoneNumber) {
throw router.push(Routes.StepThree());
}

View File

@ -60,10 +60,10 @@ const Keypad: BlitzPage = () => {
</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" />
<FontAwesomeIcon className="w-6 h-6" icon={faPhone} color="white" size="lg" />
</div>
<div className="cursor-pointer select-none my-auto" onClick={pressBackspace}>
<FontAwesomeIcon icon={faBackspace} size="lg" />
<div className="cursor-pointer select-none m-auto" onClick={pressBackspace}>
<FontAwesomeIcon className="w-6 h-6" icon={faBackspace} size="lg" />
</div>
</Row>
</section>
@ -112,9 +112,7 @@ const Digit: FunctionComponent<{ digit: string }> = ({ children, digit }) => {
);
};
const DigitLetters: FunctionComponent = ({ children }) => (
<div className="text-xs text-gray-600">{children}</div>
);
const DigitLetters: FunctionComponent = ({ children }) => <div className="text-xs text-gray-600">{children}</div>;
const phoneNumberAtom = atom("");
const pressDigitAtom = atom(null, (get, set, digit: string) => {

View File

@ -12,14 +12,12 @@ const fetchMessagesQueue = Queue<Payload>("api/queue/fetch-messages", async ({ c
const customer = await db.customer.findFirst({ where: { id: customerId } });
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
const [messagesSent, messagesReceived] = await Promise.all([
twilio(customer!.accountSid!, customer!.authToken!).messages.list({
from: phoneNumber!.phoneNumber,
}),
twilio(customer!.accountSid!, customer!.authToken!).messages.list({
to: phoneNumber!.phoneNumber,
}),
const [sent, received] = await Promise.all([
twilio(customer!.accountSid!, customer!.authToken!).messages.list({ from: phoneNumber!.phoneNumber }),
twilio(customer!.accountSid!, customer!.authToken!).messages.list({ to: phoneNumber!.phoneNumber }),
]);
const messagesSent = sent.filter((message) => message.direction.startsWith("outbound"));
const messagesReceived = received.filter((message) => message.direction === "inbound");
const messages = [...messagesSent, ...messagesReceived].sort(
(a, b) => a.dateCreated.getTime() - b.dateCreated.getTime(),
);

View File

@ -1,4 +1,4 @@
import { Suspense, useState } from "react";
import { Suspense } from "react";
import type { BlitzPage } from "blitz";
import { Routes } from "blitz";
import { useAtom } from "jotai";

View File

@ -1,9 +1,7 @@
import type { FunctionComponent } from "react";
import { Link } from "blitz";
import { CheckIcon } from "@heroicons/react/solid";
import clsx from "clsx";
import { Link, Routes, useRouter } from "blitz";
import useCustomerPhoneNumber from "../../core/hooks/use-customer-phone-number";
type StepLink = {
href: string;
@ -19,13 +17,6 @@ type Props = {
const steps = ["Welcome", "Twilio Credentials", "Pick a plan"] as const;
const OnboardingLayout: FunctionComponent<Props> = ({ children, currentStep, previous, next }) => {
const router = useRouter();
const customerPhoneNumber = useCustomerPhoneNumber();
if (customerPhoneNumber) {
throw router.push(Routes.Messages());
}
return (
<div className="bg-gray-800 fixed z-10 inset-0 overflow-y-auto">
<div className="min-h-screen text-center block p-0">

View File

@ -12,29 +12,23 @@ const Body = z.object({
phoneNumberSid: z.string(),
});
export default resolver.pipe(
resolver.zod(Body),
resolver.authorize(),
async ({ phoneNumberSid }, context) => {
const customer = await getCurrentCustomer(null, context);
const customerId = customer!.id;
const phoneNumbers = await twilio(
customer!.accountSid!,
customer!.authToken!,
).incomingPhoneNumbers.list();
const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!;
await db.phoneNumber.create({
data: {
customerId,
phoneNumberSid,
phoneNumber: phoneNumber.phoneNumber,
},
});
export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberSid }, context) => {
const customer = await getCurrentCustomer(null, context);
const customerId = customer!.id;
const phoneNumbers = await twilio(customer!.accountSid!, customer!.authToken!).incomingPhoneNumbers.list();
const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!;
await db.phoneNumber.create({
data: {
customerId,
phoneNumberSid,
phoneNumber: phoneNumber.phoneNumber,
},
});
context.session.$setPrivateData({ hasCompletedOnboarding: true });
await Promise.all([
fetchMessagesQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
setTwilioWebhooks.enqueue({ customerId }, { id: `set-twilio-webhooks-${customerId}` }),
]);
},
);
await Promise.all([
fetchMessagesQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
setTwilioWebhooks.enqueue({ customerId }, { id: `set-twilio-webhooks-${customerId}` }),
]);
});

View File

@ -16,10 +16,7 @@ const StepOne: BlitzPage = () => {
};
StepOne.getLayout = (page) => (
<OnboardingLayout
currentStep={1}
next={{ href: Routes.StepTwo().pathname, label: "Set up your phone number" }}
>
<OnboardingLayout currentStep={1} next={{ href: Routes.StepTwo().pathname, label: "Set up your phone number" }}>
{page}
</OnboardingLayout>
);
@ -39,16 +36,17 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
}
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
if (!phoneNumber) {
return { props: {} };
if (phoneNumber) {
await session.$setPublicData({ hasCompletedOnboarding: true });
return {
redirect: {
destination: Routes.Messages().pathname,
permanent: false,
},
};
}
return {
redirect: {
destination: Routes.Messages().pathname,
permanent: false,
},
};
return { props: {} };
};
export default StepOne;

View File

@ -102,6 +102,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
if (phoneNumber) {
await session.$setPublicData({ hasCompletedOnboarding: true });
return {
redirect: {
destination: Routes.Messages().pathname,
@ -129,10 +130,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }
};
}
const incomingPhoneNumbers = await twilio(
customer.accountSid,
customer.authToken,
).incomingPhoneNumbers.list();
const incomingPhoneNumbers = await twilio(customer.accountSid, customer.authToken).incomingPhoneNumbers.list();
const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid }));
return {

View File

@ -48,10 +48,7 @@ const StepTwo: BlitzPage = () => {
<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"
>
<label htmlFor="twilioAccountSid" className="block text-sm font-medium text-gray-700">
Twilio Account SID
</label>
<input
@ -62,10 +59,7 @@ const StepTwo: BlitzPage = () => {
/>
</div>
<div className="w-full">
<label
htmlFor="twilioAuthToken"
className="block text-sm font-medium text-gray-700"
>
<label htmlFor="twilioAuthToken" className="block text-sm font-medium text-gray-700">
Twilio Auth Token
</label>
<input
@ -108,11 +102,7 @@ const StepTwoLayout: FunctionComponent = ({ children }) => {
return (
<OnboardingLayout
currentStep={2}
next={
hasTwilioCredentials
? { href: Routes.StepThree().pathname, label: "Next" }
: undefined
}
next={hasTwilioCredentials ? { href: Routes.StepThree().pathname, label: "Next" } : undefined}
previous={{ href: Routes.StepOne().pathname, label: "Back" }}
>
{children}
@ -135,16 +125,17 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
}
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
if (!phoneNumber) {
return { props: {} };
if (phoneNumber) {
await session.$setPublicData({ hasCompletedOnboarding: true });
return {
redirect: {
destination: Routes.Messages().pathname,
permanent: false,
},
};
}
return {
redirect: {
destination: Routes.Messages().pathname,
permanent: false,
},
};
return { props: {} };
};
export default StepTwo;

View File

@ -22,7 +22,8 @@ test.skip("renders blitz documentation link", () => {
paddleSubscriptionId: null,
user: {} as any,
},
hasCompletedOnboarding: false,
hasFilledTwilioCredentials: false,
hasCompletedOnboarding: undefined,
});
const { getByText } = render(<Home />);

View File

@ -21,7 +21,7 @@
"semi": true,
"useTabs": true,
"tabWidth": 4,
"printWidth": 100,
"printWidth": 120,
"trailingComma": "all",
"jsxBracketSameLine": false,
"quoteProps": "as-needed",

View File

@ -12,6 +12,7 @@ declare module "blitz" {
PublicData: {
userId: User["id"];
role: Role;
hasCompletedOnboarding?: true;
};
}
}