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 (
- <>
-
-
- 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