Skip to content

Commit 5ab3a81

Browse files
committed
[Dashboard] Feature: update ecosystem wallet auth options (#4797)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR introduces new authentication options for the ecosystem feature, updates related types, and enhances the UI components to support these options. ### Detailed summary - Added new authentication options: `google`, `facebook`, `x`, `discord`, `farcaster`, `telegram`, `phone`, `email`, `guest`, `apple`, `coinbase`, `line`. - Updated `Ecosystem` type to include `authOptions`. - Modified `useUpdateEcosystem` to accept optional `authOptions`. - Added `AuthOptionsSection` to the `EcosystemPermissionsPage`. - Created `AuthOptionsForm` for managing authentication options. - Implemented confirmation dialog for adding/removing auth options. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent 931569b commit 5ab3a81

File tree

5 files changed

+152
-3
lines changed

5 files changed

+152
-3
lines changed

apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/[slug]/(active)/EcosystemPermissionsPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"use client";
2+
import { AuthOptionsSection } from "./components/server/auth-options-section";
23
import { EcosystemPartnersSection } from "./components/server/ecosystem-partners-section";
34
import { IntegrationPermissionsSection } from "./components/server/integration-permissions-section";
45
import { useEcosystem } from "./hooks/use-ecosystem";
@@ -11,6 +12,7 @@ export function EcosystemPermissionsPage({
1112
return (
1213
<div className="flex flex-col gap-12">
1314
<IntegrationPermissionsSection ecosystem={ecosystem} />
15+
<AuthOptionsSection ecosystem={ecosystem} />
1416
{ecosystem?.permission === "PARTNER_WHITELIST" && (
1517
<EcosystemPartnersSection ecosystem={ecosystem} />
1618
)}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
"use client";
2+
import { ConfirmationDialog } from "@/components/ui/ConfirmationDialog";
3+
import { Checkbox, CheckboxWithLabel } from "@/components/ui/checkbox";
4+
import { Skeleton } from "@/components/ui/skeleton";
5+
import { cn } from "@/lib/utils";
6+
import { useState } from "react";
7+
import { toast } from "sonner";
8+
import invariant from "tiny-invariant";
9+
import { type Ecosystem, authOptions } from "../../../../types";
10+
import { useUpdateEcosystem } from "../../hooks/use-update-ecosystem";
11+
12+
export function AuthOptionsForm({ ecosystem }: { ecosystem: Ecosystem }) {
13+
const [messageToConfirm, setMessageToConfirm] = useState<
14+
| {
15+
title: string;
16+
description: string;
17+
authOptions: typeof ecosystem.authOptions;
18+
}
19+
| undefined
20+
>();
21+
const {
22+
mutateAsync: updateEcosystem,
23+
variables,
24+
isPending,
25+
} = useUpdateEcosystem({
26+
onError: (error) => {
27+
const message =
28+
error instanceof Error ? error.message : "Failed to create ecosystem";
29+
toast.error(message);
30+
},
31+
});
32+
33+
return (
34+
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-5 md:gap-2">
35+
{authOptions.map((option) => (
36+
<CheckboxWithLabel
37+
key={option}
38+
className={cn(
39+
isPending &&
40+
variables?.authOptions?.includes(option) &&
41+
"animate-pulse",
42+
"hover:cursor-pointer hover:text-foreground",
43+
)}
44+
>
45+
<Checkbox
46+
checked={ecosystem.authOptions.includes(option)}
47+
onClick={() => {
48+
if (ecosystem.authOptions.includes(option)) {
49+
setMessageToConfirm({
50+
title: `Are you sure you want to remove ${option.slice(0, 1).toUpperCase() + option.slice(1)} as an authentication option for this ecosystem?`,
51+
description:
52+
"Users will no longer be able to log into your ecosystem using this option. Any users that previously used this option will be unable to log in.",
53+
authOptions: ecosystem.authOptions.filter(
54+
(o) => o !== option,
55+
),
56+
});
57+
} else {
58+
setMessageToConfirm({
59+
title: `Are you sure you want to add ${option.slice(0, 1).toUpperCase() + option.slice(1)} as an authentication option for this ecosystem?`,
60+
description:
61+
"Users will be able to log into your ecosystem using this option. If you later remove this option users that used it will no longer be able to log in.",
62+
authOptions: [...ecosystem.authOptions, option],
63+
});
64+
}
65+
}}
66+
/>
67+
{option.slice(0, 1).toUpperCase() + option.slice(1)}
68+
</CheckboxWithLabel>
69+
))}
70+
<ConfirmationDialog
71+
open={!!messageToConfirm}
72+
onOpenChange={(open) => {
73+
if (!open) {
74+
setMessageToConfirm(undefined);
75+
}
76+
}}
77+
title={messageToConfirm?.title}
78+
description={messageToConfirm?.description}
79+
onSubmit={() => {
80+
invariant(messageToConfirm, "Must have message for modal to be open");
81+
updateEcosystem({
82+
ecosystem,
83+
authOptions: messageToConfirm.authOptions,
84+
});
85+
}}
86+
/>
87+
</div>
88+
);
89+
}
90+
91+
export function AuthOptionsFormSkeleton() {
92+
return (
93+
<div className="flex flex-col gap-2 py-2 md:flex-row md:gap-4">
94+
{authOptions.map((option) => (
95+
<Skeleton key={option} className="h-14 w-full md:w-32" />
96+
))}
97+
</div>
98+
);
99+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Skeleton } from "@/components/ui/skeleton";
2+
import type { Ecosystem } from "../../../../types";
3+
import {
4+
AuthOptionsForm,
5+
AuthOptionsFormSkeleton,
6+
} from "../client/auth-options-form.client";
7+
8+
export function AuthOptionsSection({ ecosystem }: { ecosystem?: Ecosystem }) {
9+
return (
10+
<section className="flex flex-col gap-4 md:gap-8">
11+
<div className="flex flex-col gap-1">
12+
<h4 className="font-semibold text-2xl text-foreground">
13+
Authentication Options
14+
</h4>
15+
{ecosystem ? (
16+
<p className="text-muted-foreground text-sm">
17+
Configure the authentication options your ecosystem supports.
18+
</p>
19+
) : (
20+
<Skeleton className="h-6 w-[300px]" />
21+
)}
22+
</div>
23+
{ecosystem ? (
24+
<AuthOptionsForm ecosystem={ecosystem} />
25+
) : (
26+
<AuthOptionsFormSkeleton />
27+
)}
28+
</section>
29+
);
30+
}

apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/[slug]/(active)/hooks/use-update-ecosystem.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import {
44
useMutation,
55
useQueryClient,
66
} from "@tanstack/react-query";
7-
import type { Ecosystem } from "../../../types";
7+
import type { AuthOption, Ecosystem } from "../../../types";
88

99
type UpdateEcosystemParams = {
1010
ecosystem: Ecosystem;
11-
permission: "PARTNER_WHITELIST" | "ANYONE";
11+
permission?: "PARTNER_WHITELIST" | "ANYONE";
12+
authOptions?: AuthOption[];
1213
};
1314

1415
export function useUpdateEcosystem(
@@ -38,6 +39,7 @@ export function useUpdateEcosystem(
3839
},
3940
body: JSON.stringify({
4041
permission: params.permission,
42+
authOptions: params.authOptions,
4143
}),
4244
},
4345
);

apps/dashboard/src/app/(dashboard)/dashboard/connect/ecosystem/types.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
1+
export const authOptions = [
2+
"google",
3+
"facebook",
4+
"x",
5+
"discord",
6+
"farcaster",
7+
"telegram",
8+
"phone",
9+
"email",
10+
"guest",
11+
"apple",
12+
"coinbase",
13+
"line",
14+
] as const;
15+
export type AuthOption = (typeof authOptions)[number];
16+
117
export type Ecosystem = {
218
name: string;
319
imageUrl?: string;
420
id: string;
521
slug: string;
622
permission: "PARTNER_WHITELIST" | "ANYONE";
7-
authOptions: unknown;
23+
authOptions: (typeof authOptions)[number][];
824
url: string;
925
status: "active" | "requested" | "paymentFailed";
1026
createdAt: string;

0 commit comments

Comments
 (0)