@daveyplate/better-auth-ui
Version:
Plug & play shadcn/ui components for better-auth
1,592 lines (1,561 loc) • 504 kB
JavaScript
"use client"
"use client";
import {
AuthUIContext,
AuthUIProvider,
useAuthData,
useCurrentOrganization,
useIsHydrated,
useLang,
useTheme
} from "./chunk-NLJJMS6W.js";
import {
accountViewPaths,
authLocalization,
authViewPaths,
cn,
getLocalizedError,
getPasswordSchema,
getSearchParam,
getViewByPath,
isValidEmail,
organizationViewPaths
} from "./chunk-QGW7NU44.js";
// src/components/account/account-view.tsx
import { MenuIcon } from "lucide-react";
import { useContext as useContext42, useMemo as useMemo11 } from "react";
// src/hooks/use-authenticate.ts
import { useContext, useEffect } from "react";
function useAuthenticate(options) {
const { authView = "SIGN_IN", enabled = true } = options ?? {};
const {
hooks: { useSession },
basePath,
viewPaths,
replace
} = useContext(AuthUIContext);
const { data, isPending, error, refetch } = useSession();
const sessionData = data;
useEffect(() => {
if (!enabled || isPending || sessionData) return;
const searchParams = new URLSearchParams(window.location.search);
const redirectTo = searchParams.get("redirectTo") || window.location.pathname + window.location.search;
replace(
`${basePath}/${viewPaths[authView]}?redirectTo=${encodeURIComponent(redirectTo)}`
);
}, [
isPending,
sessionData,
basePath,
viewPaths,
replace,
authView,
enabled
]);
return {
data: sessionData,
user: sessionData == null ? void 0 : sessionData.user,
isPending,
error,
refetch
};
}
// src/components/organization/organizations-card.tsx
import { useContext as useContext8, useMemo as useMemo5, useState as useState4 } from "react";
// src/components/ui/card.tsx
import { jsx } from "react/jsx-runtime";
function Card({ className, ...props }) {
return /* @__PURE__ */ jsx(
"div",
{
"data-slot": "card",
className: cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
),
...props
}
);
}
function CardHeader({ className, ...props }) {
return /* @__PURE__ */ jsx(
"div",
{
"data-slot": "card-header",
className: cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
),
...props
}
);
}
function CardTitle({ className, ...props }) {
return /* @__PURE__ */ jsx(
"div",
{
"data-slot": "card-title",
className: cn("leading-none font-semibold", className),
...props
}
);
}
function CardDescription({ className, ...props }) {
return /* @__PURE__ */ jsx(
"div",
{
"data-slot": "card-description",
className: cn("text-muted-foreground text-sm", className),
...props
}
);
}
function CardContent({ className, ...props }) {
return /* @__PURE__ */ jsx(
"div",
{
"data-slot": "card-content",
className: cn("px-6", className),
...props
}
);
}
function CardFooter({ className, ...props }) {
return /* @__PURE__ */ jsx(
"div",
{
"data-slot": "card-footer",
className: cn("flex items-center px-6 [.border-t]:pt-6", className),
...props
}
);
}
// src/components/ui/skeleton.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
function Skeleton({ className, ...props }) {
return /* @__PURE__ */ jsx2(
"div",
{
"data-slot": "skeleton",
className: cn("bg-accent animate-pulse rounded-md", className),
...props
}
);
}
// src/components/settings/shared/settings-action-button.tsx
import { Loader2 } from "lucide-react";
import { useFormState } from "react-hook-form";
// src/components/ui/button.tsx
import { Slot } from "@radix-ui/react-slot";
import { cva } from "class-variance-authority";
import { jsx as jsx3 } from "react/jsx-runtime";
var buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline"
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
);
function Button({
className,
variant,
size,
asChild = false,
...props
}) {
const Comp = asChild ? Slot : "button";
return /* @__PURE__ */ jsx3(
Comp,
{
"data-slot": "button",
className: cn(buttonVariants({ variant, size, className })),
...props
}
);
}
// src/components/settings/shared/settings-action-button.tsx
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
function SettingsActionButton({
classNames,
actionLabel,
disabled,
isSubmitting,
variant,
onClick,
...props
}) {
if (!onClick) {
const formState = useFormState();
isSubmitting = formState.isSubmitting;
}
return /* @__PURE__ */ jsxs(
Button,
{
className: cn(
"md:ms-auto",
classNames == null ? void 0 : classNames.button,
variant === "default" && (classNames == null ? void 0 : classNames.primaryButton),
variant === "destructive" && (classNames == null ? void 0 : classNames.destructiveButton)
),
disabled: isSubmitting || disabled,
size: "sm",
type: onClick ? "button" : "submit",
variant,
onClick,
...props,
children: [
isSubmitting && /* @__PURE__ */ jsx4(Loader2, { className: "animate-spin" }),
actionLabel
]
}
);
}
// src/components/settings/shared/settings-card-footer.tsx
import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
function SettingsCardFooter({
className,
classNames,
actionLabel,
disabled,
instructions,
isPending,
isSubmitting,
variant,
action
}) {
return /* @__PURE__ */ jsx5(
CardFooter,
{
className: cn(
"flex flex-col justify-between gap-4 rounded-b-xl md:flex-row",
(actionLabel || instructions) && "!py-4 border-t",
variant === "destructive" ? "border-destructive/30 bg-destructive/15" : "bg-sidebar",
className,
classNames == null ? void 0 : classNames.footer
),
children: isPending ? /* @__PURE__ */ jsxs2(Fragment, { children: [
instructions && /* @__PURE__ */ jsx5(
Skeleton,
{
className: cn(
"my-0.5 h-3 w-48 max-w-full md:h-4 md:w-56",
classNames == null ? void 0 : classNames.skeleton
)
}
),
actionLabel && /* @__PURE__ */ jsx5(
Skeleton,
{
className: cn(
"h-8 w-14 md:ms-auto",
classNames == null ? void 0 : classNames.skeleton
)
}
)
] }) : /* @__PURE__ */ jsxs2(Fragment, { children: [
instructions && /* @__PURE__ */ jsx5(
CardDescription,
{
className: cn(
"text-center text-muted-foreground text-xs md:text-start md:text-sm",
classNames == null ? void 0 : classNames.instructions
),
children: instructions
}
),
actionLabel && /* @__PURE__ */ jsx5(
SettingsActionButton,
{
classNames,
actionLabel,
disabled,
isSubmitting,
variant,
onClick: action
}
)
] })
}
);
}
// src/components/settings/shared/settings-card-header.tsx
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
function SettingsCardHeader({
className,
classNames,
description,
isPending,
title
}) {
return /* @__PURE__ */ jsx6(CardHeader, { className: cn(classNames == null ? void 0 : classNames.header, className), children: isPending ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
/* @__PURE__ */ jsx6(
Skeleton,
{
className: cn(
"my-0.5 h-5 w-1/3 md:h-5.5",
classNames == null ? void 0 : classNames.skeleton
)
}
),
description && /* @__PURE__ */ jsx6(
Skeleton,
{
className: cn(
"mt-1.5 mb-0.5 h-3 w-2/3 md:h-3.5",
classNames == null ? void 0 : classNames.skeleton
)
}
)
] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
/* @__PURE__ */ jsx6(
CardTitle,
{
className: cn("text-lg md:text-xl", classNames == null ? void 0 : classNames.title),
children: title
}
),
description && /* @__PURE__ */ jsx6(
CardDescription,
{
className: cn(
"text-xs md:text-sm",
classNames == null ? void 0 : classNames.description
),
children: description
}
)
] }) });
}
// src/components/settings/shared/settings-card.tsx
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
function SettingsCard({
children,
className,
classNames,
title,
description,
instructions,
actionLabel,
disabled,
isPending,
isSubmitting,
optimistic,
variant,
action,
...props
}) {
return /* @__PURE__ */ jsxs4(
Card,
{
className: cn(
"w-full pb-0 text-start",
variant === "destructive" && "border-destructive/40",
className,
classNames == null ? void 0 : classNames.base
),
...props,
children: [
/* @__PURE__ */ jsx7(
SettingsCardHeader,
{
classNames,
description,
isPending,
title
}
),
children,
/* @__PURE__ */ jsx7(
SettingsCardFooter,
{
classNames,
actionLabel,
disabled,
isPending,
isSubmitting,
instructions,
optimistic,
variant,
action
}
)
]
}
);
}
// src/components/settings/skeletons/settings-cell-skeleton.tsx
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
function SettingsCellSkeleton({
classNames
}) {
return /* @__PURE__ */ jsxs5(
Card,
{
className: cn(
"flex-row items-center gap-3 px-4 py-3",
classNames == null ? void 0 : classNames.cell
),
children: [
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ jsx8(
Skeleton,
{
className: cn("size-5 rounded-full", classNames == null ? void 0 : classNames.skeleton)
}
),
/* @__PURE__ */ jsx8("div", { children: /* @__PURE__ */ jsx8(
Skeleton,
{
className: cn("h-4 w-24", classNames == null ? void 0 : classNames.skeleton)
}
) })
] }),
/* @__PURE__ */ jsx8(
Skeleton,
{
className: cn("ms-auto size-8 w-12", classNames == null ? void 0 : classNames.skeleton)
}
)
]
}
);
}
// src/components/organization/create-organization-dialog.tsx
import { zodResolver } from "@hookform/resolvers/zod";
import { Loader2 as Loader22, Trash2Icon, UploadCloudIcon } from "lucide-react";
import {
useContext as useContext4,
useMemo as useMemo2,
useRef,
useState
} from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
// src/lib/image-utils.ts
async function resizeAndCropImage(file, name, size, extension) {
const image = await loadImage(file);
const canvas = document.createElement("canvas");
canvas.width = canvas.height = size;
const ctx = canvas.getContext("2d");
const minEdge = Math.min(image.width, image.height);
const sx = (image.width - minEdge) / 2;
const sy = (image.height - minEdge) / 2;
const sWidth = minEdge;
const sHeight = minEdge;
ctx == null ? void 0 : ctx.drawImage(image, sx, sy, sWidth, sHeight, 0, 0, size, size);
const resizedImageBlob = await new Promise(
(resolve) => canvas.toBlob(resolve, `image/${extension}`)
);
return new File([resizedImageBlob], `${name}.${extension}`, {
type: `image/${extension}`
});
}
async function loadImage(file) {
return new Promise((resolve, reject) => {
const image = new Image();
const reader = new FileReader();
reader.onload = (e) => {
var _a;
image.src = (_a = e.target) == null ? void 0 : _a.result;
};
image.onload = () => resolve(image);
image.onerror = (err) => reject(err);
reader.readAsDataURL(file);
});
}
async function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// src/components/ui/dialog.tsx
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { XIcon } from "lucide-react";
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
function Dialog({
...props
}) {
return /* @__PURE__ */ jsx9(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
}
function DialogPortal({
...props
}) {
return /* @__PURE__ */ jsx9(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
}
function DialogOverlay({
className,
...props
}) {
return /* @__PURE__ */ jsx9(
DialogPrimitive.Overlay,
{
"data-slot": "dialog-overlay",
className: cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
),
...props
}
);
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}) {
return /* @__PURE__ */ jsxs6(DialogPortal, { "data-slot": "dialog-portal", children: [
/* @__PURE__ */ jsx9(DialogOverlay, {}),
/* @__PURE__ */ jsxs6(
DialogPrimitive.Content,
{
"data-slot": "dialog-content",
className: cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
className
),
...props,
children: [
children,
showCloseButton && /* @__PURE__ */ jsxs6(
DialogPrimitive.Close,
{
"data-slot": "dialog-close",
className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
children: [
/* @__PURE__ */ jsx9(XIcon, {}),
/* @__PURE__ */ jsx9("span", { className: "sr-only", children: "Close" })
]
}
)
]
}
)
] });
}
function DialogHeader({ className, ...props }) {
return /* @__PURE__ */ jsx9(
"div",
{
"data-slot": "dialog-header",
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
...props
}
);
}
function DialogFooter({ className, ...props }) {
return /* @__PURE__ */ jsx9(
"div",
{
"data-slot": "dialog-footer",
className: cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className
),
...props
}
);
}
function DialogTitle({
className,
...props
}) {
return /* @__PURE__ */ jsx9(
DialogPrimitive.Title,
{
"data-slot": "dialog-title",
className: cn("text-lg leading-none font-semibold", className),
...props
}
);
}
function DialogDescription({
className,
...props
}) {
return /* @__PURE__ */ jsx9(
DialogPrimitive.Description,
{
"data-slot": "dialog-description",
className: cn("text-muted-foreground text-sm", className),
...props
}
);
}
// src/components/ui/dropdown-menu.tsx
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
function DropdownMenu({
...props
}) {
return /* @__PURE__ */ jsx10(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
}
function DropdownMenuTrigger({
...props
}) {
return /* @__PURE__ */ jsx10(
DropdownMenuPrimitive.Trigger,
{
"data-slot": "dropdown-menu-trigger",
...props
}
);
}
function DropdownMenuContent({
className,
sideOffset = 4,
...props
}) {
return /* @__PURE__ */ jsx10(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx10(
DropdownMenuPrimitive.Content,
{
"data-slot": "dropdown-menu-content",
sideOffset,
className: cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className
),
...props
}
) });
}
function DropdownMenuItem({
className,
inset,
variant = "default",
...props
}) {
return /* @__PURE__ */ jsx10(
DropdownMenuPrimitive.Item,
{
"data-slot": "dropdown-menu-item",
"data-inset": inset,
"data-variant": variant,
className: cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
),
...props
}
);
}
function DropdownMenuSeparator({
className,
...props
}) {
return /* @__PURE__ */ jsx10(
DropdownMenuPrimitive.Separator,
{
"data-slot": "dropdown-menu-separator",
className: cn("bg-border -mx-1 my-1 h-px", className),
...props
}
);
}
// src/components/ui/form.tsx
import * as React from "react";
import { Slot as Slot2 } from "@radix-ui/react-slot";
import {
Controller,
FormProvider,
useFormContext,
useFormState as useFormState2
} from "react-hook-form";
// src/components/ui/label.tsx
import * as LabelPrimitive from "@radix-ui/react-label";
import { jsx as jsx11 } from "react/jsx-runtime";
function Label2({
className,
...props
}) {
return /* @__PURE__ */ jsx11(
LabelPrimitive.Root,
{
"data-slot": "label",
className: cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
),
...props
}
);
}
// src/components/ui/form.tsx
import { jsx as jsx12 } from "react/jsx-runtime";
var Form = FormProvider;
var FormFieldContext = React.createContext(
{}
);
var FormField = ({
...props
}) => {
return /* @__PURE__ */ jsx12(FormFieldContext.Provider, { value: { name: props.name }, children: /* @__PURE__ */ jsx12(Controller, { ...props }) });
};
var useFormField = () => {
const fieldContext = React.useContext(FormFieldContext);
const itemContext = React.useContext(FormItemContext);
const { getFieldState } = useFormContext();
const formState = useFormState2({ name: fieldContext.name });
const fieldState = getFieldState(fieldContext.name, formState);
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>");
}
const { id } = itemContext;
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState
};
};
var FormItemContext = React.createContext(
{}
);
function FormItem({ className, ...props }) {
const id = React.useId();
return /* @__PURE__ */ jsx12(FormItemContext.Provider, { value: { id }, children: /* @__PURE__ */ jsx12(
"div",
{
"data-slot": "form-item",
className: cn("grid gap-2", className),
...props
}
) });
}
function FormLabel({
className,
...props
}) {
const { error, formItemId } = useFormField();
return /* @__PURE__ */ jsx12(
Label2,
{
"data-slot": "form-label",
"data-error": !!error,
className: cn("data-[error=true]:text-destructive", className),
htmlFor: formItemId,
...props
}
);
}
function FormControl({ ...props }) {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
return /* @__PURE__ */ jsx12(
Slot2,
{
"data-slot": "form-control",
id: formItemId,
"aria-describedby": !error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`,
"aria-invalid": !!error,
...props
}
);
}
function FormMessage({ className, ...props }) {
const { error, formMessageId } = useFormField();
const body = error ? String((error == null ? void 0 : error.message) ?? "") : props.children;
if (!body) {
return null;
}
return /* @__PURE__ */ jsx12(
"p",
{
"data-slot": "form-message",
id: formMessageId,
className: cn("text-destructive text-sm", className),
...props,
children: body
}
);
}
// src/components/ui/input.tsx
import { jsx as jsx13 } from "react/jsx-runtime";
function Input({ className, type, ...props }) {
return /* @__PURE__ */ jsx13(
"input",
{
type,
"data-slot": "input",
className: cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className
),
...props
}
);
}
// src/components/organization/organization-logo.tsx
import { BuildingIcon } from "lucide-react";
import { useContext as useContext3, useMemo } from "react";
// src/components/ui/avatar.tsx
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { jsx as jsx14 } from "react/jsx-runtime";
function Avatar({
className,
...props
}) {
return /* @__PURE__ */ jsx14(
AvatarPrimitive.Root,
{
"data-slot": "avatar",
className: cn(
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
className
),
...props
}
);
}
function AvatarImage({
className,
...props
}) {
return /* @__PURE__ */ jsx14(
AvatarPrimitive.Image,
{
"data-slot": "avatar-image",
className: cn("aspect-square size-full", className),
...props
}
);
}
function AvatarFallback({
className,
...props
}) {
return /* @__PURE__ */ jsx14(
AvatarPrimitive.Fallback,
{
"data-slot": "avatar-fallback",
className: cn(
"bg-muted flex size-full items-center justify-center rounded-full",
className
),
...props
}
);
}
// src/components/organization/organization-logo.tsx
import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
function OrganizationLogo({
className,
classNames,
isPending,
size,
organization,
localization: propLocalization,
...props
}) {
const { localization: contextLocalization, avatar } = useContext3(AuthUIContext);
const localization = useMemo(
() => ({ ...contextLocalization, ...propLocalization }),
[contextLocalization, propLocalization]
);
const name = organization == null ? void 0 : organization.name;
const src = organization == null ? void 0 : organization.logo;
if (isPending) {
return /* @__PURE__ */ jsx15(
Skeleton,
{
className: cn(
"shrink-0 rounded-full",
size === "sm" ? "size-6" : size === "lg" ? "size-10" : size === "xl" ? "size-12" : "size-8",
className,
classNames == null ? void 0 : classNames.base,
classNames == null ? void 0 : classNames.skeleton
)
}
);
}
return /* @__PURE__ */ jsxs8(
Avatar,
{
className: cn(
"bg-muted",
size === "sm" ? "size-6" : size === "lg" ? "size-10" : size === "xl" ? "size-12" : "size-8",
className,
classNames == null ? void 0 : classNames.base
),
...props,
children: [
(avatar == null ? void 0 : avatar.Image) ? /* @__PURE__ */ jsx15(
avatar.Image,
{
alt: name || (localization == null ? void 0 : localization.ORGANIZATION),
className: classNames == null ? void 0 : classNames.image,
src: src || ""
}
) : /* @__PURE__ */ jsx15(
AvatarImage,
{
alt: name || (localization == null ? void 0 : localization.ORGANIZATION),
className: classNames == null ? void 0 : classNames.image,
src: src || void 0
}
),
/* @__PURE__ */ jsx15(
AvatarFallback,
{
className: cn("text-foreground", classNames == null ? void 0 : classNames.fallback),
delayMs: src ? 600 : void 0,
children: /* @__PURE__ */ jsx15(
BuildingIcon,
{
className: cn("size-[50%]", classNames == null ? void 0 : classNames.fallbackIcon)
}
)
}
)
]
}
);
}
// src/components/organization/create-organization-dialog.tsx
import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
function CreateOrganizationDialog({
className,
classNames,
localization: localizationProp,
onOpenChange,
...props
}) {
var _a, _b, _c;
const {
authClient,
localization: contextLocalization,
organization: organizationOptions,
navigate,
toast,
localizeErrors
} = useContext4(AuthUIContext);
const localization = useMemo2(
() => ({ ...contextLocalization, ...localizationProp }),
[contextLocalization, localizationProp]
);
const [logo, setLogo] = useState(null);
const [logoPending, setLogoPending] = useState(false);
const fileInputRef = useRef(null);
const openFileDialog = () => {
var _a2;
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
};
const formSchema = z.object({
logo: z.string().optional(),
name: z.string().min(1, {
message: `${localization.ORGANIZATION_NAME} ${localization.IS_REQUIRED}`
}),
slug: z.string().min(1, {
message: `${localization.ORGANIZATION_SLUG} ${localization.IS_REQUIRED}`
}).regex(/^[a-z0-9-]+$/, {
message: `${localization.ORGANIZATION_SLUG} ${localization.IS_INVALID}`
})
});
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
logo: "",
name: "",
slug: ""
}
});
const isSubmitting = form.formState.isSubmitting;
const handleLogoChange = async (file) => {
if (!(organizationOptions == null ? void 0 : organizationOptions.logo)) return;
setLogoPending(true);
try {
const resizedFile = await resizeAndCropImage(
file,
crypto.randomUUID(),
organizationOptions.logo.size,
organizationOptions.logo.extension
);
let image;
if (organizationOptions == null ? void 0 : organizationOptions.logo.upload) {
image = await organizationOptions.logo.upload(resizedFile);
} else {
image = await fileToBase64(resizedFile);
}
setLogo(image || null);
form.setValue("logo", image || "");
} catch (error) {
toast({
variant: "error",
message: getLocalizedError({
error,
localization,
localizeErrors
})
});
}
setLogoPending(false);
};
const deleteLogo = async () => {
var _a2;
setLogoPending(true);
const currentUrl = logo || void 0;
if (currentUrl && ((_a2 = organizationOptions == null ? void 0 : organizationOptions.logo) == null ? void 0 : _a2.delete)) {
await organizationOptions.logo.delete(currentUrl);
}
setLogo(null);
form.setValue("logo", "");
setLogoPending(false);
};
async function onSubmit({ name, slug, logo: logo2 }) {
try {
const organization = await authClient.organization.create({
name,
slug,
logo: logo2,
fetchOptions: { throw: true }
});
if ((organizationOptions == null ? void 0 : organizationOptions.pathMode) === "slug") {
navigate(`${organizationOptions.basePath}/${organization.slug}`);
return;
}
await authClient.organization.setActive({
organizationId: organization.id
});
onOpenChange == null ? void 0 : onOpenChange(false);
form.reset();
setLogo(null);
toast({
variant: "success",
message: localization.CREATE_ORGANIZATION_SUCCESS
});
} catch (error) {
toast({
variant: "error",
message: getLocalizedError({
error,
localization,
localizeErrors
})
});
}
}
return /* @__PURE__ */ jsx16(Dialog, { onOpenChange, ...props, children: /* @__PURE__ */ jsxs9(DialogContent, { className: (_a = classNames == null ? void 0 : classNames.dialog) == null ? void 0 : _a.content, children: [
/* @__PURE__ */ jsxs9(DialogHeader, { className: (_b = classNames == null ? void 0 : classNames.dialog) == null ? void 0 : _b.header, children: [
/* @__PURE__ */ jsx16(
DialogTitle,
{
className: cn("text-lg md:text-xl", classNames == null ? void 0 : classNames.title),
children: localization.CREATE_ORGANIZATION
}
),
/* @__PURE__ */ jsx16(
DialogDescription,
{
className: cn(
"text-xs md:text-sm",
classNames == null ? void 0 : classNames.description
),
children: localization.ORGANIZATIONS_INSTRUCTIONS
}
)
] }),
/* @__PURE__ */ jsx16(Form, { ...form, children: /* @__PURE__ */ jsxs9(
"form",
{
onSubmit: form.handleSubmit(onSubmit),
className: "space-y-6",
children: [
(organizationOptions == null ? void 0 : organizationOptions.logo) && /* @__PURE__ */ jsx16(
FormField,
{
control: form.control,
name: "logo",
render: () => /* @__PURE__ */ jsxs9(FormItem, { children: [
/* @__PURE__ */ jsx16(
"input",
{
ref: fileInputRef,
accept: "image/*",
disabled: logoPending,
hidden: true,
type: "file",
onChange: (e) => {
var _a2;
const file = (_a2 = e.target.files) == null ? void 0 : _a2.item(0);
if (file) handleLogoChange(file);
e.target.value = "";
}
}
),
/* @__PURE__ */ jsx16(FormLabel, { children: localization.LOGO }),
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-4", children: [
/* @__PURE__ */ jsxs9(DropdownMenu, { children: [
/* @__PURE__ */ jsx16(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx16(
Button,
{
className: "size-fit rounded-full",
size: "icon",
type: "button",
variant: "ghost",
children: /* @__PURE__ */ jsx16(
OrganizationLogo,
{
className: "size-16",
isPending: logoPending,
localization,
organization: {
name: form.watch(
"name"
),
logo
}
}
)
}
) }),
/* @__PURE__ */ jsxs9(
DropdownMenuContent,
{
align: "start",
onCloseAutoFocus: (e) => e.preventDefault(),
children: [
/* @__PURE__ */ jsxs9(
DropdownMenuItem,
{
onClick: openFileDialog,
disabled: logoPending,
children: [
/* @__PURE__ */ jsx16(UploadCloudIcon, {}),
localization.UPLOAD_LOGO
]
}
),
logo && /* @__PURE__ */ jsxs9(
DropdownMenuItem,
{
onClick: deleteLogo,
disabled: logoPending,
variant: "destructive",
children: [
/* @__PURE__ */ jsx16(Trash2Icon, {}),
localization.DELETE_LOGO
]
}
)
]
}
)
] }),
/* @__PURE__ */ jsxs9(
Button,
{
disabled: logoPending,
variant: "outline",
onClick: openFileDialog,
type: "button",
children: [
logoPending && /* @__PURE__ */ jsx16(Loader22, { className: "animate-spin" }),
localization.UPLOAD
]
}
)
] }),
/* @__PURE__ */ jsx16(FormMessage, {})
] })
}
),
/* @__PURE__ */ jsx16(
FormField,
{
control: form.control,
name: "name",
render: ({ field }) => /* @__PURE__ */ jsxs9(FormItem, { children: [
/* @__PURE__ */ jsx16(FormLabel, { children: localization.ORGANIZATION_NAME }),
/* @__PURE__ */ jsx16(FormControl, { children: /* @__PURE__ */ jsx16(
Input,
{
placeholder: localization.ORGANIZATION_NAME_PLACEHOLDER,
...field
}
) }),
/* @__PURE__ */ jsx16(FormMessage, {})
] })
}
),
/* @__PURE__ */ jsx16(
FormField,
{
control: form.control,
name: "slug",
render: ({ field }) => /* @__PURE__ */ jsxs9(FormItem, { children: [
/* @__PURE__ */ jsx16(FormLabel, { children: localization.ORGANIZATION_SLUG }),
/* @__PURE__ */ jsx16(FormControl, { children: /* @__PURE__ */ jsx16(
Input,
{
placeholder: localization.ORGANIZATION_SLUG_PLACEHOLDER,
...field
}
) }),
/* @__PURE__ */ jsx16(FormMessage, {})
] })
}
),
/* @__PURE__ */ jsxs9(DialogFooter, { className: (_c = classNames == null ? void 0 : classNames.dialog) == null ? void 0 : _c.footer, children: [
/* @__PURE__ */ jsx16(
Button,
{
type: "button",
variant: "outline",
onClick: () => onOpenChange == null ? void 0 : onOpenChange(false),
className: cn(
classNames == null ? void 0 : classNames.button,
classNames == null ? void 0 : classNames.outlineButton
),
children: localization.CANCEL
}
),
/* @__PURE__ */ jsxs9(
Button,
{
type: "submit",
className: cn(
classNames == null ? void 0 : classNames.button,
classNames == null ? void 0 : classNames.primaryButton
),
disabled: isSubmitting,
children: [
isSubmitting && /* @__PURE__ */ jsx16(Loader22, { className: "animate-spin" }),
localization.CREATE_ORGANIZATION
]
}
)
] })
]
}
) })
] }) });
}
// src/components/organization/organization-cell.tsx
import { EllipsisIcon, Loader2 as Loader24, LogOutIcon, SettingsIcon } from "lucide-react";
import { useCallback, useContext as useContext7, useMemo as useMemo4, useState as useState3 } from "react";
// src/components/organization/leave-organization-dialog.tsx
import { Loader2 as Loader23 } from "lucide-react";
import { useContext as useContext6, useMemo as useMemo3, useState as useState2 } from "react";
// src/components/organization/organization-cell-view.tsx
import { useContext as useContext5 } from "react";
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
function OrganizationCellView({
className,
classNames,
isPending,
size,
organization,
localization: propLocalization
}) {
const { localization: contextLocalization } = useContext5(AuthUIContext);
const localization = { ...contextLocalization, ...propLocalization };
return /* @__PURE__ */ jsxs10(
"div",
{
className: cn(
"flex items-center gap-2 truncate",
className,
classNames == null ? void 0 : classNames.base
),
children: [
/* @__PURE__ */ jsx17(
OrganizationLogo,
{
className: cn(size !== "sm" && "my-0.5"),
classNames: classNames == null ? void 0 : classNames.avatar,
isPending,
localization,
organization,
size
}
),
/* @__PURE__ */ jsx17(
"div",
{
className: cn(
"flex flex-col truncate text-left leading-tight",
classNames == null ? void 0 : classNames.content
),
children: isPending ? /* @__PURE__ */ jsxs10(Fragment3, { children: [
/* @__PURE__ */ jsx17(
Skeleton,
{
className: cn(
"max-w-full",
size === "lg" ? "h-4.5 w-32" : "h-3.5 w-24",
classNames == null ? void 0 : classNames.title,
classNames == null ? void 0 : classNames.skeleton
)
}
),
size !== "sm" && /* @__PURE__ */ jsx17(
Skeleton,
{
className: cn(
"mt-1.5 max-w-full",
size === "lg" ? "h-3.5 w-24" : "h-3 w-16",
classNames == null ? void 0 : classNames.subtitle,
classNames == null ? void 0 : classNames.skeleton
)
}
)
] }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
/* @__PURE__ */ jsx17(
"span",
{
className: cn(
"truncate font-semibold",
size === "lg" ? "text-base" : "text-sm",
classNames == null ? void 0 : classNames.title
),
children: (organization == null ? void 0 : organization.name) || (localization == null ? void 0 : localization.ORGANIZATION)
}
),
size !== "sm" && (organization == null ? void 0 : organization.slug) && /* @__PURE__ */ jsx17(
"span",
{
className: cn(
"truncate opacity-70",
size === "lg" ? "text-sm" : "text-xs",
classNames == null ? void 0 : classNames.subtitle
),
children: organization.slug
}
)
] })
}
)
]
}
);
}
// src/components/organization/leave-organization-dialog.tsx
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
function LeaveOrganizationDialog({
organization,
className,
classNames,
localization: localizationProp,
onOpenChange,
...props
}) {
var _a, _b, _c;
const {
authClient,
hooks: { useListOrganizations },
localization: contextLocalization,
toast,
localizeErrors
} = useContext6(AuthUIContext);
const localization = useMemo3(
() => ({ ...contextLocalization, ...localizationProp }),
[contextLocalization, localizationProp]
);
const { refetch: refetchOrganizations } = useListOrganizations();
const [isLeaving, setIsLeaving] = useState2(false);
const handleLeaveOrganization = async () => {
setIsLeaving(true);
try {
await authClient.organization.leave({
organizationId: organization.id,
fetchOptions: { throw: true }
});
await (refetchOrganizations == null ? void 0 : refetchOrganizations());
toast({
variant: "success",
message: localization.LEAVE_ORGANIZATION_SUCCESS
});
onOpenChange == null ? void 0 : onOpenChange(false);
} catch (error) {
toast({
variant: "error",
message: getLocalizedError({
error,
localization,
localizeErrors
})
});
}
setIsLeaving(false);
};
return /* @__PURE__ */ jsx18(Dialog, { onOpenChange, ...props, children: /* @__PURE__ */ jsxs11(
DialogContent,
{
className: (_a = classNames == null ? void 0 : classNames.dialog) == null ? void 0 : _a.content,
onOpenAutoFocus: (e) => e.preventDefault(),
children: [
/* @__PURE__ */ jsxs11(DialogHeader, { className: (_b = classNames == null ? void 0 : classNames.dialog) == null ? void 0 : _b.header, children: [
/* @__PURE__ */ jsx18(
DialogTitle,
{
className: cn("text-lg md:text-xl", classNames == null ? void 0 : classNames.title),
children: localization.LEAVE_ORGANIZATION
}
),
/* @__PURE__ */ jsx18(
DialogDescription,
{
className: cn(
"text-xs md:text-sm",
classNames == null ? void 0 : classNames.description
),
children: localization.LEAVE_ORGANIZATION_CONFIRM
}
)
] }),
/* @__PURE__ */ jsx18(
Card,
{
className: cn(
"my-2 flex-row p-4",
className,
classNames == null ? void 0 : classNames.cell
),
children: /* @__PURE__ */ jsx18(
OrganizationCellView,
{
organization,
localization
}
)
}
),
/* @__PURE__ */ jsxs11(DialogFooter, { className: (_c = classNames == null ? void 0 : classNames.dialog) == null ? void 0 : _c.footer, children: [
/* @__PURE__ */ jsx18(
Button,
{
type: "button",
variant: "outline",
onClick: () => onOpenChange == null ? void 0 : onOpenChange(false),
className: cn(
classNames == null ? void 0 : classNames.button,
classNames == null ? void 0 : classNames.outlineButton
),
disabled: isLeaving,
children: localization.CANCEL
}
),
/* @__PURE__ */ jsxs11(
Button,
{
type: "button",
variant: "destructive",
onClick: handleLeaveOrganization,
className: cn(
classNames == null ? void 0 : classNames.button,
classNames == null ? void 0 : classNames.destructiveButton
),
disabled: isLeaving,
children: [
isLeaving && /* @__PURE__ */ jsx18(Loader23, { className: "animate-spin" }),
localization.LEAVE_ORGANIZATION
]
}
)
] })
]
}
) });
}
// src/components/organization/organization-cell.tsx
import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
function OrganizationCell({
className,
classNames,
organization,
localization: localizationProp
}) {
var _a;
const {
authClient,
localization: contextLocalization,
organization: organizationOptions,
navigate,
toast,
localizeErrors
} = useContext7(AuthUIContext);
const localization = useMemo4(
() => ({ ...contextLocalization, ...localizationProp }),
[contextLocalization, localizationProp]
);
const { pathMode } = organizationOptions || {};
const [isLeaveDialogOpen, setIsLeaveDialogOpen] = useState3(false);
const [isManagingOrganization, setIsManagingOrganization] = useState3(false);
const handleManageOrganization = useCallback(async () => {
var _a2;
setIsManagingOrganization(true);
if (pathMode === "slug") {
navigate(
`${organizationOptions == null ? void 0 : organizationOptions.basePath}/${organization.slug}/${organizationOptions == null ? void 0 : organizationOptions.viewPaths.SETTINGS}`
);
return;
}
try {
await authClient.organization.setActive({
organizationId: organization.id,
fetchOptions: {
throw: true
}
});
navigate(
`${organizationOptions == null ? void 0 : organizationOptions.basePath}/${(_a2 = organizationOptions == null ? void 0 : organizationOptions.viewPaths) == null ? void 0 : _a2.SETTINGS}`
);
} catch (error) {
toast({
variant: "error",
message: getLocalizedError({
error,
localization,
localizeErrors
})
});
setIsManagingOrganization(false);
}
}, [
authClient,
organization.id,
organizationOptions == null ? void 0 : organizationOptions.basePath,
(_a = organizationOptions == null ? void 0 : organizationOptions.viewPaths) == null ? void 0 : _a.SETTINGS,
organization.slug,
pathMode,
navigate,
toast,
localization,
localizeErrors
]);
return /* @__PURE__ */ jsxs12(Fragment4, { children: [
/* @__PURE__ */ jsxs12(Card, { className: cn("flex-row p-4", className, classNames == null ? void 0 : classNames.cell), children: [
/* @__PURE__ */ jsx19(
OrganizationCellView,
{
organization,
localization
}
),
/* @__PURE__ */ jsxs12(DropdownMenu, { children: [
/* @__PURE__ */ jsx19(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx19(