From 866bc764756c089c086abeed0f063f981a88ca65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20LAGACHE?= Date: Tue, 23 Jun 2026 11:34:20 +0200 Subject: [PATCH] fix --- app/not-found.tsx | 7 +- src/components/common/back-button.tsx | 18 ----- src/db/services/organization.ts | 14 +++- .../onboarding/hooks/use-create-org.ts | 11 +-- src/features/onboarding/onboarding-state.ts | 34 +++++++--- src/features/onboarding/steps/step-login.tsx | 68 +++++++++++++++++-- src/features/onboarding/types/index.ts | 4 +- src/features/profile/profile-appearance.tsx | 3 +- 8 files changed, 110 insertions(+), 49 deletions(-) delete mode 100644 src/components/common/back-button.tsx diff --git a/app/not-found.tsx b/app/not-found.tsx index fe932423..f62b46cd 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,4 +1,5 @@ -import BackButton from "@/components/common/back-button"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; export default async function NotFound() { return ( @@ -12,7 +13,9 @@ export default async function NotFound() { The content you are trying to view is not available.

- Go home + ); diff --git a/src/components/common/back-button.tsx b/src/components/common/back-button.tsx deleted file mode 100644 index 65a554e3..00000000 --- a/src/components/common/back-button.tsx +++ /dev/null @@ -1,18 +0,0 @@ -"use client"; -import React from "react"; -import { useRouter } from "next/navigation"; -import { Button } from "@/components/ui/button"; - -type BackButtonProps = React.ComponentProps & { - children: React.ReactNode; -}; - -export default function BackButton({ children, ...props }: BackButtonProps) { - const router = useRouter(); - - return ( - - ); -} diff --git a/src/db/services/organization.ts b/src/db/services/organization.ts index 51ac0ddf..2ede3e96 100644 --- a/src/db/services/organization.ts +++ b/src/db/services/organization.ts @@ -1,7 +1,7 @@ "use server"; import { db } from "@/db"; -import { eq } from "drizzle-orm"; +import { and, eq, inArray, ne } from "drizzle-orm"; import { member } from "@/db/schema/04_member"; import { organization } from "@/db/schema/03_organization"; @@ -15,3 +15,15 @@ export async function getUserOrganization(userId: string) { where: eq(organization.id, memberRow.organizationId), }); } + +export async function getUserOwnOrganization(userId: string) { + const memberRows = await db.query.member.findMany({ + columns: { organizationId: true }, + where: eq(member.userId, userId), + }); + if (!memberRows.length) return null; + const orgIds = memberRows.map((r) => r.organizationId); + return db.query.organization.findFirst({ + where: and(inArray(organization.id, orgIds), ne(organization.slug, "default")), + }); +} diff --git a/src/features/onboarding/hooks/use-create-org.ts b/src/features/onboarding/hooks/use-create-org.ts index 2cbbe6a9..849ee65f 100644 --- a/src/features/onboarding/hooks/use-create-org.ts +++ b/src/features/onboarding/hooks/use-create-org.ts @@ -5,7 +5,6 @@ import { useOnboarding } from "@onboardjs/react"; import { toast } from "sonner"; import { createOrganizationAction, - getMyOrganizationAction, updateOrganizationAction, } from "@/features/organizations/organization.action"; import { slugify } from "@/utils/slugify"; @@ -18,18 +17,10 @@ export const useCreateOrg = () => { const trimmed = name.trim(); if (!trimmed) throw new Error("Organisation name is required"); - let existingOrg = state?.context.flowData.org as + const existingOrg = state?.context.flowData.org as | { id: string; name: string } | undefined; - if (!existingOrg) { - const fetchResult = await getMyOrganizationAction({}); - const fetchData = fetchResult?.data; - if (fetchData?.success && fetchData.value) { - existingOrg = { id: fetchData.value.id, name: fetchData.value.name }; - } - } - if (existingOrg) { const result = await updateOrganizationAction({ organizationId: existingOrg.id, diff --git a/src/features/onboarding/onboarding-state.ts b/src/features/onboarding/onboarding-state.ts index 245b9fea..d0b3e8ab 100644 --- a/src/features/onboarding/onboarding-state.ts +++ b/src/features/onboarding/onboarding-state.ts @@ -7,7 +7,10 @@ import { member } from "@/db/schema/04_member"; import { currentUser } from "@/lib/auth/current-user"; import { getSettings } from "@/db/services/setting"; import { hasUsers } from "@/db/services/user"; -import { getUserOrganization } from "@/db/services/organization"; +import { + getUserOrganization, + getUserOwnOrganization, +} from "@/db/services/organization"; import { getOrganizationProject } from "@/db/services/project"; import { getOrganizationAgents } from "@/db/services/agent"; import { getDatabasesSettings } from "@/db/services/database"; @@ -20,6 +23,8 @@ import type { OnboardingFlowData, OnboardingMeta, } from "@/features/onboarding/types"; +import { getOAuthProviders } from "@/lib/auth/oauth"; +import { getOidcProviders } from "@/lib/auth/oidc"; import { generateEdgeKey } from "@/utils/edge_key"; import { getServerUrl } from "@/utils/get-server-url"; @@ -38,26 +43,33 @@ export async function resolveOnboardingState(): Promise emailPasswordEnabled: env.AUTH_EMAIL_PASSWORD_ENABLED === "true", hasExistingUsers: false, ssoProviders: [], + oauthProviders: [], defaultUserMode: !!(env.AUTH_DEFAULT_USER && env.AUTH_DEFAULT_PASSWORD), resumeStepId: "login", }; meta.hasExistingUsers = await hasUsers(); - if (env.AUTH_OIDC_CLIENT) { + for (const p of getOidcProviders()) { meta.ssoProviders.push({ - id: env.AUTH_OIDC_ID ?? "oidc", - label: env.AUTH_OIDC_TITLE ?? "SSO", + id: p.id, + label: p.title, + icon: p.icon, + description: p.description || undefined, }); } + for (const p of getOAuthProviders()) { + meta.oauthProviders.push({ id: p.id, label: p.title, icon: p.icon }); + } + const user = await currentUser(); if (!user) { return { stepId: "login", flowData: { meta } }; } - let org = await getUserOrganization(user.id); - if (!org) { + const inDefaultOrg = await getUserOrganization(user.id); + if (!inDefaultOrg) { const defaultOrg = await db.query.organization.findFirst({ where: eq(organization.slug, "default"), }); @@ -67,13 +79,15 @@ export async function resolveOnboardingState(): Promise organizationId: defaultOrg.id, role: "owner", }); - org = defaultOrg; - } else { - meta.resumeStepId = "preferences"; - return { stepId: "preferences", flowData: { meta } }; } } + const org = await getUserOwnOrganization(user.id); + if (!org) { + meta.resumeStepId = "preferences"; + return { stepId: "preferences", flowData: { meta } }; + } + const orgData = { id: org.id, name: org.name }; const [notifierChannels, storageChannels, agents, project] = diff --git a/src/features/onboarding/steps/step-login.tsx b/src/features/onboarding/steps/step-login.tsx index 8a1cbe51..f7224ec7 100644 --- a/src/features/onboarding/steps/step-login.tsx +++ b/src/features/onboarding/steps/step-login.tsx @@ -7,6 +7,7 @@ import { useMutation } from "@tanstack/react-query"; import { z } from "zod"; import { toast } from "sonner"; import { Globe, Mail, KeyRound, ShieldAlert } from "lucide-react"; +import { Icon } from "@iconify/react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { PasswordInput } from "@/components/ui/password-input"; @@ -38,8 +39,9 @@ export const StepLogin = () => { const passkeyEnabled = meta?.passkeyEnabled ?? false; const emailPasswordEnabled = meta?.emailPasswordEnabled ?? false; const hasAnySsoProvider = (meta?.ssoProviders?.length ?? 0) > 0; + const hasAnyOAuthProvider = (meta?.oauthProviders?.length ?? 0) > 0; const hasAnyAuthMethod = - emailPasswordEnabled || passkeyEnabled || hasAnySsoProvider; + emailPasswordEnabled || passkeyEnabled || hasAnySsoProvider || hasAnyOAuthProvider; useEffect(() => { if (session?.user) { @@ -76,11 +78,21 @@ export const StepLogin = () => { }, }); - const handleSso = async (providerId: string) => { + const handleOAuth = async (providerId: string) => { const result = await signIn.social({ provider: providerId as any, callbackURL: "/welcome", }); + if (result?.error) + toast.error(result.error.message ?? "OAuth sign in failed"); + }; + + const handleSso = async (providerId: string) => { + const result = await signIn.sso({ + providerId, + providerType: "oidc", + callbackURL: "/welcome", + }); if (result?.error) toast.error(result.error.message ?? "SSO sign in failed"); }; @@ -129,7 +141,7 @@ export const StepLogin = () => { Set up your instance by creating the first account.

- {hasAnySsoProvider && ( + {(hasAnySsoProvider || hasAnyOAuthProvider) && (
{meta?.ssoProviders.map((provider) => ( + ))} + {meta?.oauthProviders.map((provider) => ( + @@ -174,7 +208,7 @@ export const StepLogin = () => { : "Your session expired. Sign in to continue where you left off."}

- {hasAnySsoProvider && !meta?.defaultUserMode && ( + {(hasAnySsoProvider || hasAnyOAuthProvider) && !meta?.defaultUserMode && (
{meta?.ssoProviders.map((provider) => ( + ))} + {meta?.oauthProviders.map((provider) => ( + diff --git a/src/features/onboarding/types/index.ts b/src/features/onboarding/types/index.ts index 772bd8f0..b00761af 100644 --- a/src/features/onboarding/types/index.ts +++ b/src/features/onboarding/types/index.ts @@ -1,9 +1,11 @@ -export type OnboardingSsoProvider = { id: string; label: string }; +export type OnboardingSsoProvider = { id: string; label: string; icon: string; description?: string }; +export type OnboardingOAuthProvider = { id: string; label: string; icon: string }; export type OnboardingMeta = { passkeyEnabled: boolean; hasExistingUsers: boolean; ssoProviders: OnboardingSsoProvider[]; + oauthProviders: OnboardingOAuthProvider[]; defaultUserMode: boolean; resumeStepId: string; emailPasswordEnabled: boolean; diff --git a/src/features/profile/profile-appearance.tsx b/src/features/profile/profile-appearance.tsx index 209beb0e..a97045b7 100644 --- a/src/features/profile/profile-appearance.tsx +++ b/src/features/profile/profile-appearance.tsx @@ -21,7 +21,7 @@ export function ProfileAppearance() { } function ThemeSelector() { - const {theme} = useTheme(); + const {theme, setTheme} = useTheme(); return ( @@ -39,6 +39,7 @@ function ThemeSelector() { isActive ? "border-primary bg-primary/5" : "border-muted/40" )} onClick={async () => { + setTheme(item.value); await authClient.updateUser({theme: item.value}); }} >