session thingy for onboarding checks
This commit is contained in:
parent
7acbca65ce
commit
7d34fcd48f
@ -4,14 +4,14 @@ import db from "db";
|
|||||||
import twilio from "twilio";
|
import twilio from "twilio";
|
||||||
|
|
||||||
export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
|
export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
|
||||||
await Promise.all([
|
/*await Promise.all([
|
||||||
db.message.deleteMany(),
|
db.message.deleteMany(),
|
||||||
db.phoneCall.deleteMany(),
|
db.phoneCall.deleteMany(),
|
||||||
db.phoneNumber.deleteMany(),
|
db.phoneNumber.deleteMany(),
|
||||||
db.customer.deleteMany(),
|
|
||||||
]);
|
]);
|
||||||
|
await db.customer.deleteMany();
|
||||||
|
await db.user.deleteMany();*/
|
||||||
|
|
||||||
await db.user.deleteMany();
|
|
||||||
const accountSid = "ACa886d066be0832990d1cf43fb1d53362";
|
const accountSid = "ACa886d066be0832990d1cf43fb1d53362";
|
||||||
const authToken = "8696a59a64b94bb4eba3548ed815953b";
|
const authToken = "8696a59a64b94bb4eba3548ed815953b";
|
||||||
/*const ddd = await twilio(accountSid, authToken)
|
/*const ddd = await twilio(accountSid, authToken)
|
||||||
@ -37,6 +37,17 @@ export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
|
|||||||
to: "+33757592025",
|
to: "+33757592025",
|
||||||
from: "+33757592722",
|
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();
|
res.status(200).end();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { useQuery } from "blitz";
|
import { useAuthenticatedSession, useQuery } from "blitz";
|
||||||
|
|
||||||
import getCurrentCustomer from "../../customers/queries/get-current-customer";
|
import getCurrentCustomer from "../../customers/queries/get-current-customer";
|
||||||
|
|
||||||
export default function useCurrentCustomer() {
|
export default function useCurrentCustomer() {
|
||||||
|
const session = useAuthenticatedSession();
|
||||||
const [customer] = useQuery(getCurrentCustomer, null);
|
const [customer] = useQuery(getCurrentCustomer, null);
|
||||||
return {
|
return {
|
||||||
customer,
|
customer,
|
||||||
hasCompletedOnboarding: Boolean(!!customer && customer.accountSid && customer.authToken),
|
hasFilledTwilioCredentials: Boolean(customer && customer.accountSid && customer.authToken),
|
||||||
|
hasCompletedOnboarding: session.hasCompletedOnboarding,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,9 @@ import getCurrentCustomerPhoneNumber from "../../phone-numbers/queries/get-curre
|
|||||||
import useCurrentCustomer from "./use-current-customer";
|
import useCurrentCustomer from "./use-current-customer";
|
||||||
|
|
||||||
export default function useCustomerPhoneNumber() {
|
export default function useCustomerPhoneNumber() {
|
||||||
const { hasCompletedOnboarding } = useCurrentCustomer();
|
const { customer } = useCurrentCustomer();
|
||||||
const [customerPhoneNumber] = useQuery(
|
const hasFilledTwilioCredentials = Boolean(customer && customer.accountSid && customer.authToken);
|
||||||
getCurrentCustomerPhoneNumber,
|
const [customerPhoneNumber] = useQuery(getCurrentCustomerPhoneNumber, {}, { enabled: hasFilledTwilioCredentials });
|
||||||
{},
|
|
||||||
{ enabled: hasCompletedOnboarding },
|
|
||||||
);
|
|
||||||
|
|
||||||
return customerPhoneNumber;
|
return customerPhoneNumber;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,14 @@ import useCustomerPhoneNumber from "./use-customer-phone-number";
|
|||||||
|
|
||||||
export default function useRequireOnboarding() {
|
export default function useRequireOnboarding() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { customer, hasCompletedOnboarding } = useCurrentCustomer();
|
const { hasFilledTwilioCredentials, hasCompletedOnboarding } = useCurrentCustomer();
|
||||||
const customerPhoneNumber = useCustomerPhoneNumber();
|
const customerPhoneNumber = useCustomerPhoneNumber();
|
||||||
|
|
||||||
if (!hasCompletedOnboarding) {
|
if (hasCompletedOnboarding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasFilledTwilioCredentials) {
|
||||||
throw router.push(Routes.StepTwo());
|
throw router.push(Routes.StepTwo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +21,6 @@ export default function useRequireOnboarding() {
|
|||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
console.log("customerPhoneNumber", customerPhoneNumber);
|
|
||||||
if (!customerPhoneNumber) {
|
if (!customerPhoneNumber) {
|
||||||
throw router.push(Routes.StepThree());
|
throw router.push(Routes.StepThree());
|
||||||
}
|
}
|
||||||
|
@ -60,10 +60,10 @@ const Keypad: BlitzPage = () => {
|
|||||||
</Row>
|
</Row>
|
||||||
<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">
|
<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>
|
||||||
<div className="cursor-pointer select-none my-auto" onClick={pressBackspace}>
|
<div className="cursor-pointer select-none m-auto" onClick={pressBackspace}>
|
||||||
<FontAwesomeIcon icon={faBackspace} size="lg" />
|
<FontAwesomeIcon className="w-6 h-6" icon={faBackspace} size="lg" />
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
</section>
|
</section>
|
||||||
@ -112,9 +112,7 @@ const Digit: FunctionComponent<{ digit: string }> = ({ children, digit }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const DigitLetters: FunctionComponent = ({ children }) => (
|
const DigitLetters: FunctionComponent = ({ children }) => <div className="text-xs text-gray-600">{children}</div>;
|
||||||
<div className="text-xs text-gray-600">{children}</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const phoneNumberAtom = atom("");
|
const phoneNumberAtom = atom("");
|
||||||
const pressDigitAtom = atom(null, (get, set, digit: string) => {
|
const pressDigitAtom = atom(null, (get, set, digit: string) => {
|
||||||
|
@ -12,14 +12,12 @@ const fetchMessagesQueue = Queue<Payload>("api/queue/fetch-messages", async ({ c
|
|||||||
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
||||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
|
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
|
||||||
|
|
||||||
const [messagesSent, messagesReceived] = await Promise.all([
|
const [sent, received] = await Promise.all([
|
||||||
twilio(customer!.accountSid!, customer!.authToken!).messages.list({
|
twilio(customer!.accountSid!, customer!.authToken!).messages.list({ from: phoneNumber!.phoneNumber }),
|
||||||
from: phoneNumber!.phoneNumber,
|
twilio(customer!.accountSid!, customer!.authToken!).messages.list({ to: 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(
|
const messages = [...messagesSent, ...messagesReceived].sort(
|
||||||
(a, b) => a.dateCreated.getTime() - b.dateCreated.getTime(),
|
(a, b) => a.dateCreated.getTime() - b.dateCreated.getTime(),
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Suspense, useState } from "react";
|
import { Suspense } from "react";
|
||||||
import type { BlitzPage } from "blitz";
|
import type { BlitzPage } from "blitz";
|
||||||
import { Routes } from "blitz";
|
import { Routes } from "blitz";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import type { FunctionComponent } from "react";
|
import type { FunctionComponent } from "react";
|
||||||
|
import { Link } from "blitz";
|
||||||
import { CheckIcon } from "@heroicons/react/solid";
|
import { CheckIcon } from "@heroicons/react/solid";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { Link, Routes, useRouter } from "blitz";
|
|
||||||
|
|
||||||
import useCustomerPhoneNumber from "../../core/hooks/use-customer-phone-number";
|
|
||||||
|
|
||||||
type StepLink = {
|
type StepLink = {
|
||||||
href: string;
|
href: string;
|
||||||
@ -19,13 +17,6 @@ type Props = {
|
|||||||
const steps = ["Welcome", "Twilio Credentials", "Pick a plan"] as const;
|
const steps = ["Welcome", "Twilio Credentials", "Pick a plan"] as const;
|
||||||
|
|
||||||
const OnboardingLayout: FunctionComponent<Props> = ({ children, currentStep, previous, next }) => {
|
const OnboardingLayout: FunctionComponent<Props> = ({ children, currentStep, previous, next }) => {
|
||||||
const router = useRouter();
|
|
||||||
const customerPhoneNumber = useCustomerPhoneNumber();
|
|
||||||
|
|
||||||
if (customerPhoneNumber) {
|
|
||||||
throw router.push(Routes.Messages());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-800 fixed z-10 inset-0 overflow-y-auto">
|
<div className="bg-gray-800 fixed z-10 inset-0 overflow-y-auto">
|
||||||
<div className="min-h-screen text-center block p-0">
|
<div className="min-h-screen text-center block p-0">
|
||||||
|
@ -12,29 +12,23 @@ const Body = z.object({
|
|||||||
phoneNumberSid: z.string(),
|
phoneNumberSid: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default resolver.pipe(
|
export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberSid }, context) => {
|
||||||
resolver.zod(Body),
|
const customer = await getCurrentCustomer(null, context);
|
||||||
resolver.authorize(),
|
const customerId = customer!.id;
|
||||||
async ({ phoneNumberSid }, context) => {
|
const phoneNumbers = await twilio(customer!.accountSid!, customer!.authToken!).incomingPhoneNumbers.list();
|
||||||
const customer = await getCurrentCustomer(null, context);
|
const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!;
|
||||||
const customerId = customer!.id;
|
await db.phoneNumber.create({
|
||||||
const phoneNumbers = await twilio(
|
data: {
|
||||||
customer!.accountSid!,
|
customerId,
|
||||||
customer!.authToken!,
|
phoneNumberSid,
|
||||||
).incomingPhoneNumbers.list();
|
phoneNumber: phoneNumber.phoneNumber,
|
||||||
const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!;
|
},
|
||||||
await db.phoneNumber.create({
|
});
|
||||||
data: {
|
context.session.$setPrivateData({ hasCompletedOnboarding: true });
|
||||||
customerId,
|
|
||||||
phoneNumberSid,
|
|
||||||
phoneNumber: phoneNumber.phoneNumber,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchMessagesQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
|
fetchMessagesQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
|
||||||
fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
|
fetchCallsQueue.enqueue({ customerId }, { id: `fetch-messages-${customerId}` }),
|
||||||
setTwilioWebhooks.enqueue({ customerId }, { id: `set-twilio-webhooks-${customerId}` }),
|
setTwilioWebhooks.enqueue({ customerId }, { id: `set-twilio-webhooks-${customerId}` }),
|
||||||
]);
|
]);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
@ -16,10 +16,7 @@ const StepOne: BlitzPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
StepOne.getLayout = (page) => (
|
StepOne.getLayout = (page) => (
|
||||||
<OnboardingLayout
|
<OnboardingLayout currentStep={1} next={{ href: Routes.StepTwo().pathname, label: "Set up your phone number" }}>
|
||||||
currentStep={1}
|
|
||||||
next={{ href: Routes.StepTwo().pathname, label: "Set up your phone number" }}
|
|
||||||
>
|
|
||||||
{page}
|
{page}
|
||||||
</OnboardingLayout>
|
</OnboardingLayout>
|
||||||
);
|
);
|
||||||
@ -39,16 +36,17 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
|
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
|
||||||
if (!phoneNumber) {
|
if (phoneNumber) {
|
||||||
return { props: {} };
|
await session.$setPublicData({ hasCompletedOnboarding: true });
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: Routes.Messages().pathname,
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { props: {} };
|
||||||
redirect: {
|
|
||||||
destination: Routes.Messages().pathname,
|
|
||||||
permanent: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StepOne;
|
export default StepOne;
|
||||||
|
@ -102,6 +102,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }
|
|||||||
|
|
||||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
|
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
|
||||||
if (phoneNumber) {
|
if (phoneNumber) {
|
||||||
|
await session.$setPublicData({ hasCompletedOnboarding: true });
|
||||||
return {
|
return {
|
||||||
redirect: {
|
redirect: {
|
||||||
destination: Routes.Messages().pathname,
|
destination: Routes.Messages().pathname,
|
||||||
@ -129,10 +130,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const incomingPhoneNumbers = await twilio(
|
const incomingPhoneNumbers = await twilio(customer.accountSid, customer.authToken).incomingPhoneNumbers.list();
|
||||||
customer.accountSid,
|
|
||||||
customer.authToken,
|
|
||||||
).incomingPhoneNumbers.list();
|
|
||||||
const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid }));
|
const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid }));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -48,10 +48,7 @@ const StepTwo: BlitzPage = () => {
|
|||||||
<div className="flex flex-col space-y-4 items-center">
|
<div className="flex flex-col space-y-4 items-center">
|
||||||
<form onSubmit={onSubmit} className="flex flex-col gap-6">
|
<form onSubmit={onSubmit} className="flex flex-col gap-6">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<label
|
<label htmlFor="twilioAccountSid" className="block text-sm font-medium text-gray-700">
|
||||||
htmlFor="twilioAccountSid"
|
|
||||||
className="block text-sm font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
Twilio Account SID
|
Twilio Account SID
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -62,10 +59,7 @@ const StepTwo: BlitzPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<label
|
<label htmlFor="twilioAuthToken" className="block text-sm font-medium text-gray-700">
|
||||||
htmlFor="twilioAuthToken"
|
|
||||||
className="block text-sm font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
Twilio Auth Token
|
Twilio Auth Token
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -108,11 +102,7 @@ const StepTwoLayout: FunctionComponent = ({ children }) => {
|
|||||||
return (
|
return (
|
||||||
<OnboardingLayout
|
<OnboardingLayout
|
||||||
currentStep={2}
|
currentStep={2}
|
||||||
next={
|
next={hasTwilioCredentials ? { href: Routes.StepThree().pathname, label: "Next" } : undefined}
|
||||||
hasTwilioCredentials
|
|
||||||
? { href: Routes.StepThree().pathname, label: "Next" }
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
previous={{ href: Routes.StepOne().pathname, label: "Back" }}
|
previous={{ href: Routes.StepOne().pathname, label: "Back" }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@ -135,16 +125,17 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
|
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId: session.userId } });
|
||||||
if (!phoneNumber) {
|
if (phoneNumber) {
|
||||||
return { props: {} };
|
await session.$setPublicData({ hasCompletedOnboarding: true });
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
destination: Routes.Messages().pathname,
|
||||||
|
permanent: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { props: {} };
|
||||||
redirect: {
|
|
||||||
destination: Routes.Messages().pathname,
|
|
||||||
permanent: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StepTwo;
|
export default StepTwo;
|
||||||
|
@ -22,7 +22,8 @@ test.skip("renders blitz documentation link", () => {
|
|||||||
paddleSubscriptionId: null,
|
paddleSubscriptionId: null,
|
||||||
user: {} as any,
|
user: {} as any,
|
||||||
},
|
},
|
||||||
hasCompletedOnboarding: false,
|
hasFilledTwilioCredentials: false,
|
||||||
|
hasCompletedOnboarding: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { getByText } = render(<Home />);
|
const { getByText } = render(<Home />);
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"semi": true,
|
"semi": true,
|
||||||
"useTabs": true,
|
"useTabs": true,
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"printWidth": 100,
|
"printWidth": 120,
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"jsxBracketSameLine": false,
|
"jsxBracketSameLine": false,
|
||||||
"quoteProps": "as-needed",
|
"quoteProps": "as-needed",
|
||||||
|
Loading…
Reference in New Issue
Block a user