diff --git a/app/features/settings/components/phone/twilio-api-form.tsx b/app/features/settings/components/phone/twilio-api-form.tsx deleted file mode 100644 index 0e648e3..0000000 --- a/app/features/settings/components/phone/twilio-api-form.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useState } from "react"; -import { useTransition } from "@remix-run/react"; -import { IoHelpCircle } from "react-icons/io5"; - -import HelpModal from "./help-modal"; -import Button from "../button"; -import SettingsSection from "../settings-section"; - -export default function TwilioApiForm() { - const transition = useTransition(); - const [isHelpModalOpen, setIsHelpModalOpen] = useState(false); - const isSubmitting = transition.state === "submitting"; - - const onSubmit = async () => { - // await setTwilioApiFieldsMutation({ twilioAccountSid, twilioAuthToken }); // TODO - }; - - return ( - <> -
- - - - } - > - -
- Shellphone needs some informations about your Twilio account to securely use your phone numbers. -
- -
- - -
-
- - -
-
-
- - setIsHelpModalOpen(false)} isHelpModalOpen={isHelpModalOpen} /> - - ); -} diff --git a/app/features/settings/components/phone/twilio-connect.tsx b/app/features/settings/components/phone/twilio-connect.tsx new file mode 100644 index 0000000..870d128 --- /dev/null +++ b/app/features/settings/components/phone/twilio-connect.tsx @@ -0,0 +1,40 @@ +import { useState } from "react"; +import { IoHelpCircle } from "react-icons/io5"; + +import HelpModal from "./help-modal"; +import SettingsSection from "../settings-section"; +import useSession from "~/features/core/hooks/use-session"; + +export default function TwilioConnect() { + const { currentOrganization } = useSession(); + const [isHelpModalOpen, setIsHelpModalOpen] = useState(false); + + return ( + <> +
+ + +
+ Shellphone needs to connect to your Twilio account to securely use your phone numbers. +
+ + {currentOrganization.twilioAccountSid === null ? ( + + Connect Twilio account + + ) : ( +

✓ Your Twilio account is connected to Shellphone.

+ )} +
+
+ + setIsHelpModalOpen(false)} isHelpModalOpen={isHelpModalOpen} /> + + ); +} diff --git a/app/routes/__app.tsx b/app/routes/__app.tsx index 76fac84..b93cbf1 100644 --- a/app/routes/__app.tsx +++ b/app/routes/__app.tsx @@ -1,11 +1,30 @@ -import type { LoaderFunction } from "@remix-run/node"; +import { type LoaderFunction, json } from "@remix-run/node"; import { Outlet, useCatch, useMatches } from "@remix-run/react"; -import { requireLoggedIn } from "~/utils/auth.server"; +import { type SessionData, type SessionOrganization, requireLoggedIn } from "~/utils/auth.server"; import Footer from "~/features/core/components/footer"; +import db from "~/utils/db.server"; + +export type AppLoaderData = SessionData export const loader:LoaderFunction = async ({ request }) => { - return requireLoggedIn(request); + const user = await requireLoggedIn(request); + const organization = await db.organization.findUnique({ + where: { id: user.organizations[0].id }, + include: { + memberships: { + where: { userId: user.id }, + select: { role: true }, + }, + }, + }); + const currentOrganization: SessionOrganization = { + id: organization!.id, + twilioAccountSid: organization!.twilioAccountSid, + role: organization!.memberships[0].role, + }; + + return json({ ...user, currentOrganization }); } export default function __App() { diff --git a/app/routes/__app/settings/phone.tsx b/app/routes/__app/settings/phone.tsx index c06fae3..0f81007 100644 --- a/app/routes/__app/settings/phone.tsx +++ b/app/routes/__app/settings/phone.tsx @@ -1,10 +1,10 @@ -import TwilioApiForm from "~/features/settings/components/phone/twilio-api-form"; +import TwilioConnect from "~/features/settings/components/phone/twilio-connect"; import PhoneNumberForm from "~/features/settings/components/phone/phone-number-form"; function PhoneSettings() { return (
- +
); diff --git a/app/routes/twilio.authorize.ts b/app/routes/twilio.authorize.ts new file mode 100644 index 0000000..95e64f3 --- /dev/null +++ b/app/routes/twilio.authorize.ts @@ -0,0 +1,25 @@ +import { type LoaderFunction, redirect } from "@remix-run/node"; +import { refreshSessionData, requireLoggedIn } from "~/utils/auth.server"; +import db from "~/utils/db.server"; +import { commitSession } from "~/utils/session.server"; + +export const loader: LoaderFunction = async ({ request }) => { + const user = await requireLoggedIn(request); + const url = new URL(request.url); + const twilioAccountSid = url.searchParams.get("AccountSid"); + if (!twilioAccountSid) { + throw new Error("unreachable"); + } + + await db.organization.update({ + where: { id: user.organizations[0].id }, + data: { twilioAccountSid }, + }); + + const { session } = await refreshSessionData(request); + return redirect("/settings/phone", { + headers: { + "Set-Cookie": await commitSession(session), + }, + }); +}; diff --git a/app/utils/auth.server.ts b/app/utils/auth.server.ts index dd204e4..9f11ed7 100644 --- a/app/utils/auth.server.ts +++ b/app/utils/auth.server.ts @@ -9,10 +9,11 @@ import authenticator from "./authenticator.server"; import { AuthenticationError } from "./errors"; import { commitSession, destroySession, getSession } from "./session.server"; -export type SessionOrganization = Pick & { role: MembershipRole }; +export type SessionOrganization = Pick & { role: MembershipRole }; export type SessionUser = Omit & { organizations: SessionOrganization[]; }; +export type SessionData = SessionUser & { currentOrganization: SessionOrganization }; const SP = new SecurePassword(); @@ -38,7 +39,7 @@ export async function login({ form }: FormStrategyVerifyParams): Promise