{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "organization",
  "title": "Organization",
  "description": "Organization plugin: multi-tenant organization management with members, invitations, and roles. Registers direct invitation acceptance, an organizations settings tab, a full `<Organization />` shell, and an `<OrganizationSwitcher />` dropdown.",
  "dependencies": [
    "@better-auth-ui/core@latest",
    "@better-auth-ui/react@latest",
    "@tanstack/react-form",
    "@tanstack/react-pacer",
    "@tanstack/react-query",
    "@tanstack/react-store",
    "@tanstack/react-table",
    "better-auth",
    "cn",
    "date-fns",
    "lucide-react"
  ],
  "registryDependencies": [
    "alert-dialog",
    "avatar",
    "badge",
    "button",
    "calendar",
    "card",
    "checkbox",
    "combobox",
    "dialog",
    "dropdown-menu",
    "empty",
    "field",
    "https://better-auth-ui.com/r/radix-nova/additional-field.json",
    "https://better-auth-ui.com/r/radix-nova/user-view.json",
    "input",
    "input-group",
    "item",
    "label",
    "popover",
    "radio-group",
    "select",
    "separator",
    "skeleton",
    "slider",
    "sonner",
    "spinner",
    "switch",
    "table",
    "tabs",
    "textarea"
  ],
  "files": [
    {
      "path": "examples/start-shadcn-example/src/components/auth/additional-field.tsx",
      "content": "\"use client\"\n\nimport {\n  type AdditionalField as AdditionalFieldConfig,\n  type AdditionalFieldFormValue,\n  getFormFieldErrors,\n  resolveInputType\n} from \"@better-auth-ui/core\"\nimport { useAuth, useCopyToClipboard } from \"@better-auth-ui/react\"\nimport { format } from \"date-fns\"\nimport { CalendarIcon, Check, ChevronDownIcon, Copy } from \"lucide-react\"\nimport { type ComponentType, useRef, useState } from \"react\"\nimport { toast } from \"sonner\"\n\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Calendar } from \"@/components/ui/calendar\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  Combobox,\n  ComboboxContent,\n  ComboboxEmpty,\n  ComboboxInput,\n  ComboboxItem,\n  ComboboxList\n} from \"@/components/ui/combobox\"\nimport {\n  Field,\n  FieldContent,\n  FieldError,\n  FieldLabel\n} from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput\n} from \"@/components/ui/input-group\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger\n} from \"@/components/ui/popover\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue\n} from \"@/components/ui/select\"\nimport { Slider } from \"@/components/ui/slider\"\nimport { Switch } from \"@/components/ui/switch\"\nimport { Textarea } from \"@/components/ui/textarea\"\nimport { cn } from \"cn\"\n\nexport type AdditionalFieldProps = {\n  name: string\n  field: AdditionalFieldConfig\n  value: AdditionalFieldFormValue\n  onBlur: () => void\n  onChange: (value: AdditionalFieldFormValue) => void\n  isInvalid?: boolean\n  errors?: unknown[]\n  isPending?: boolean\n  /** Complete suffix appended to labels for fields that are not required. */\n  optionalLabel?: string\n}\n\nfunction valueToString(value: AdditionalFieldFormValue) {\n  if (value == null) return \"\"\n  return value instanceof Date ? value.toISOString() : String(value)\n}\n\n/** Convert a `defaultValue` into a `Date` for the calendar. */\nfunction toDate(value: unknown): Date | undefined {\n  if (value instanceof Date) return value\n  if (typeof value === \"string\") {\n    const parsed = new Date(value)\n    return Number.isNaN(parsed.getTime()) ? undefined : parsed\n  }\n  return undefined\n}\n\n/** Format a Date as `HH:mm:ss` for an `<input type=\"time\">`. */\nfunction formatTime(date: Date) {\n  const pad = (n: number) => n.toString().padStart(2, \"0\")\n  return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n}\n\n/**\n * Icon-only copy button used as an `InputGroupAddon`. `getValue` is invoked\n * lazily on click so the button copies the input's *live* value rather than a\n * stale snapshot — important when paired with editable inputs.\n */\nfunction CopyButton({\n  getValue,\n  isDisabled\n}: {\n  getValue: () => string | undefined\n  isDisabled?: boolean\n}) {\n  const { localization } = useAuth()\n  const { copied, copy } = useCopyToClipboard({\n    onError: (error) => {\n      console.error(\"[Better Auth UI] Copy failed\", error)\n      toast.error(localization.errors.copyFailed)\n    }\n  })\n\n  async function handleCopy() {\n    const value = getValue()\n    if (!value) return\n\n    await copy(value)\n  }\n\n  return (\n    <InputGroupButton\n      aria-label={\n        copied\n          ? localization.settings.copiedToClipboard\n          : localization.settings.copyToClipboard\n      }\n      title={\n        copied\n          ? localization.settings.copiedToClipboard\n          : localization.settings.copyToClipboard\n      }\n      onClick={handleCopy}\n      disabled={isDisabled}\n    >\n      {copied ? <Check /> : <Copy />}\n    </InputGroupButton>\n  )\n}\n\n/** Renders a single additional user field via shadcn primitives. */\nexport function AdditionalField({\n  name,\n  field: configuredField,\n  value,\n  onBlur,\n  onChange,\n  isInvalid,\n  errors,\n  isPending,\n  optionalLabel\n}: AdditionalFieldProps) {\n  const field =\n    optionalLabel && !configuredField.required\n      ? {\n          ...configuredField,\n          label: (\n            <>\n              {configuredField.label}\n              {optionalLabel}\n            </>\n          )\n        }\n      : configuredField\n  const inputType = resolveInputType(field)\n  const fieldErrors = getFormFieldErrors(errors ?? [])\n\n  if (field.render) {\n    const FieldRenderer = field.render as ComponentType<AdditionalFieldProps>\n    return (\n      <FieldRenderer\n        name={name}\n        field={field}\n        value={value}\n        onBlur={onBlur}\n        onChange={onChange}\n        isInvalid={isInvalid}\n        errors={errors}\n        isPending={isPending}\n        optionalLabel={optionalLabel}\n      />\n    )\n  }\n\n  if (inputType === \"hidden\") {\n    return (\n      <input type=\"hidden\" name={name} value={valueToString(value)} readOnly />\n    )\n  }\n\n  if (inputType === \"textarea\") {\n    return (\n      <Field data-invalid={isInvalid}>\n        <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n\n        <Textarea\n          id={name}\n          name={name}\n          value={valueToString(value)}\n          onBlur={onBlur}\n          onChange={(event) => onChange(event.target.value || null)}\n          placeholder={field.placeholder}\n          required={field.required}\n          readOnly={field.readOnly}\n          disabled={isPending}\n          aria-invalid={isInvalid}\n        />\n\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  if (inputType === \"number\") {\n    const maxFractionDigits = field.formatOptions?.maximumFractionDigits\n\n    return (\n      <Field data-invalid={isInvalid}>\n        <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n\n        <Input\n          id={name}\n          name={name}\n          type=\"number\"\n          inputMode={maxFractionDigits ? \"decimal\" : \"numeric\"}\n          min={field.min}\n          max={field.max}\n          step={\n            field.step ??\n            (maxFractionDigits ? 1 / 10 ** maxFractionDigits : undefined)\n          }\n          value={typeof value === \"number\" ? value : \"\"}\n          onBlur={onBlur}\n          onChange={(event) =>\n            onChange(\n              event.target.value === \"\" ? null : event.target.valueAsNumber\n            )\n          }\n          placeholder={field.placeholder}\n          required={field.required}\n          readOnly={field.readOnly}\n          disabled={isPending}\n          aria-invalid={isInvalid}\n        />\n\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  if (inputType === \"slider\") {\n    return (\n      <SliderField\n        name={name}\n        field={field}\n        value={value}\n        onBlur={onBlur}\n        onChange={onChange}\n        isInvalid={isInvalid}\n        errors={errors}\n        isPending={isPending}\n      />\n    )\n  }\n\n  if (inputType === \"switch\") {\n    return (\n      <Field data-invalid={isInvalid} orientation=\"horizontal\">\n        <Switch\n          id={name}\n          name={name}\n          checked={value === true}\n          onBlur={onBlur}\n          onCheckedChange={onChange}\n          disabled={isPending || field.readOnly}\n          aria-invalid={isInvalid}\n        />\n\n        <FieldContent>\n          <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n        </FieldContent>\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  if (inputType === \"checkbox\") {\n    return (\n      <Field data-invalid={isInvalid} orientation=\"horizontal\">\n        <Checkbox\n          id={name}\n          name={name}\n          checked={value === true}\n          onBlur={onBlur}\n          onCheckedChange={(checked) => onChange(checked === true)}\n          required={field.required}\n          disabled={isPending || field.readOnly}\n          aria-invalid={isInvalid}\n        />\n\n        <FieldContent>\n          <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n        </FieldContent>\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  if (inputType === \"select\") {\n    return (\n      <Field data-invalid={isInvalid}>\n        <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n\n        <Select\n          name={name}\n          value={valueToString(value) || undefined}\n          onValueChange={(nextValue) => onChange(nextValue)}\n          required={field.required}\n          disabled={isPending || field.readOnly}\n        >\n          <SelectTrigger\n            id={name}\n            className=\"w-full\"\n            onBlur={onBlur}\n            aria-invalid={isInvalid}\n          >\n            <SelectValue placeholder={field.placeholder} />\n          </SelectTrigger>\n\n          <SelectContent>\n            {field.options?.map((option) => (\n              <SelectItem key={option.value} value={option.value}>\n                {option.label}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  if (inputType === \"combobox\") {\n    const selectedOption = field.options?.find(\n      (option) => option.value === valueToString(value)\n    )\n\n    return (\n      <Field data-invalid={isInvalid}>\n        <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n\n        <Combobox\n          items={field.options ?? []}\n          name={name}\n          value={selectedOption ?? null}\n          onValueChange={(option) => onChange(option?.value ?? null)}\n          required={field.required}\n          disabled={isPending || field.readOnly}\n        >\n          <ComboboxInput\n            placeholder={field.placeholder}\n            id={name}\n            onBlur={onBlur}\n            aria-invalid={isInvalid}\n          />\n\n          <ComboboxContent>\n            <ComboboxEmpty>No items found.</ComboboxEmpty>\n\n            <ComboboxList>\n              {(option) => (\n                <ComboboxItem key={option.value} value={option}>\n                  {option.label}\n                </ComboboxItem>\n              )}\n            </ComboboxList>\n          </ComboboxContent>\n        </Combobox>\n\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  if (inputType === \"date\" || inputType === \"datetime\") {\n    return (\n      <DateInput\n        name={name}\n        field={field}\n        value={value}\n        onBlur={onBlur}\n        onChange={onChange}\n        isInvalid={isInvalid}\n        errors={errors}\n        isPending={isPending}\n      />\n    )\n  }\n\n  return (\n    <InputField\n      name={name}\n      field={field}\n      value={value}\n      onBlur={onBlur}\n      onChange={onChange}\n      isInvalid={isInvalid}\n      errors={errors}\n      isPending={isPending}\n    />\n  )\n}\n\nfunction InputField({\n  name,\n  field,\n  value,\n  onBlur,\n  onChange,\n  isInvalid,\n  errors,\n  isPending\n}: AdditionalFieldProps) {\n  const inputRef = useRef<HTMLInputElement>(null)\n  const fieldErrors = getFormFieldErrors(errors ?? [])\n\n  const hasPrefix = field.prefix != null\n  const hasSuffix = field.suffix != null || field.copyable\n\n  const isNumeric = field.type === \"number\"\n  const maxFractionDigits = field.formatOptions?.maximumFractionDigits\n  const nativeInputType = isNumeric ? \"number\" : undefined\n  const nativeInputMode = isNumeric\n    ? maxFractionDigits\n      ? \"decimal\"\n      : \"numeric\"\n    : undefined\n  const nativeStep = maxFractionDigits ? 1 / 10 ** maxFractionDigits : undefined\n\n  if (hasPrefix || hasSuffix) {\n    return (\n      <Field data-invalid={isInvalid}>\n        <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n\n        <InputGroup>\n          {hasPrefix && (\n            <InputGroupAddon align=\"inline-start\">\n              {field.prefix}\n            </InputGroupAddon>\n          )}\n\n          <InputGroupInput\n            ref={inputRef}\n            id={name}\n            name={name}\n            type={nativeInputType}\n            inputMode={nativeInputMode}\n            step={nativeStep}\n            value={valueToString(value)}\n            onBlur={onBlur}\n            onChange={(event) => onChange(event.target.value || null)}\n            placeholder={field.placeholder}\n            required={field.required}\n            readOnly={field.readOnly}\n            disabled={isPending}\n            aria-invalid={isInvalid}\n          />\n\n          {field.copyable ? (\n            <InputGroupAddon align=\"inline-end\">\n              <CopyButton\n                getValue={() => inputRef.current?.value}\n                isDisabled={isPending}\n              />\n            </InputGroupAddon>\n          ) : (\n            field.suffix != null && (\n              <InputGroupAddon align=\"inline-end\">\n                {field.suffix}\n              </InputGroupAddon>\n            )\n          )}\n        </InputGroup>\n\n        <FieldError errors={fieldErrors} />\n      </Field>\n    )\n  }\n\n  return (\n    <Field data-invalid={isInvalid}>\n      <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n\n      <Input\n        id={name}\n        name={name}\n        type={nativeInputType}\n        inputMode={nativeInputMode}\n        step={nativeStep}\n        value={valueToString(value)}\n        onBlur={onBlur}\n        onChange={(event) => onChange(event.target.value || null)}\n        placeholder={field.placeholder}\n        required={field.required}\n        readOnly={field.readOnly}\n        disabled={isPending}\n        aria-invalid={isInvalid}\n      />\n\n      <FieldError errors={fieldErrors} />\n    </Field>\n  )\n}\n\n/**\n * Slider field. Radix Slider doesn't render the current value, so we render\n * it next to the label and control the state to keep the displayed value in\n * sync. The selected value is submitted via the underlying Radix `name` prop.\n */\nfunction SliderField({\n  name,\n  field,\n  value,\n  onBlur,\n  onChange,\n  isInvalid,\n  errors,\n  isPending\n}: AdditionalFieldProps) {\n  const maxFractionDigits = field.formatOptions?.maximumFractionDigits\n  const min = field.min ?? 0\n  const max = field.max ?? 100\n  const step =\n    field.step ?? (maxFractionDigits ? 1 / 10 ** maxFractionDigits : 1)\n  const numericValue = typeof value === \"number\" ? value : min\n  const fieldErrors = getFormFieldErrors(errors ?? [])\n\n  const formatter = new Intl.NumberFormat(undefined, field.formatOptions)\n\n  return (\n    <Field data-invalid={isInvalid}>\n      <div className=\"flex items-center justify-between gap-2\">\n        <FieldLabel htmlFor={name}>{field.label}</FieldLabel>\n        <span className=\"text-sm text-muted-foreground tabular-nums\">\n          {formatter.format(numericValue)}\n        </span>\n      </div>\n\n      <Slider\n        id={name}\n        name={name}\n        value={[numericValue]}\n        onBlur={onBlur}\n        onValueChange={(nextValue) =>\n          onChange((Array.isArray(nextValue) ? nextValue[0] : nextValue) ?? min)\n        }\n        min={min}\n        max={max}\n        step={step}\n        disabled={isPending || field.readOnly}\n        aria-invalid={isInvalid}\n      />\n\n      <FieldError errors={fieldErrors} />\n    </Field>\n  )\n}\n\n/**\n * Date / datetime input. Composes `Popover` + `Calendar` for the date and\n * (optionally) `<input type=\"time\">` for the time. Submits the combined ISO\n * value via a hidden `<input>` so it shows up in `FormData`.\n */\nfunction DateInput({\n  name,\n  field,\n  value,\n  onBlur,\n  onChange,\n  isInvalid,\n  errors,\n  isPending\n}: AdditionalFieldProps) {\n  const { localization } = useAuth()\n  const inputType = resolveInputType(field)\n  const isDateTime = inputType === \"datetime\"\n  const fieldErrors = getFormFieldErrors(errors ?? [])\n\n  const date = toDate(value)\n  const [time, setTime] = useState<string>(\n    isDateTime && date ? formatTime(date) : \"\"\n  )\n  const [open, setOpen] = useState(false)\n\n  // Compose the hidden form value: ISO date for \"date\", ISO datetime for\n  // \"datetime\" (date + time).\n  return (\n    <Field data-invalid={isInvalid}>\n      <FieldLabel htmlFor={`${name}-date`}>{field.label}</FieldLabel>\n\n      <div className=\"relative flex gap-2\">\n        <Popover open={open} onOpenChange={setOpen}>\n          <PopoverTrigger\n            type=\"button\"\n            id={`${name}-date`}\n            data-empty={!date}\n            aria-invalid={isInvalid}\n            aria-required={field.required}\n            onBlur={onBlur}\n            disabled={isPending || field.readOnly}\n            className={cn(\n              buttonVariants({ variant: \"outline\" }),\n              \"flex-1 justify-between font-normal\",\n              \"data-[empty=true]:text-muted-foreground\"\n            )}\n          >\n            {date ? format(date, \"PPP\") : <span>{field.placeholder}</span>}\n\n            {isDateTime ? <ChevronDownIcon /> : <CalendarIcon />}\n          </PopoverTrigger>\n\n          <PopoverContent className=\"w-auto overflow-hidden p-0\" align=\"start\">\n            <Calendar\n              mode=\"single\"\n              selected={date}\n              defaultMonth={date}\n              captionLayout=\"dropdown\"\n              onSelect={(value) => {\n                if (!value) {\n                  onChange(null)\n                } else {\n                  const nextValue = new Date(value)\n                  if (isDateTime && time.trim()) {\n                    const [hours = \"0\", minutes = \"0\", seconds = \"0\"] =\n                      time.split(\":\")\n                    nextValue.setHours(\n                      Number(hours),\n                      Number(minutes),\n                      Number(seconds),\n                      0\n                    )\n                  } else {\n                    nextValue.setHours(0, 0, 0, 0)\n                  }\n                  onChange(nextValue)\n                }\n                if (!isDateTime) setOpen(false)\n              }}\n            />\n          </PopoverContent>\n        </Popover>\n\n        {isDateTime && (\n          <Field className=\"w-32\">\n            <FieldLabel htmlFor={`${name}-time`} className=\"sr-only\">\n              {localization.settings.time}\n            </FieldLabel>\n\n            <Input\n              type=\"time\"\n              id={`${name}-time`}\n              step=\"1\"\n              value={time}\n              onChange={(event) => {\n                const nextTime = event.target.value\n                setTime(nextTime)\n                if (!date) return\n                const nextValue = new Date(date)\n                const [hours = \"0\", minutes = \"0\", seconds = \"0\"] =\n                  nextTime.split(\":\")\n                nextValue.setHours(\n                  Number(hours),\n                  Number(minutes),\n                  Number(seconds),\n                  0\n                )\n                onChange(nextValue)\n              }}\n              disabled={isPending || field.readOnly}\n              className=\"appearance-none bg-background [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none\"\n            />\n          </Field>\n        )}\n      </div>\n\n      <FieldError errors={fieldErrors} />\n    </Field>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/additional-field.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/auth-form.tsx",
      "content": "\"use client\"\n\nimport {\n  type AdditionalField as AdditionalFieldConfig,\n  type AdditionalFieldFormValue,\n  DEFAULT_ADDITIONAL_FIELD_VALIDATION_DEBOUNCE_MS,\n  getFormFieldErrors,\n  normalizeAuthFormServerError,\n  validateAdditionalFieldRequired,\n  validateAdditionalFieldValue\n} from \"@better-auth-ui/core\"\nimport {\n  type AnyFormApi,\n  createFormHook,\n  createFormHookContexts\n} from \"@tanstack/react-form\"\nimport {\n  type ComponentProps,\n  type FormEvent,\n  type ReactNode,\n  useRef\n} from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Field,\n  FieldDescription,\n  FieldError,\n  FieldLabel\n} from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { AdditionalField, type AdditionalFieldProps } from \"./additional-field\"\n\nconst { fieldContext, formContext, useFieldContext, useFormContext } =\n  createFormHookContexts()\n\nconst DEFAULT_AUTH_FORM_SERVER_ERROR = \"Unable to submit this form. Try again.\"\n\nexport function focusFirstInvalidAuthFormControl(form: HTMLFormElement) {\n  requestAnimationFrame(() => {\n    form\n      .querySelector<HTMLElement>(\n        '[aria-invalid=\"true\"]:not([disabled]), :invalid:not([disabled])'\n      )\n      ?.focus()\n  })\n}\n\nfunction AuthFormFieldError() {\n  const field = useFieldContext<unknown>()\n  const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid\n\n  if (!isInvalid) return null\n\n  const errors = getFormFieldErrors(field.state.meta.errors)\n\n  return errors.length > 0 ? <FieldError errors={errors} /> : null\n}\n\nfunction AuthFormServerError() {\n  const form = useFormContext()\n\n  return (\n    <form.Subscribe selector={(state) => state.errorMap.onServer}>\n      {(error) => {\n        const formError =\n          error && typeof error === \"object\" && \"form\" in error\n            ? error.form\n            : error\n        const errors = getFormFieldErrors(formError ? [formError] : [])\n        return errors.length > 0 ? <FieldError errors={errors} /> : null\n      }}\n    </form.Subscribe>\n  )\n}\n\nexport function setAuthFormServerError(\n  form: AnyFormApi,\n  error: unknown,\n  fallbackMessage: string\n) {\n  const normalized = normalizeAuthFormServerError(error, fallbackMessage)\n  form.setErrorMap({\n    onServer: {\n      fields: normalized.fields ?? {},\n      form: normalized.form\n    }\n  })\n}\n\nexport function clearAuthFormServerError(form: AnyFormApi) {\n  form.setErrorMap({ onServer: { fields: {} } })\n}\n\nexport function clearAuthFormFieldServerError(\n  form: AnyFormApi,\n  fieldName: string\n) {\n  form.setErrorMap({ onServer: undefined })\n  if (!fieldName) return\n\n  const fieldMeta = form.getFieldMeta(fieldName as never)\n  if (!fieldMeta?.errorMap.onServer) return\n\n  form.setFieldMeta(fieldName as never, (current = fieldMeta) => ({\n    ...current,\n    errorMap: { ...current.errorMap, onServer: undefined },\n    errorSourceMap: { ...current.errorSourceMap, onServer: undefined }\n  }))\n}\n\nexport async function runAuthFormAction(\n  form: AnyFormApi,\n  action: () => Promise<unknown>,\n  serverErrorMessage = DEFAULT_AUTH_FORM_SERVER_ERROR\n) {\n  clearAuthFormServerError(form)\n  try {\n    await action()\n    return true\n  } catch (error) {\n    if (!form.state.errorMap.onServer) {\n      setAuthFormServerError(form, error, serverErrorMessage)\n    }\n    return false\n  }\n}\n\nexport async function submitAuthForm(\n  form: AnyFormApi,\n  serverErrorMessage = DEFAULT_AUTH_FORM_SERVER_ERROR\n) {\n  clearAuthFormServerError(form)\n  try {\n    await form.handleSubmit()\n    return form.state.isValid\n  } catch (error) {\n    if (!form.state.errorMap.onServer) {\n      setAuthFormServerError(form, error, serverErrorMessage)\n    }\n    return false\n  }\n}\n\ntype AuthFormRootProps = Omit<ComponentProps<\"form\">, \"onSubmit\"> & {\n  onBeforeSubmit?: () => void\n  serverErrorMessage?: string\n}\n\nfunction AuthFormRoot({\n  children,\n  onBeforeSubmit,\n  onInput,\n  serverErrorMessage = DEFAULT_AUTH_FORM_SERVER_ERROR,\n  ...props\n}: AuthFormRootProps) {\n  const form = useFormContext()\n  const submittingRef = useRef(false)\n\n  async function submit(event: FormEvent<HTMLFormElement>) {\n    event.preventDefault()\n    if (submittingRef.current || form.state.isSubmitting) return\n\n    const formElement = event.currentTarget\n    onBeforeSubmit?.()\n    submittingRef.current = true\n    try {\n      const isValid = await submitAuthForm(form, serverErrorMessage)\n      if (!isValid) focusFirstInvalidAuthFormControl(formElement)\n    } finally {\n      submittingRef.current = false\n    }\n  }\n\n  return (\n    <form\n      {...props}\n      onInvalid={(event) =>\n        focusFirstInvalidAuthFormControl(event.currentTarget)\n      }\n      onInput={(event) => {\n        const target = event.target\n        const fieldName =\n          target instanceof HTMLInputElement ||\n          target instanceof HTMLSelectElement ||\n          target instanceof HTMLTextAreaElement\n            ? target.name\n            : \"\"\n        clearAuthFormFieldServerError(form, fieldName)\n        onInput?.(event)\n      }}\n      onSubmit={submit}\n    >\n      {children}\n    </form>\n  )\n}\n\ntype AuthFormTextFieldProps = Omit<\n  ComponentProps<typeof Input>,\n  \"name\" | \"onBlur\" | \"onChange\" | \"value\"\n> & {\n  description?: ReactNode\n  label: ReactNode\n}\n\nfunction AuthFormTextField({\n  description,\n  id,\n  label,\n  ...props\n}: AuthFormTextFieldProps) {\n  const field = useFieldContext<string>()\n  const form = useFormContext()\n  const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n  const inputId = id ?? field.name\n\n  return (\n    <Field data-invalid={isInvalid}>\n      <FieldLabel htmlFor={inputId}>{label}</FieldLabel>\n      <Input\n        {...props}\n        aria-busy={field.state.meta.isValidating || undefined}\n        aria-invalid={isInvalid}\n        id={inputId}\n        name={field.name}\n        onBlur={field.handleBlur}\n        onChange={(event) => {\n          clearAuthFormFieldServerError(form, field.name)\n          field.handleChange(event.target.value)\n        }}\n        value={field.state.value}\n      />\n      {description ? <FieldDescription>{description}</FieldDescription> : null}\n      <AuthFormFieldError />\n    </Field>\n  )\n}\n\nfunction AuthFormSubmitButton({\n  children,\n  disabled,\n  isPending,\n  ...props\n}: ComponentProps<typeof Button> & { isPending?: boolean }) {\n  const form = useFormContext()\n\n  return (\n    <form.Subscribe\n      selector={(state) => [state.isSubmitting, state.isValidating] as const}\n    >\n      {([isSubmitting, isValidating]) => (\n        <Button\n          {...props}\n          aria-busy={isPending || isSubmitting || undefined}\n          aria-disabled={\n            disabled || isPending || isSubmitting || isValidating || undefined\n          }\n          disabled={disabled || isPending || isSubmitting || isValidating}\n          type=\"submit\"\n        >\n          {isPending || isSubmitting ? (\n            <Spinner data-icon=\"inline-start\" />\n          ) : null}\n          {children}\n        </Button>\n      )}\n    </form.Subscribe>\n  )\n}\n\ntype AuthFormAdditionalFieldProps = Omit<\n  AdditionalFieldProps,\n  \"errors\" | \"isInvalid\" | \"name\" | \"onBlur\" | \"onChange\" | \"value\"\n>\n\nfunction AuthFormAdditionalField(props: AuthFormAdditionalFieldProps) {\n  const field = useFieldContext<AdditionalFieldFormValue>()\n  const form = useFormContext()\n  const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n\n  return (\n    <AdditionalField\n      {...props}\n      errors={\n        isInvalid ? getFormFieldErrors(field.state.meta.errors) : undefined\n      }\n      isInvalid={isInvalid}\n      name={field.name}\n      onBlur={field.handleBlur}\n      onChange={(value) => {\n        clearAuthFormFieldServerError(form, field.name)\n        field.handleChange(value)\n      }}\n      value={field.state.value}\n    />\n  )\n}\n\nexport const {\n  useAppForm: useAuthForm,\n  withFieldGroup: withAuthFieldGroup,\n  withForm: withAuthForm\n} = createFormHook({\n  fieldComponents: {\n    AuthFormAdditionalField,\n    AuthFormFieldError,\n    AuthFormTextField\n  },\n  fieldContext,\n  formComponents: {\n    AuthFormRoot,\n    AuthFormServerError,\n    AuthFormSubmitButton\n  },\n  formContext\n})\n\nexport function isAuthFormFieldInvalid({\n  isTouched,\n  isValid\n}: {\n  isTouched: boolean\n  isValid: boolean\n}) {\n  return isTouched && !isValid\n}\n\nexport function getAuthAdditionalFieldValidators(\n  field: AdditionalFieldConfig,\n  requiredMessage: string\n) {\n  return {\n    onChange: ({ value }: { value: AdditionalFieldFormValue }) =>\n      validateAdditionalFieldRequired(field, value, requiredMessage),\n    onChangeAsync: field.validate\n      ? ({ value }: { value: AdditionalFieldFormValue }) =>\n          validateAdditionalFieldValue(field, value)\n      : undefined,\n    onChangeAsyncDebounceMs: field.validate\n      ? (field.validateDebounceMs ??\n        DEFAULT_ADDITIONAL_FIELD_VALIDATION_DEBOUNCE_MS)\n      : undefined\n  }\n}\n",
      "type": "registry:component",
      "target": "@components/auth/auth-form.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/accept-invitation.tsx",
      "content": "\"use client\"\n\nimport { getSafeRedirectTo } from \"@better-auth-ui/core\"\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthenticate, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useAcceptInvitation,\n  useInvitation,\n  useRejectInvitation\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Invitation } from \"better-auth/client\"\nimport { BriefcaseBusiness } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardFooter,\n  CardHeader,\n  CardTitle\n} from \"@/components/ui/card\"\nimport { FieldDescription } from \"@/components/ui/field\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { UserAvatar } from \"../user/user-avatar\"\nimport { cn } from \"cn\"\nimport { useIsHydrated } from \"../use-is-hydrated\"\n\ntype UserInvitation = Invitation & { organizationName?: string }\n\nexport type AcceptInvitationProps = {\n  className?: string\n}\n\nfunction isPendingInvitation(invitation: UserInvitation | undefined) {\n  if (invitation?.status !== \"pending\") return false\n\n  return new Date(invitation.expiresAt).getTime() > Date.now()\n}\n\n/**\n * Render the organization invitation addressed by the `invitationId` query\n * parameter and let the signed-in recipient accept or reject it directly.\n */\nexport function AcceptInvitation({ className }: AcceptInvitationProps) {\n  const { authClient, navigate, redirectTo } = useAuth()\n  const { localization, roles } = useAuthPlugin(organizationPlugin)\n  const organizationAuthClient = authClient as OrganizationAuthClient\n  const isHydrated = useIsHydrated()\n  const invitationId = isHydrated\n    ? new URLSearchParams(window.location.search).get(\"invitationId\")\n    : null\n  const session = useAuthenticate(organizationAuthClient)\n  const invitationQuery = useInvitation(organizationAuthClient, {\n    query: { id: invitationId ?? \"\" },\n    enabled: Boolean(invitationId)\n  })\n  const invitation = invitationQuery.data as UserInvitation | undefined\n\n  const returnToApplication = () => {\n    navigate({\n      to: getSafeRedirectTo(redirectTo, window.location.origin),\n      replace: true\n    })\n  }\n\n  const { mutate: acceptInvitation, isPending: isAccepting } =\n    useAcceptInvitation(organizationAuthClient, {\n      onSuccess: returnToApplication\n    })\n  const { mutate: rejectInvitation, isPending: isRejecting } =\n    useRejectInvitation(organizationAuthClient, {\n      onSuccess: returnToApplication\n    })\n  const isLoading =\n    !isHydrated ||\n    session.isPending ||\n    !session.data ||\n    (Boolean(invitationId) && invitationQuery.isPending)\n  const isAvailable = isPendingInvitation(invitation)\n  const organizationName =\n    invitation?.organizationName || localization.organization\n  const role = invitation\n    ? (roles?.[invitation.role] ?? invitation.role)\n    : localization.member\n\n  return (\n    <Card\n      className={cn(\"w-full max-w-lg gap-0 overflow-hidden p-0\", className)}\n    >\n      <CardHeader className=\"grid justify-items-center gap-5 p-6 text-center\">\n        <div className=\"flex size-16 items-center justify-center rounded-full bg-muted/50\">\n          <BriefcaseBusiness aria-hidden=\"true\" className=\"size-7\" />\n        </div>\n        <div className=\"grid gap-1\">\n          <CardTitle className=\"max-w-full break-words text-xl font-semibold\">\n            {isLoading ? (\n              <Skeleton className=\"h-6 w-48\" />\n            ) : isAvailable ? (\n              organizationName\n            ) : (\n              localization.invitationUnavailable\n            )}\n          </CardTitle>\n          <CardDescription>\n            {localization.acceptInvitationTitle}\n          </CardDescription>\n        </div>\n      </CardHeader>\n      <CardContent className=\"px-4 pb-6 sm:px-6\">\n        <div className=\"grid gap-5 rounded-lg bg-muted/50 p-5\">\n          {isLoading ? (\n            <Skeleton className=\"h-10 w-full\" />\n          ) : isAvailable ? (\n            <>\n              <FieldDescription>\n                {localization.acceptInvitationDescription\n                  .replace(\"{{organization}}\", organizationName)\n                  .replace(\"{{role}}\", role)}\n              </FieldDescription>\n              <dl className=\"grid gap-1\">\n                <dt className=\"text-sm text-muted-foreground\">\n                  {localization.role}\n                </dt>\n                <dd className=\"text-base font-medium\">{role}</dd>\n              </dl>\n            </>\n          ) : (\n            <FieldDescription>\n              {localization.invitationUnavailableDescription}\n            </FieldDescription>\n          )}\n          <div className=\"flex min-w-0 items-center gap-3\">\n            <UserAvatar\n              user={session.data?.user}\n              isPending={session.isPending}\n            />\n            {session.data ? (\n              <div className=\"grid min-w-0 gap-0.5\">\n                <p className=\"break-words text-sm font-medium\">\n                  {session.data.user.name || session.data.user.email}\n                </p>\n                {session.data.user.name ? (\n                  <p className=\"break-all text-sm text-muted-foreground\">\n                    {session.data.user.email}\n                  </p>\n                ) : null}\n              </div>\n            ) : (\n              <Skeleton className=\"h-4 w-40\" />\n            )}\n          </div>\n        </div>\n      </CardContent>\n      <CardFooter className=\"grid grid-cols-2 gap-3 p-4 sm:p-6\">\n        {isLoading ? (\n          <>\n            <Skeleton className=\"h-11 w-full\" />\n            <Skeleton className=\"h-11 w-full\" />\n          </>\n        ) : isAvailable && invitation ? (\n          <>\n            <Button\n              className=\"h-11 w-full\"\n              variant=\"secondary\"\n              disabled={isAccepting || isRejecting}\n              onClick={() => rejectInvitation({ invitationId: invitation.id })}\n            >\n              {isRejecting ? <Spinner /> : null}\n              {localization.rejectInvitation}\n            </Button>\n            <Button\n              className=\"h-11 w-full\"\n              disabled={isAccepting || isRejecting}\n              onClick={() => acceptInvitation({ invitationId: invitation.id })}\n            >\n              {isAccepting ? <Spinner /> : null}\n              {localization.accept}\n            </Button>\n          </>\n        ) : (\n          <Button\n            className=\"col-span-2 h-11 w-full\"\n            onClick={returnToApplication}\n          >\n            {localization.return}\n          </Button>\n        )}\n      </CardFooter>\n    </Card>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/accept-invitation.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/change-organization-logo.tsx",
      "content": "\"use client\"\n\nimport { fileToBase64 } from \"@better-auth-ui/core\"\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useHasPermission,\n  useUpdateOrganization\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { Trash2, Upload } from \"lucide-react\"\nimport { type ChangeEvent, useRef, useState } from \"react\"\nimport { toast } from \"sonner\"\n\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { Label } from \"@/components/ui/label\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport { OrganizationLogo } from \"./organization-logo\"\n\nexport type ChangeOrganizationLogoProps = {\n  className?: string\n}\n\nexport function ChangeOrganizationLogo({\n  className\n}: ChangeOrganizationLogoProps) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const { logo, localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n\n  const { data: activeOrganization, isPending: activeOrganizationPending } =\n    useActiveOrganization(authClient)\n  const canUpdate = useHasPermission(authClient, {\n    permissions: { organization: [\"update\"] }\n  })\n\n  const { mutate: updateOrganization, isPending: updatePending } =\n    useUpdateOrganization(authClient)\n\n  const fileInputRef = useRef<HTMLInputElement>(null)\n  const [isUploading, setIsUploading] = useState(false)\n  const [isDeleting, setIsDeleting] = useState(false)\n\n  const isPending = updatePending || isUploading || isDeleting\n\n  async function handleFileChange(e: ChangeEvent<HTMLInputElement>) {\n    const file = e.target.files?.[0]\n    if (!file || !activeOrganization || !canUpdate.data?.success) return\n\n    e.target.value = \"\"\n\n    setIsUploading(true)\n\n    try {\n      const resized =\n        (await logo.resize?.(file, logo.size, logo.extension)) || file\n\n      const image =\n        (await logo.upload?.(resized)) || (await fileToBase64(resized))\n\n      updateOrganization(\n        { data: { logo: image } },\n        {\n          onSuccess: () =>\n            toast.success(organizationLocalization.logoChangedSuccess),\n          onSettled: () => setIsUploading(false)\n        }\n      )\n    } catch (error) {\n      setIsUploading(false)\n      console.error(\"[Better Auth UI] Image operation failed\", error)\n      toast.error(localization.errors.imageUploadFailed)\n    }\n  }\n\n  async function handleDelete() {\n    if (!canUpdate.data?.success) return\n    const currentLogo = activeOrganization?.logo\n\n    updateOrganization(\n      { data: { logo: \"\" } },\n      {\n        onSuccess: async () => {\n          if (!currentLogo) {\n            toast.success(organizationLocalization.logoDeletedSuccess)\n            return\n          }\n\n          setIsDeleting(true)\n          try {\n            await logo.delete?.(currentLogo)\n            toast.success(organizationLocalization.logoDeletedSuccess)\n          } catch (error) {\n            console.error(\"[Better Auth UI] Image operation failed\", error)\n            toast.error(localization.errors.imageDeleteFailed)\n          } finally {\n            setIsDeleting(false)\n          }\n        }\n      }\n    )\n  }\n\n  if (!logo.enabled) {\n    return null\n  }\n\n  return (\n    <div className={cn(\"flex flex-col gap-1\", className)}>\n      <Label aria-disabled={!activeOrganization}>\n        {organizationLocalization.logo}\n      </Label>\n\n      <input\n        ref={fileInputRef}\n        type=\"file\"\n        accept=\"image/*\"\n        className=\"hidden\"\n        onChange={handleFileChange}\n      />\n\n      <div className=\"flex items-center gap-4\">\n        {canUpdate.data?.success ? (\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"icon\"\n            className=\"h-auto w-auto rounded-full p-0\"\n            disabled={!activeOrganization || isPending}\n            onClick={() => fileInputRef.current?.click()}\n          >\n            <OrganizationLogo\n              size=\"lg\"\n              isPending={activeOrganizationPending}\n              organization={activeOrganization}\n            />\n          </Button>\n        ) : (\n          <OrganizationLogo\n            size=\"lg\"\n            isPending={activeOrganizationPending}\n            organization={activeOrganization}\n          />\n        )}\n\n        {(canUpdate.isPending || canUpdate.data?.success) && (\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              className={cn(\n                buttonVariants({ size: \"sm\", variant: \"secondary\" })\n              )}\n              disabled={!activeOrganization || isPending || canUpdate.isPending}\n            >\n              {isPending && <Spinner />}\n\n              {organizationLocalization.changeLogo}\n            </DropdownMenuTrigger>\n\n            <DropdownMenuContent align=\"start\" className=\"min-w-fit\">\n              <DropdownMenuItem onClick={() => fileInputRef.current?.click()}>\n                <Upload className=\"text-muted-foreground\" />\n\n                {organizationLocalization.uploadLogo}\n              </DropdownMenuItem>\n\n              <DropdownMenuItem\n                disabled={!activeOrganization?.logo}\n                onClick={handleDelete}\n                variant=\"destructive\"\n              >\n                <Trash2 />\n\n                {organizationLocalization.deleteLogo}\n              </DropdownMenuItem>\n            </DropdownMenuContent>\n          </DropdownMenu>\n        )}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/change-organization-logo.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/create-organization-dialog.tsx",
      "content": "\"use client\"\n\nimport {\n  getAdditionalFieldDefaultValues,\n  getAdditionalFieldSubmitValues,\n  validateStringLength\n} from \"@better-auth-ui/core\"\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useCreateOrganization } from \"@better-auth-ui/react/plugins/organization\"\nimport { Briefcase } from \"lucide-react\"\nimport { useEffect, useRef, useState } from \"react\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport {\n  getAuthAdditionalFieldValidators,\n  isAuthFormFieldInvalid,\n  useAuthForm\n} from \"../auth-form\"\nimport { SlugField, sanitizeSlug } from \"./slug-field\"\n\n/** Props for the `CreateOrganizationDialog` component. */\nexport type CreateOrganizationDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  hideSlug?: boolean\n}\n\nexport function CreateOrganizationDialog({\n  open,\n  onOpenChange,\n  hideSlug: hideSlugProp\n}: CreateOrganizationDialogProps) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const {\n    additionalFields,\n    localization: organizationLocalization,\n    hideSlug: pluginHideSlug\n  } = useAuthPlugin(organizationPlugin)\n  const hideSlug = hideSlugProp ?? pluginHideSlug ?? false\n\n  const [slugEdited, setSlugEdited] = useState(false)\n  const submissionGeneration = useRef(0)\n  const submissionAttemptGeneration = useRef(0)\n\n  const { mutateAsync: createOrganization } = useCreateOrganization(authClient)\n\n  const form = useAuthForm({\n    defaultValues: {\n      additionalFields: getAdditionalFieldDefaultValues(additionalFields),\n      name: \"\",\n      slug: \"\"\n    },\n    onSubmit: async ({ value }) => {\n      const generation = submissionAttemptGeneration.current\n      if (generation !== submissionGeneration.current) return\n      await createOrganization({\n        ...getAdditionalFieldSubmitValues(\n          additionalFields,\n          value.additionalFields\n        ),\n        name: value.name,\n        slug: hideSlug ? undefined : value.slug\n      })\n      if (generation === submissionGeneration.current) onOpenChange(false)\n    }\n  })\n\n  useEffect(() => {\n    if (!open) {\n      submissionGeneration.current += 1\n      form.reset()\n      setSlugEdited(false)\n    }\n  }, [form, open])\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent>\n        <form.AppForm>\n          <form.AuthFormRoot\n            className=\"flex flex-col gap-6\"\n            onBeforeSubmit={() => {\n              submissionAttemptGeneration.current = submissionGeneration.current\n            }}\n          >\n            <DialogHeader>\n              <DialogTitle className=\"flex items-center gap-2\">\n                <Briefcase />\n                {organizationLocalization.createOrganization}\n              </DialogTitle>\n\n              <DialogDescription>\n                {organizationLocalization.organizationsDescription}\n              </DialogDescription>\n            </DialogHeader>\n\n            <div className=\"flex flex-col gap-4\">\n              <form.AppField\n                name=\"name\"\n                validators={{\n                  onChange: ({ value }) =>\n                    validateStringLength(value, {\n                      requiredMessage: localization.auth.fieldRequired,\n                      trim: true\n                    })\n                }}\n              >\n                {(field) => {\n                  const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n\n                  return (\n                    <Field data-invalid={isInvalid}>\n                      <FieldLabel htmlFor=\"create-organization-name\">\n                        {organizationLocalization.name}\n                      </FieldLabel>\n\n                      <Input\n                        id=\"create-organization-name\"\n                        name={field.name}\n                        autoFocus\n                        placeholder={organizationLocalization.namePlaceholder}\n                        value={field.state.value}\n                        onBlur={field.handleBlur}\n                        onChange={(event) => {\n                          const value = event.target.value\n                          field.handleChange(value)\n                          if (!slugEdited) {\n                            form.setFieldValue(\"slug\", sanitizeSlug(value))\n                          }\n                        }}\n                        aria-invalid={isInvalid}\n                      />\n\n                      <field.AuthFormFieldError />\n                    </Field>\n                  )\n                }}\n              </form.AppField>\n\n              {!hideSlug && (\n                <form.AppField name=\"slug\">\n                  {(field) => (\n                    <SlugField\n                      id=\"create-organization-slug\"\n                      value={field.state.value}\n                      onChange={(value) => {\n                        field.handleChange(value)\n                        setSlugEdited(true)\n                      }}\n                    />\n                  )}\n                </form.AppField>\n              )}\n\n              {additionalFields.map((configuredField) => (\n                <form.AppField\n                  key={configuredField.name}\n                  name={`additionalFields.${configuredField.name}`}\n                  validators={getAuthAdditionalFieldValidators(\n                    configuredField,\n                    localization.auth.fieldRequired\n                  )}\n                >\n                  {(field) => (\n                    <field.AuthFormAdditionalField\n                      field={configuredField}\n                      optionalLabel={localization.settings.optional}\n                    />\n                  )}\n                </form.AppField>\n              ))}\n            </div>\n\n            <DialogFooter>\n              <DialogClose\n                className={buttonVariants({ variant: \"outline\" })}\n                type=\"button\"\n              >\n                {localization.settings.cancel}\n              </DialogClose>\n\n              <form.AuthFormSubmitButton>\n                {organizationLocalization.createOrganization}\n              </form.AuthFormSubmitButton>\n            </DialogFooter>\n          </form.AuthFormRoot>\n        </form.AppForm>\n      </DialogContent>\n    </Dialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/create-organization-dialog.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/delete-organization-dialog.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useDeleteOrganization } from \"@better-auth-ui/react/plugins/organization\"\nimport type { Organization } from \"better-auth/client\"\nimport { TriangleAlert } from \"lucide-react\"\nimport { toast } from \"sonner\"\n\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogMedia,\n  AlertDialogTitle\n} from \"@/components/ui/alert-dialog\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { useAuthForm } from \"../auth-form\"\nimport { OrganizationView } from \"./organization-view\"\n\nexport type DeleteOrganizationDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  organization: Organization\n}\n\nexport function DeleteOrganizationDialog({\n  open,\n  onOpenChange,\n  organization\n}: DeleteOrganizationDialogProps) {\n  const { authClient, basePaths, localization, navigate } =\n    useAuth<OrganizationAuthClient>()\n  const {\n    localization: organizationLocalization,\n    viewPaths: organizationPluginViewPaths\n  } = useAuthPlugin(organizationPlugin)\n\n  const { mutateAsync: deleteOrganization, isPending } = useDeleteOrganization(\n    authClient,\n    {\n      onSuccess: () => {\n        onOpenChange(false)\n        toast.success(organizationLocalization.organizationDeleted)\n\n        navigate({\n          to: `${basePaths.settings}/${organizationPluginViewPaths.settings.organizations}`,\n          replace: true\n        })\n      }\n    }\n  )\n\n  const form = useAuthForm({\n    defaultValues: {},\n    onSubmit: async () => {\n      await deleteOrganization({ organizationId: organization.id })\n    }\n  })\n\n  return (\n    <AlertDialog open={open} onOpenChange={onOpenChange}>\n      <AlertDialogContent>\n        <form.AppForm>\n          <form.AuthFormRoot className=\"flex flex-col gap-6\">\n            <AlertDialogHeader>\n              <AlertDialogMedia className=\"bg-destructive/10 text-destructive\">\n                <TriangleAlert />\n              </AlertDialogMedia>\n\n              <AlertDialogTitle>\n                {organizationLocalization.deleteOrganization}\n              </AlertDialogTitle>\n\n              <AlertDialogDescription>\n                {organizationLocalization.deleteOrganizationDescription}\n              </AlertDialogDescription>\n            </AlertDialogHeader>\n\n            <Card>\n              <CardContent>\n                <OrganizationView organization={organization} hideRole />\n              </CardContent>\n            </Card>\n\n            <AlertDialogFooter>\n              <AlertDialogCancel disabled={isPending}>\n                {localization.settings.cancel}\n              </AlertDialogCancel>\n\n              <form.AuthFormSubmitButton\n                isPending={isPending}\n                variant=\"destructive\"\n                disabled={isPending}\n              >\n                {organizationLocalization.deleteOrganization}\n              </form.AuthFormSubmitButton>\n            </AlertDialogFooter>\n          </form.AuthFormRoot>\n        </form.AppForm>\n      </AlertDialogContent>\n    </AlertDialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/delete-organization-dialog.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/delete-organization-skeleton.tsx",
      "content": "\"use client\"\n\nimport { Skeleton } from \"@/components/ui/skeleton\"\n\n/**\n * Placeholder matching `DeleteOrganization` while the delete permission resolves.\n */\nexport function DeleteOrganizationSkeleton() {\n  return (\n    <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n      <div className=\"flex flex-col gap-1\">\n        <Skeleton className=\"h-3.5 w-40 rounded-md\" />\n        <Skeleton className=\"h-3 w-64 rounded-md\" />\n      </div>\n\n      <Skeleton className=\"h-8 w-36 shrink-0 rounded-md\" />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/delete-organization-skeleton.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/delete-organization.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useHasPermission\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { useState } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { DeleteOrganizationDialog } from \"./delete-organization-dialog\"\nimport { DeleteOrganizationSkeleton } from \"./delete-organization-skeleton\"\n\n/**\n * Danger-zone row to delete the active organization. Hidden for members without\n * the `organization:delete` permission.\n */\nexport function DeleteOrganization() {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const { localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n\n  const { data: activeOrganization } = useActiveOrganization(authClient)\n\n  const { data: permission, isPending: permissionPending } = useHasPermission(\n    authClient,\n    {\n      permissions: { organization: [\"delete\"] }\n    }\n  )\n\n  const [confirmOpen, setConfirmOpen] = useState(false)\n\n  if (permissionPending) {\n    return <DeleteOrganizationSkeleton />\n  }\n\n  if (!permission?.success) {\n    return null\n  }\n\n  return (\n    <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n      <div>\n        <p className=\"text-sm font-medium leading-tight\">\n          {organizationLocalization.deleteOrganization}\n        </p>\n\n        <p className=\"text-muted-foreground mt-0.5 text-xs\">\n          {organizationLocalization.deleteOrganizationDescription}\n        </p>\n      </div>\n\n      <Button\n        disabled={!activeOrganization}\n        size=\"sm\"\n        variant=\"outline\"\n        className=\"text-destructive\"\n        onClick={() => setConfirmOpen(true)}\n      >\n        {organizationLocalization.deleteOrganization}\n      </Button>\n\n      {activeOrganization && (\n        <DeleteOrganizationDialog\n          open={confirmOpen}\n          onOpenChange={setConfirmOpen}\n          organization={activeOrganization}\n        />\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/delete-organization.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/edit-member-roles-dialog.tsx",
      "content": "\"use client\"\n\nimport { validateMinimumItems } from \"@better-auth-ui/core\"\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { parseMemberRoles } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useUpdateMemberRole } from \"@better-auth-ui/react/plugins/organization\"\nimport { ShieldCheck } from \"lucide-react\"\nimport { useEffect } from \"react\"\nimport { toast } from \"sonner\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport {\n  Field,\n  FieldContent,\n  FieldGroup,\n  FieldLabel,\n  FieldLegend,\n  FieldSet,\n  FieldTitle\n} from \"@/components/ui/field\"\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { useAuthForm } from \"../auth-form\"\n\nexport type EditMemberRolesDialogProps = {\n  member: {\n    id: string\n    role?: string | null\n  }\n  onOpenChange: (open: boolean) => void\n  open: boolean\n  organizationId: string\n  roles: Array<[string, string]>\n  protectedRole?: string\n  protectedRoleRemovalDisabled?: boolean\n}\n\nconst selectedMemberRoles = (\n  memberRole: string | null | undefined,\n  allowMultipleRoles: boolean,\n  protectedRole?: string\n) => {\n  const parsedRoles = parseMemberRoles(memberRole)\n\n  if (allowMultipleRoles) return parsedRoles\n\n  const selectedRole =\n    protectedRole && parsedRoles.includes(protectedRole)\n      ? protectedRole\n      : parsedRoles[0]\n\n  return selectedRole ? [selectedRole] : []\n}\n\nexport function EditMemberRolesDialog({\n  member,\n  onOpenChange,\n  open,\n  organizationId,\n  roles,\n  protectedRole,\n  protectedRoleRemovalDisabled\n}: EditMemberRolesDialogProps) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const { allowMultipleRoles, localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n  const { mutateAsync: updateMemberRole, isPending } = useUpdateMemberRole(\n    authClient,\n    {\n      onSuccess: () => {\n        toast.success(organizationLocalization.memberRoleUpdated)\n        onOpenChange(false)\n      }\n    }\n  )\n  const form = useAuthForm({\n    defaultValues: {\n      roles: selectedMemberRoles(member.role, allowMultipleRoles, protectedRole)\n    },\n    onSubmit: async ({ value }) => {\n      if (value.roles.length === 0) return\n\n      try {\n        await updateMemberRole({\n          memberId: member.id,\n          organizationId,\n          role: value.roles\n        })\n      } catch {\n        // The mutation reports the error through its configured handler.\n      }\n    }\n  })\n\n  useEffect(() => {\n    if (open)\n      form.reset({\n        roles: selectedMemberRoles(\n          member.role,\n          allowMultipleRoles,\n          protectedRole\n        )\n      })\n  }, [allowMultipleRoles, form.reset, member.role, open, protectedRole])\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent>\n        <form.AppForm>\n          <form.AuthFormRoot className=\"flex flex-col gap-6\">\n            <DialogHeader>\n              <DialogTitle className=\"flex items-center gap-2\">\n                <ShieldCheck />\n                {organizationLocalization.changeMemberRole}\n              </DialogTitle>\n              <DialogDescription>\n                {organizationLocalization.changeMemberRoleDescription}\n              </DialogDescription>\n            </DialogHeader>\n\n            <form.AppField\n              name=\"roles\"\n              mode=\"array\"\n              validators={{\n                onChange: ({ value }) =>\n                  validateMinimumItems(\n                    value,\n                    1,\n                    organizationLocalization.selectAtLeastOneRole\n                  )\n              }}\n            >\n              {(field) => {\n                const selectedRoles = field.state.value\n                const protectedRoleSelected =\n                  !allowMultipleRoles &&\n                  protectedRoleRemovalDisabled &&\n                  protectedRole !== undefined &&\n                  selectedRoles.includes(protectedRole)\n                const roleOptions = roles.map(([role, label]) => {\n                  const checked = selectedRoles.includes(role)\n                  const disabled =\n                    isPending ||\n                    (allowMultipleRoles &&\n                      checked &&\n                      selectedRoles.length === 1) ||\n                    (protectedRoleSelected && role !== protectedRole) ||\n                    (role === protectedRole &&\n                      checked &&\n                      protectedRoleRemovalDisabled)\n                  const id = `member-${member.id}-role-${role}`\n\n                  return (\n                    <FieldLabel htmlFor={id} key={role}>\n                      <Field orientation=\"horizontal\" data-disabled={disabled}>\n                        <FieldContent>\n                          <FieldTitle>{label}</FieldTitle>\n                        </FieldContent>\n                        {allowMultipleRoles ? (\n                          <Checkbox\n                            checked={checked}\n                            disabled={disabled}\n                            id={id}\n                            onCheckedChange={(next) => {\n                              if (next === true && !checked) {\n                                field.pushValue(role)\n                                return\n                              }\n\n                              const index = selectedRoles.indexOf(role)\n                              if (index >= 0) field.removeValue(index)\n                            }}\n                          />\n                        ) : (\n                          <RadioGroupItem\n                            disabled={disabled}\n                            id={id}\n                            value={role}\n                          />\n                        )}\n                      </Field>\n                    </FieldLabel>\n                  )\n                })\n\n                return (\n                  <>\n                    {allowMultipleRoles ? (\n                      <FieldSet>\n                        <FieldLegend className=\"sr-only\" variant=\"label\">\n                          {organizationLocalization.changeMemberRole}\n                        </FieldLegend>\n                        <FieldGroup className=\"gap-2\">{roleOptions}</FieldGroup>\n                      </FieldSet>\n                    ) : (\n                      <RadioGroup\n                        disabled={isPending}\n                        onValueChange={(role) => field.handleChange([role])}\n                        value={selectedRoles[0] ?? \"\"}\n                      >\n                        {roleOptions}\n                      </RadioGroup>\n                    )}\n\n                    <field.AuthFormFieldError />\n\n                    <DialogFooter>\n                      <DialogClose\n                        className={buttonVariants({ variant: \"outline\" })}\n                        disabled={isPending}\n                        type=\"button\"\n                      >\n                        {localization.settings.cancel}\n                      </DialogClose>\n                      <form.AuthFormSubmitButton disabled={isPending}>\n                        {localization.settings.saveChanges}\n                      </form.AuthFormSubmitButton>\n                    </DialogFooter>\n                  </>\n                )\n              }}\n            </form.AppField>\n          </form.AuthFormRoot>\n        </form.AppForm>\n      </DialogContent>\n    </Dialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/edit-member-roles-dialog.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/invite-member-dialog.tsx",
      "content": "\"use client\"\n\nimport {\n  getAdditionalFieldDefaultValues,\n  getAdditionalFieldSubmitValues,\n  validateEmailAddress\n} from \"@better-auth-ui/core\"\nimport {\n  mergeOrganizationRoleLabels,\n  type OrganizationAuthClient,\n  type OrganizationRolesAuthClient,\n  type OrganizationTeamsAuthClient\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useHasPermission,\n  useInviteMember,\n  useListOrganizationInvitations,\n  useListRoles,\n  useListTeams\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { ChevronDown, UserPlus } from \"lucide-react\"\nimport { useEffect, useMemo, useRef } from \"react\"\nimport { toast } from \"sonner\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectTrigger,\n  SelectValue\n} from \"@/components/ui/select\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport {\n  getAuthAdditionalFieldValidators,\n  isAuthFormFieldInvalid,\n  useAuthForm\n} from \"../auth-form\"\n\n/** Props for the `InviteMemberDialog` component. */\nexport type InviteMemberDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n}\n\nconst pickDefaultRole = (keys: string[]) =>\n  keys.includes(\"member\") ? \"member\" : (keys.at(-1) ?? \"\")\n\n/**\n * Render a dialog for inviting a member to the organization.\n */\nexport function InviteMemberDialog({\n  open,\n  onOpenChange\n}: InviteMemberDialogProps) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const {\n    allowMultipleRoles,\n    modelFields: { invitation: invitationFields },\n    dynamicAccessControl,\n    invitationLimit,\n    localization: organizationLocalization,\n    roles,\n    teams: teamsEnabled\n  } = useAuthPlugin(organizationPlugin)\n  const { data: activeOrganization } = useActiveOrganization(authClient)\n  const teams = useListTeams(authClient as OrganizationTeamsAuthClient, {\n    query: { organizationId: activeOrganization?.id },\n    enabled: teamsEnabled\n  })\n  const invitations = useListOrganizationInvitations(authClient)\n  const canInvite = useHasPermission(authClient, {\n    organizationId: activeOrganization?.id,\n    permissions: { invitation: [\"create\"] }\n  })\n  const canReadRoles = useHasPermission(authClient, {\n    organizationId: activeOrganization?.id,\n    permissions: { ac: [\"read\"] }\n  })\n  const dynamicRoles = useListRoles(authClient as OrganizationRolesAuthClient, {\n    query: { organizationId: activeOrganization?.id },\n    enabled:\n      dynamicAccessControl?.enabled === true &&\n      canReadRoles.data?.success === true\n  })\n  const assignableRoles = useMemo(\n    () => mergeOrganizationRoleLabels(roles, dynamicRoles.data),\n    [dynamicRoles.data, roles]\n  )\n\n  const activeOrganizationId = activeOrganization?.id\n  const previousOrganizationId = useRef(activeOrganizationId)\n\n  const { mutateAsync: inviteMember, isPending: isInviting } = useInviteMember(\n    authClient as OrganizationTeamsAuthClient,\n    {\n      onSuccess: () => {\n        onOpenChange(false)\n        toast.success(organizationLocalization.inviteMemberSuccess)\n      }\n    }\n  )\n\n  const atInvitationLimit =\n    invitationLimit !== undefined &&\n    (invitations.data?.filter((invitation) => invitation.status === \"pending\")\n      .length ?? 0) >= invitationLimit\n\n  const form = useAuthForm({\n    defaultValues: {\n      additionalFields: getAdditionalFieldDefaultValues(invitationFields),\n      email: \"\",\n      roles: [] as string[],\n      teamId: \"\"\n    },\n    onSubmit: async ({ value }) => {\n      if (\n        !activeOrganizationId ||\n        !canInvite.data?.success ||\n        value.roles.length === 0 ||\n        atInvitationLimit\n      )\n        return\n\n      const teamId = teams.data?.some((team) => team.id === value.teamId)\n        ? value.teamId\n        : undefined\n\n      try {\n        await inviteMember({\n          ...getAdditionalFieldSubmitValues(\n            invitationFields,\n            value.additionalFields\n          ),\n          email: value.email.trim(),\n          organizationId: activeOrganizationId,\n          role: value.roles as Parameters<typeof inviteMember>[0][\"role\"],\n          teamId\n        })\n      } catch {\n        // The mutation reports the error through its configured handler.\n      }\n    }\n  })\n\n  useEffect(() => {\n    const keys = Object.keys(assignableRoles)\n    const current = form.getFieldValue(\"roles\")\n    const kept = current.filter((entry) => keys.includes(entry))\n    const roles =\n      kept.length > 0\n        ? allowMultipleRoles\n          ? kept\n          : kept.slice(0, 1)\n        : (() => {\n            const fallback = pickDefaultRole(keys)\n            return fallback ? [fallback] : []\n          })()\n\n    form.setFieldValue(\"roles\", roles)\n  }, [allowMultipleRoles, assignableRoles, form])\n\n  useEffect(() => {\n    const organizationChanged =\n      previousOrganizationId.current !== activeOrganizationId\n\n    if (open || organizationChanged) {\n      const fallback = pickDefaultRole(Object.keys(assignableRoles))\n      form.reset({\n        additionalFields: getAdditionalFieldDefaultValues(invitationFields),\n        email: \"\",\n        roles: fallback ? [fallback] : [],\n        teamId: \"\"\n      })\n    }\n    previousOrganizationId.current = activeOrganizationId\n  }, [activeOrganizationId, assignableRoles, form, invitationFields, open])\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent>\n        <form.AppForm>\n          <form.AuthFormRoot className=\"flex flex-col gap-6\">\n            <DialogHeader>\n              <DialogTitle className=\"flex items-center gap-2\">\n                <UserPlus />\n                {organizationLocalization.inviteMember}\n              </DialogTitle>\n\n              <DialogDescription>\n                {organizationLocalization.inviteMemberDescription}\n              </DialogDescription>\n            </DialogHeader>\n\n            <div className=\"flex flex-col gap-4\">\n              <form.AppField\n                name=\"email\"\n                validators={{\n                  onChange: ({ value }) =>\n                    validateEmailAddress(value, {\n                      invalidMessage: localization.auth.invalidEmail,\n                      requiredMessage: localization.auth.fieldRequired\n                    })\n                }}\n              >\n                {(field) => {\n                  const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n\n                  return (\n                    <Field data-invalid={isInvalid}>\n                      <FieldLabel htmlFor=\"invite-member-email\">\n                        {localization.auth.email}\n                      </FieldLabel>\n                      <Input\n                        id=\"invite-member-email\"\n                        name={field.name}\n                        type=\"email\"\n                        autoFocus\n                        placeholder={localization.auth.email}\n                        disabled={isInviting}\n                        value={field.state.value}\n                        onBlur={field.handleBlur}\n                        onChange={(event) =>\n                          field.handleChange(event.target.value)\n                        }\n                        aria-invalid={isInvalid}\n                      />\n                      <field.AuthFormFieldError />\n                    </Field>\n                  )\n                }}\n              </form.AppField>\n\n              <form.AppField\n                name=\"roles\"\n                validators={{\n                  onChange: ({ value }) =>\n                    value.length > 0\n                      ? undefined\n                      : localization.auth.fieldRequired\n                }}\n              >\n                {(field) => {\n                  const selectedRoles = field.state.value\n                  const roleSummary = selectedRoles\n                    .map((entry) => assignableRoles[entry] ?? entry)\n                    .join(\", \")\n                  const toggleRole = (role: string) =>\n                    field.handleChange(\n                      selectedRoles.includes(role)\n                        ? selectedRoles.filter((entry) => entry !== role)\n                        : [...selectedRoles, role]\n                    )\n\n                  return (\n                    <Field\n                      data-invalid={isAuthFormFieldInvalid(field.state.meta)}\n                    >\n                      <FieldLabel htmlFor=\"invite-member-role\">\n                        {organizationLocalization.role}\n                      </FieldLabel>\n                      {allowMultipleRoles ? (\n                        <DropdownMenu>\n                          <DropdownMenuTrigger\n                            id=\"invite-member-role\"\n                            disabled={isInviting}\n                            className={cn(\n                              buttonVariants({ variant: \"outline\" }),\n                              \"w-full justify-between font-normal\"\n                            )}\n                          >\n                            <span\n                              className={cn(\n                                !roleSummary && \"text-muted-foreground\"\n                              )}\n                            >\n                              {roleSummary ||\n                                organizationLocalization.selectRoles}\n                            </span>\n                            <ChevronDown className=\"opacity-50\" />\n                          </DropdownMenuTrigger>\n\n                          <DropdownMenuContent\n                            align=\"start\"\n                            className=\"w-(--radix-dropdown-menu-trigger-width)\"\n                          >\n                            {Object.entries(assignableRoles).map(\n                              ([key, label]) => {\n                                const checked = selectedRoles.includes(key)\n\n                                return (\n                                  <DropdownMenuCheckboxItem\n                                    key={key}\n                                    checked={checked}\n                                    disabled={\n                                      checked && selectedRoles.length === 1\n                                    }\n                                    onSelect={(event) => {\n                                      event.preventDefault()\n                                      toggleRole(key)\n                                    }}\n                                  >\n                                    {label}\n                                  </DropdownMenuCheckboxItem>\n                                )\n                              }\n                            )}\n                          </DropdownMenuContent>\n                        </DropdownMenu>\n                      ) : (\n                        <Select\n                          disabled={isInviting}\n                          onValueChange={(role) => field.handleChange([role])}\n                          value={selectedRoles[0] ?? \"\"}\n                        >\n                          <SelectTrigger\n                            id=\"invite-member-role\"\n                            className=\"w-full\"\n                          >\n                            <SelectValue\n                              placeholder={organizationLocalization.selectRoles}\n                            />\n                          </SelectTrigger>\n                          <SelectContent>\n                            <SelectGroup>\n                              {Object.entries(assignableRoles).map(\n                                ([key, label]) => (\n                                  <SelectItem key={key} value={key}>\n                                    {label}\n                                  </SelectItem>\n                                )\n                              )}\n                            </SelectGroup>\n                          </SelectContent>\n                        </Select>\n                      )}\n                      <field.AuthFormFieldError />\n                    </Field>\n                  )\n                }}\n              </form.AppField>\n\n              {teamsEnabled && (\n                <form.AppField name=\"teamId\">\n                  {(field) => (\n                    <Field>\n                      <FieldLabel htmlFor=\"invite-member-team\">\n                        {organizationLocalization.team}\n                      </FieldLabel>\n                      <Select\n                        value={field.state.value}\n                        onValueChange={(value) =>\n                          field.handleChange(value ?? \"\")\n                        }\n                        disabled={isInviting}\n                      >\n                        <SelectTrigger\n                          id=\"invite-member-team\"\n                          className=\"w-full\"\n                        >\n                          <SelectValue\n                            placeholder={organizationLocalization.selectTeam}\n                          />\n                        </SelectTrigger>\n                        <SelectContent>\n                          {teams.data?.map((team) => (\n                            <SelectItem key={team.id} value={team.id}>\n                              {team.name}\n                            </SelectItem>\n                          ))}\n                        </SelectContent>\n                      </Select>\n                    </Field>\n                  )}\n                </form.AppField>\n              )}\n\n              {invitationFields.map((configuredField) => (\n                <form.AppField\n                  key={configuredField.name}\n                  name={`additionalFields.${configuredField.name}`}\n                  validators={getAuthAdditionalFieldValidators(\n                    configuredField,\n                    localization.auth.fieldRequired\n                  )}\n                >\n                  {(field) => (\n                    <field.AuthFormAdditionalField\n                      field={configuredField}\n                      isPending={isInviting}\n                      optionalLabel={localization.settings.optional}\n                    />\n                  )}\n                </form.AppField>\n              ))}\n            </div>\n\n            <DialogFooter>\n              <DialogClose\n                className={buttonVariants({ variant: \"outline\" })}\n                disabled={isInviting}\n                type=\"button\"\n              >\n                {localization.settings.cancel}\n              </DialogClose>\n\n              <form.AuthFormSubmitButton\n                disabled={\n                  isInviting ||\n                  atInvitationLimit ||\n                  canInvite.isPending ||\n                  !canInvite.data?.success\n                }\n              >\n                {organizationLocalization.inviteMember}\n              </form.AuthFormSubmitButton>\n            </DialogFooter>\n          </form.AuthFormRoot>\n        </form.AppForm>\n      </DialogContent>\n    </Dialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/invite-member-dialog.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/leave-organization-dialog.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useLeaveOrganization } from \"@better-auth-ui/react/plugins/organization\"\nimport type { Organization } from \"better-auth/client\"\nimport { LogOut } from \"lucide-react\"\nimport { toast } from \"sonner\"\n\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogMedia,\n  AlertDialogTitle\n} from \"@/components/ui/alert-dialog\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { OrganizationView } from \"./organization-view\"\n\nexport type LeaveOrganizationDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  organization: Organization\n}\n\nexport function LeaveOrganizationDialog({\n  open,\n  onOpenChange,\n  organization\n}: LeaveOrganizationDialogProps) {\n  const { authClient, basePaths, localization, navigate } =\n    useAuth<OrganizationAuthClient>()\n  const {\n    localization: organizationLocalization,\n    viewPaths: organizationPluginViewPaths\n  } = useAuthPlugin(organizationPlugin)\n\n  const { mutate: leaveOrganization, isPending } = useLeaveOrganization(\n    authClient,\n    {\n      onSuccess: () => {\n        onOpenChange(false)\n        toast.success(organizationLocalization.leftOrganization)\n\n        navigate({\n          to: `${basePaths.settings}/${organizationPluginViewPaths.settings.organizations}`,\n          replace: true\n        })\n      }\n    }\n  )\n\n  return (\n    <AlertDialog open={open} onOpenChange={onOpenChange}>\n      <AlertDialogContent>\n        <AlertDialogHeader>\n          <AlertDialogMedia className=\"bg-destructive/10 text-destructive\">\n            <LogOut />\n          </AlertDialogMedia>\n\n          <AlertDialogTitle>\n            {organizationLocalization.leaveOrganization}\n          </AlertDialogTitle>\n\n          <AlertDialogDescription>\n            {organizationLocalization.leaveOrganizationDescription}\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n\n        <Card>\n          <CardContent>\n            <OrganizationView organization={organization} hideRole />\n          </CardContent>\n        </Card>\n\n        <AlertDialogFooter>\n          <AlertDialogCancel disabled={isPending}>\n            {localization.settings.cancel}\n          </AlertDialogCancel>\n\n          <Button\n            variant=\"destructive\"\n            disabled={isPending}\n            onClick={() =>\n              leaveOrganization({ organizationId: organization.id })\n            }\n          >\n            {isPending && <Spinner />}\n\n            {organizationLocalization.leaveOrganization}\n          </Button>\n        </AlertDialogFooter>\n      </AlertDialogContent>\n    </AlertDialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/leave-organization-dialog.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/leave-organization.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useActiveOrganization } from \"@better-auth-ui/react/plugins/organization\"\nimport { useState } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { LeaveOrganizationDialog } from \"./leave-organization-dialog\"\n\n/**\n * Danger-zone row to leave the active organization.\n */\nexport function LeaveOrganization() {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const { localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n\n  const { data: activeOrganization } = useActiveOrganization(authClient)\n\n  const [confirmOpen, setConfirmOpen] = useState(false)\n\n  return (\n    <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n      <div>\n        <p className=\"text-sm font-medium leading-tight\">\n          {organizationLocalization.leaveOrganization}\n        </p>\n\n        <p className=\"text-muted-foreground mt-0.5 text-xs\">\n          {organizationLocalization.leaveOrganizationDescription}\n        </p>\n      </div>\n\n      <Button\n        disabled={!activeOrganization}\n        size=\"sm\"\n        variant=\"outline\"\n        className=\"text-destructive\"\n        onClick={() => setConfirmOpen(true)}\n      >\n        {organizationLocalization.leaveOrganization}\n      </Button>\n\n      {activeOrganization && (\n        <LeaveOrganizationDialog\n          open={confirmOpen}\n          onOpenChange={setConfirmOpen}\n          organization={activeOrganization}\n        />\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/leave-organization.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-danger-zone.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth } from \"@better-auth-ui/react\"\nimport { useHasPermission } from \"@better-auth-ui/react/plugins/organization\"\nimport type { ComponentProps } from \"react\"\n\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Separator } from \"@/components/ui/separator\"\nimport { cn } from \"cn\"\nimport { DeleteOrganization } from \"./delete-organization\"\nimport { DeleteOrganizationSkeleton } from \"./delete-organization-skeleton\"\nimport { LeaveOrganization } from \"./leave-organization\"\n\nexport type OrganizationDangerZoneProps = {\n  className?: string\n}\n\n/**\n * Danger zone heading with `LeaveOrganization` and `DeleteOrganization`\n * for the active organization in a single card.\n *\n * Resolves the `organization:delete` permission before rendering anything to\n * avoid flashing `LeaveOrganization` (and a stray separator) before the\n * delete row appears or disappears.\n */\nexport function OrganizationDangerZone({\n  className,\n  ...props\n}: OrganizationDangerZoneProps & ComponentProps<\"div\">) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n\n  const { data: deletePermission, isPending: deletePermissionPending } =\n    useHasPermission(authClient, {\n      permissions: { organization: [\"delete\"] }\n    })\n\n  const canDelete = !!deletePermission?.success\n\n  return (\n    <div className={cn(\"flex w-full flex-col\", className)} {...props}>\n      <h2 className=\"mb-3 text-sm font-semibold text-destructive\">\n        {localization.settings.dangerZone}\n      </h2>\n\n      <Card>\n        <CardContent>\n          {deletePermissionPending ? (\n            <DeleteOrganizationSkeleton />\n          ) : (\n            <>\n              <LeaveOrganization />\n\n              {canDelete && (\n                <>\n                  <Separator className=\"my-4\" />\n\n                  <DeleteOrganization />\n                </>\n              )}\n            </>\n          )}\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-danger-zone.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-invitation-row-skeleton.tsx",
      "content": "\"use client\"\n\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { TableCell, TableRow } from \"@/components/ui/table\"\n\n/**\n * Placeholder row matching `OrganizationInvitationRow` while invitations load.\n */\nexport function OrganizationInvitationRowSkeleton() {\n  return (\n    <TableRow>\n      <TableCell>\n        <Skeleton className=\"h-4 w-48 rounded-md\" />\n      </TableCell>\n\n      <TableCell>\n        <Skeleton className=\"h-4 w-36 rounded-md\" />\n      </TableCell>\n\n      <TableCell>\n        <Skeleton className=\"h-4 w-16 rounded-md\" />\n      </TableCell>\n\n      <TableCell>\n        <Skeleton className=\"h-4 w-14 rounded-full\" />\n      </TableCell>\n\n      <TableCell />\n    </TableRow>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-invitation-row-skeleton.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-invitation-row.tsx",
      "content": "\"use client\"\n\nimport { formatAdditionalFieldValue } from \"@better-auth-ui/core\"\nimport {\n  memberRoleLabels,\n  type OrganizationAuthClient\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useCancelInvitation,\n  useHasPermission,\n  useInviteMember\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Invitation } from \"better-auth/client\"\nimport { Send, X } from \"lucide-react\"\nimport { toast } from \"sonner\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { TableCell, TableRow } from \"@/components/ui/table\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport { OrganizationInvitationRowSkeleton } from \"./organization-invitation-row-skeleton\"\nimport {\n  type OrganizationSelectableRow,\n  OrganizationTableSelectRow\n} from \"./organization-table-selection\"\n\nexport type OrganizationInvitationRowProps = {\n  invitation: Invitation\n  selectableRow?: OrganizationSelectableRow<Invitation>\n  showCreatedAt?: boolean\n  showEmail?: boolean\n  showRole?: boolean\n  showStatus?: boolean\n}\n\nconst statusBadgeClasses: Record<string, string> = {\n  pending: \"bg-amber-500/10 text-amber-600 dark:text-amber-400\",\n  accepted: \"bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\",\n  rejected: \"bg-destructive/10 text-destructive\",\n  canceled: \"bg-muted text-muted-foreground\"\n}\n\nexport function OrganizationInvitationRow({\n  invitation,\n  selectableRow,\n  showCreatedAt = true,\n  showEmail = true,\n  showRole = true,\n  showStatus = true\n}: OrganizationInvitationRowProps) {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const {\n    modelFields: { invitation: invitationFields },\n    localization: organizationLocalization,\n    roles\n  } = useAuthPlugin(organizationPlugin)\n\n  const {\n    data: cancelInvitationPermission,\n    isPending: cancelPermissionPending\n  } = useHasPermission(authClient, {\n    permissions: { invitation: [\"cancel\"] }\n  })\n\n  const { mutate: cancelInvitation, isPending: cancelPending } =\n    useCancelInvitation(authClient)\n\n  const { data: inviteMemberPermission, isPending: invitePermissionPending } =\n    useHasPermission(authClient, {\n      permissions: { invitation: [\"create\"] }\n    })\n\n  // Better Auth treats a re-invite as a resend: it extends the existing\n  // invitation's expiry and sends the email again rather than creating a\n  // second row.\n  const { mutate: resendInvitation, isPending: resendPending } =\n    useInviteMember(authClient, {\n      onSuccess: () => toast.success(organizationLocalization.invitationResent)\n    })\n\n  const roleLabel = memberRoleLabels(invitation.role, roles).join(\", \")\n\n  const statusLabel =\n    organizationLocalization[\n      invitation.status as keyof typeof organizationLocalization\n    ] ?? invitation.status\n\n  if (cancelPermissionPending || invitePermissionPending) {\n    return <OrganizationInvitationRowSkeleton />\n  }\n\n  const isPending = invitation.status === \"pending\"\n\n  return (\n    <TableRow\n      data-state={selectableRow?.getIsSelected() ? \"selected\" : undefined}\n    >\n      {selectableRow && (\n        <TableCell>\n          <OrganizationTableSelectRow\n            localization={organizationLocalization}\n            row={selectableRow}\n          />\n        </TableCell>\n      )}\n\n      {showEmail && (\n        <TableCell>\n          <div className=\"flex flex-col gap-1\">\n            <span className=\"font-medium text-sm\">{invitation.email}</span>\n            {invitationFields.map((field) => {\n              const value = formatAdditionalFieldValue(\n                (invitation as unknown as Record<string, unknown>)[field.name]\n              )\n              return value ? (\n                <span\n                  className=\"text-xs text-muted-foreground\"\n                  key={field.name}\n                >\n                  {field.label}: {value}\n                </span>\n              ) : null\n            })}\n          </div>\n        </TableCell>\n      )}\n\n      {showCreatedAt && (\n        <TableCell className=\"text-muted-foreground text-xs tabular-nums whitespace-nowrap\">\n          {new Date(invitation.createdAt).toLocaleString(undefined, {\n            dateStyle: \"short\",\n            timeStyle: \"short\"\n          })}\n        </TableCell>\n      )}\n\n      {showRole && <TableCell className=\"text-sm\">{roleLabel}</TableCell>}\n\n      {showStatus && (\n        <TableCell className=\"text-sm\">\n          <Badge\n            variant=\"secondary\"\n            className={cn(statusBadgeClasses[invitation.status])}\n          >\n            {String(statusLabel)}\n          </Badge>\n        </TableCell>\n      )}\n\n      <TableCell className=\"text-end\">\n        <div className=\"flex justify-end gap-2\">\n          {inviteMemberPermission?.success && isPending && (\n            <Button\n              size=\"icon\"\n              variant=\"outline\"\n              className=\"size-8\"\n              disabled={resendPending}\n              onClick={() =>\n                resendInvitation({\n                  ...Object.fromEntries(\n                    invitationFields.flatMap((field) => {\n                      const value = (\n                        invitation as unknown as Record<string, unknown>\n                      )[field.name]\n                      return value === undefined ? [] : [[field.name, value]]\n                    })\n                  ),\n                  email: invitation.email,\n                  organizationId: invitation.organizationId,\n                  role: invitation.role as Parameters<\n                    typeof resendInvitation\n                  >[0][\"role\"],\n                  resend: true\n                })\n              }\n              aria-label={organizationLocalization.resendInvitation}\n            >\n              {resendPending ? <Spinner /> : <Send />}\n            </Button>\n          )}\n\n          {cancelInvitationPermission?.success && isPending && (\n            <Button\n              size=\"icon\"\n              variant=\"outline\"\n              className=\"size-8 text-destructive\"\n              disabled={cancelPending}\n              onClick={() => cancelInvitation({ invitationId: invitation.id })}\n              aria-label={organizationLocalization.cancelInvitation}\n            >\n              {cancelPending ? <Spinner /> : <X />}\n            </Button>\n          )}\n        </div>\n      </TableCell>\n    </TableRow>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-invitation-row.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-invitations-empty.tsx",
      "content": "\"use client\"\n\nimport { useAuthPlugin } from \"@better-auth-ui/react\"\nimport { Send } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Empty,\n  EmptyContent,\n  EmptyDescription,\n  EmptyHeader,\n  EmptyMedia,\n  EmptyTitle\n} from \"@/components/ui/empty\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\n\nexport type OrganizationInvitationsEmptyProps = {\n  isInvitePending?: boolean\n  onInvitePress?: () => void\n}\n\n/**\n * Empty state for `OrganizationInvitations`.\n */\nexport function OrganizationInvitationsEmpty({\n  isInvitePending,\n  onInvitePress\n}: OrganizationInvitationsEmptyProps) {\n  const { localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n\n  return (\n    <Empty>\n      <EmptyHeader>\n        <EmptyMedia variant=\"icon\">\n          <Send />\n        </EmptyMedia>\n        <EmptyTitle>{organizationLocalization.noInvitations}</EmptyTitle>\n        <EmptyDescription>\n          {organizationLocalization.organizationInvitationsEmptyDescription}\n        </EmptyDescription>\n      </EmptyHeader>\n      {(isInvitePending || onInvitePress) && (\n        <EmptyContent>\n          <Button disabled={isInvitePending} size=\"sm\" onClick={onInvitePress}>\n            {organizationLocalization.inviteMember}\n          </Button>\n        </EmptyContent>\n      )}\n    </Empty>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-invitations-empty.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-invitations.tsx",
      "content": "\"use client\"\n\nimport {\n  hasMemberRole,\n  type OrganizationAuthClient,\n  type OrganizationLocalization\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useCancelInvitation,\n  useHasPermission,\n  useListOrganizationInvitations\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Invitation } from \"better-auth/client\"\nimport { Filter, Search, X } from \"lucide-react\"\nimport { type ComponentProps, useState } from \"react\"\nimport { toast } from \"sonner\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { Card } from \"@/components/ui/card\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput\n} from \"@/components/ui/input-group\"\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow\n} from \"@/components/ui/table\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport { InviteMemberDialog } from \"./invite-member-dialog\"\nimport { OrganizationInvitationRow } from \"./organization-invitation-row\"\nimport { OrganizationInvitationRowSkeleton } from \"./organization-invitation-row-skeleton\"\nimport { OrganizationInvitationsEmpty } from \"./organization-invitations-empty\"\nimport { OrganizationSortableTableHead } from \"./organization-sortable-table-head\"\nimport {\n  createOrganizationColumnHelper,\n  ORGANIZATION_TABLE_PAGE_SIZE,\n  useOrganizationTable\n} from \"./organization-table\"\nimport { OrganizationTableBulkAction } from \"./organization-table-bulk-action\"\nimport { OrganizationTablePagination } from \"./organization-table-pagination\"\nimport { OrganizationTableSelectAll } from \"./organization-table-selection\"\nimport { useOrganizationTableState } from \"./organization-table-state\"\nimport { OrganizationTableViewOptions } from \"./organization-table-view-options\"\n\nconst invitationColumnHelper = createOrganizationColumnHelper<Invitation>()\nconst invitationColumns = invitationColumnHelper.columns([\n  invitationColumnHelper.accessor(\"email\", {\n    enableHiding: false,\n    filterFn: \"includesString\"\n  }),\n  invitationColumnHelper.accessor(\n    (invitation) => new Date(invitation.createdAt).getTime(),\n    { id: \"createdAt\", enableGlobalFilter: false }\n  ),\n  invitationColumnHelper.accessor(\"role\", {\n    enableGlobalFilter: false,\n    filterFn: (row, columnId, value) =>\n      hasMemberRole(row.getValue<string>(columnId), String(value))\n  }),\n  invitationColumnHelper.accessor(\"status\", {\n    enableGlobalFilter: false,\n    filterFn: (row, columnId, value) => row.getValue(columnId) === String(value)\n  })\n])\nconst INVITATION_COLUMN_IDS = [\"email\", \"createdAt\", \"role\", \"status\"] as const\nconst EMPTY_INVITATIONS: Invitation[] = []\n\n/** Props for the `OrganizationInvitations` component. */\nexport type OrganizationInvitationsProps = {\n  className?: string\n}\n\n/**\n * Organization invitations table with invite control and per-row actions.\n */\nexport function OrganizationInvitations({\n  className,\n  ...props\n}: OrganizationInvitationsProps & ComponentProps<\"div\">) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const { localization: organizationLocalization, roles } =\n    useAuthPlugin(organizationPlugin)\n  const { data: invitations, isPending: invitationsPending } =\n    useListOrganizationInvitations(authClient)\n\n  const canInvite = useHasPermission(authClient, {\n    permissions: { invitation: [\"create\"] }\n  })\n  const canCancel = useHasPermission(authClient, {\n    permissions: { invitation: [\"cancel\"] }\n  })\n\n  const isPending = invitationsPending || canCancel.isPending\n  const tableState = useOrganizationTableState(\n    \"organizationInvitations\",\n    ORGANIZATION_TABLE_PAGE_SIZE,\n    INVITATION_COLUMN_IDS\n  )\n  const { globalFilter, pagination } = tableState\n\n  const table = useOrganizationTable(\n    {\n      atoms: tableState.atoms,\n      columns: invitationColumns,\n      data: invitations ?? EMPTY_INVITATIONS,\n      enableRowSelection: (row) =>\n        canCancel.data?.success === true && row.original.status === \"pending\",\n      globalFilterFn: \"includesString\",\n      getRowId: (invitation) => invitation.id\n    },\n    () => null\n  )\n\n  const cancelInvitations = useCancelInvitation(authClient)\n  const roleFilter = String(table.getColumn(\"role\")?.getFilterValue() ?? \"all\")\n  const statusFilter = String(\n    table.getColumn(\"status\")?.getFilterValue() ?? \"all\"\n  )\n  const roleFacetRows = table.getColumn(\"role\")?.getFacetedRowModel().flatRows\n  const statusCounts = table.getColumn(\"status\")?.getFacetedUniqueValues()\n  const selectedInvitations = table.getSelectedRowModel().rows\n  const showSelection = canCancel.data?.success === true\n  const visibleColumnCount = table.getVisibleLeafColumns().length\n\n  async function cancelSelectedInvitations() {\n    const results = await Promise.allSettled(\n      selectedInvitations.map((row) =>\n        cancelInvitations.mutateAsync({ invitationId: row.original.id })\n      )\n    )\n    const canceledCount = results.filter(\n      (result) => result.status === \"fulfilled\"\n    ).length\n\n    if (canceledCount > 0) {\n      toast.success(\n        organizationLocalization.invitationsCanceled.replace(\n          \"{{count}}\",\n          String(canceledCount)\n        )\n      )\n    }\n    table.resetRowSelection(true)\n  }\n\n  const [inviteOpen, setInviteOpen] = useState(false)\n\n  return (\n    <div className={cn(\"flex flex-col gap-3\", className)} {...props}>\n      <h3 className=\"truncate text-sm font-semibold\">\n        {organizationLocalization.invitations}\n      </h3>\n\n      <div className=\"flex flex-col gap-4\">\n        <div className=\"flex flex-wrap items-center gap-3\">\n          <InputGroup className=\"min-w-0 sm:w-[220px]\">\n            <InputGroupInput\n              type=\"search\"\n              value={globalFilter}\n              onChange={(event) => table.setGlobalFilter(event.target.value)}\n              aria-label={organizationLocalization.search}\n              placeholder={organizationLocalization.search}\n              disabled={isPending}\n            />\n\n            <InputGroupAddon>\n              <Search className=\"text-muted-foreground\" />\n            </InputGroupAddon>\n          </InputGroup>\n\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              className={cn(buttonVariants({ size: \"sm\", variant: \"outline\" }))}\n              disabled={isPending}\n            >\n              <Filter />\n\n              {organizationLocalization.role}\n            </DropdownMenuTrigger>\n\n            <DropdownMenuContent align=\"start\">\n              <DropdownMenuRadioGroup\n                value={roleFilter}\n                onValueChange={(value) =>\n                  table\n                    .getColumn(\"role\")\n                    ?.setFilterValue(value === \"all\" ? undefined : value)\n                }\n              >\n                <DropdownMenuRadioItem value=\"all\">\n                  {organizationLocalization.all}\n                </DropdownMenuRadioItem>\n\n                {Object.entries(roles).map(([key, label]) => (\n                  <DropdownMenuRadioItem key={key} value={key}>\n                    {label} (\n                    {roleFacetRows?.filter((row) =>\n                      hasMemberRole(row.original.role, key)\n                    ).length ?? 0}\n                    )\n                  </DropdownMenuRadioItem>\n                ))}\n              </DropdownMenuRadioGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              className={cn(buttonVariants({ size: \"sm\", variant: \"outline\" }))}\n              disabled={isPending}\n            >\n              <Filter />\n\n              {organizationLocalization.status}\n            </DropdownMenuTrigger>\n\n            <DropdownMenuContent align=\"start\">\n              <DropdownMenuRadioGroup\n                value={statusFilter}\n                onValueChange={(value) =>\n                  table\n                    .getColumn(\"status\")\n                    ?.setFilterValue(value === \"all\" ? undefined : value)\n                }\n              >\n                <DropdownMenuRadioItem value=\"all\">\n                  {organizationLocalization.all}\n                </DropdownMenuRadioItem>\n\n                {([\"pending\", \"accepted\", \"rejected\", \"canceled\"] as const).map(\n                  (status) => (\n                    <DropdownMenuRadioItem key={status} value={status}>\n                      {organizationLocalization[\n                        status as keyof OrganizationLocalization\n                      ] ?? status}{\" \"}\n                      ({statusCounts?.get(status) ?? 0})\n                    </DropdownMenuRadioItem>\n                  )\n                )}\n              </DropdownMenuRadioGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n\n          <div className=\"ms-auto\">\n            <OrganizationTableViewOptions\n              columns={[\n                {\n                  id: \"createdAt\",\n                  label: organizationLocalization.invitedAt,\n                  visible: table.getColumn(\"createdAt\")?.getIsVisible() ?? true,\n                  onVisibleChange: (visible) =>\n                    table.getColumn(\"createdAt\")?.toggleVisibility(visible)\n                },\n                {\n                  id: \"role\",\n                  label: organizationLocalization.role,\n                  visible: table.getColumn(\"role\")?.getIsVisible() ?? true,\n                  onVisibleChange: (visible) =>\n                    table.getColumn(\"role\")?.toggleVisibility(visible)\n                },\n                {\n                  id: \"status\",\n                  label: organizationLocalization.status,\n                  visible: table.getColumn(\"status\")?.getIsVisible() ?? true,\n                  onVisibleChange: (visible) =>\n                    table.getColumn(\"status\")?.toggleVisibility(visible)\n                }\n              ]}\n              disabled={isPending}\n              localization={organizationLocalization}\n            />\n          </div>\n        </div>\n\n        {(roleFilter !== \"all\" || statusFilter !== \"all\") && (\n          <div className=\"flex flex-wrap gap-2\">\n            {roleFilter !== \"all\" && (\n              <Badge variant=\"secondary\" className=\"gap-1\">\n                {organizationLocalization.role}:{\" \"}\n                <span className=\"capitalize\">\n                  {roles?.[roleFilter] ?? roleFilter}\n                </span>\n                <Button\n                  aria-label={organizationLocalization.clear}\n                  className=\"size-4 rounded-sm text-muted-foreground\"\n                  onClick={() =>\n                    table.getColumn(\"role\")?.setFilterValue(undefined)\n                  }\n                  size=\"icon-xs\"\n                  type=\"button\"\n                  variant=\"ghost\"\n                >\n                  <X className=\"size-3\" />\n                </Button>\n              </Badge>\n            )}\n\n            {statusFilter !== \"all\" && (\n              <Badge variant=\"secondary\" className=\"gap-1\">\n                {organizationLocalization.status}:{\" \"}\n                {organizationLocalization[\n                  statusFilter as keyof OrganizationLocalization\n                ] ?? statusFilter}\n                <Button\n                  aria-label={organizationLocalization.clear}\n                  className=\"size-4 rounded-sm text-muted-foreground\"\n                  onClick={() =>\n                    table.getColumn(\"status\")?.setFilterValue(undefined)\n                  }\n                  size=\"icon-xs\"\n                  type=\"button\"\n                  variant=\"ghost\"\n                >\n                  <X className=\"size-3\" />\n                </Button>\n              </Badge>\n            )}\n          </div>\n        )}\n\n        {selectedInvitations.length > 0 && (\n          <OrganizationTableBulkAction\n            actionLabel={organizationLocalization.cancelSelectedInvitations}\n            cancelLabel={localization.settings.cancel}\n            count={selectedInvitations.length}\n            description={\n              organizationLocalization.cancelSelectedInvitationsDescription\n            }\n            isPending={cancelInvitations.isPending}\n            onConfirm={cancelSelectedInvitations}\n            selectedLabel={organizationLocalization.selectedCount}\n          />\n        )}\n\n        <Card className=\"p-0\">\n          <Table aria-label={organizationLocalization.invitations}>\n            <TableHeader>\n              <TableRow>\n                {showSelection && (\n                  <TableHead className=\"w-10\">\n                    <OrganizationTableSelectAll\n                      allSelected={table.getIsAllPageRowsSelected()}\n                      disabled={isPending}\n                      localization={organizationLocalization}\n                      onCheckedChange={(checked) =>\n                        table.toggleAllPageRowsSelected(checked)\n                      }\n                      someSelected={table.getIsSomePageRowsSelected()}\n                    />\n                  </TableHead>\n                )}\n\n                <OrganizationSortableTableHead\n                  column={table.getColumn(\"email\")}\n                >\n                  {localization.auth.email}\n                </OrganizationSortableTableHead>\n\n                {table.getColumn(\"createdAt\")?.getIsVisible() && (\n                  <OrganizationSortableTableHead\n                    column={table.getColumn(\"createdAt\")}\n                  >\n                    {organizationLocalization.invitedAt}\n                  </OrganizationSortableTableHead>\n                )}\n\n                {table.getColumn(\"role\")?.getIsVisible() && (\n                  <OrganizationSortableTableHead\n                    column={table.getColumn(\"role\")}\n                  >\n                    {organizationLocalization.role}\n                  </OrganizationSortableTableHead>\n                )}\n\n                {table.getColumn(\"status\")?.getIsVisible() && (\n                  <OrganizationSortableTableHead\n                    column={table.getColumn(\"status\")}\n                  >\n                    {organizationLocalization.status}\n                  </OrganizationSortableTableHead>\n                )}\n\n                <TableHead className=\"text-end\">\n                  {organizationLocalization.actions}\n                </TableHead>\n              </TableRow>\n            </TableHeader>\n\n            <TableBody>\n              {isPending ? (\n                <OrganizationInvitationRowSkeleton />\n              ) : !table.getRowModel().rows.length ? (\n                <TableRow>\n                  <TableCell\n                    colSpan={visibleColumnCount + 1 + Number(showSelection)}\n                  >\n                    <OrganizationInvitationsEmpty\n                      isInvitePending={canInvite.isPending}\n                      onInvitePress={\n                        canInvite.data?.success\n                          ? () => setInviteOpen(true)\n                          : undefined\n                      }\n                    />\n                  </TableCell>\n                </TableRow>\n              ) : (\n                table\n                  .getRowModel()\n                  .rows.map((row) => (\n                    <OrganizationInvitationRow\n                      key={row.original.id}\n                      invitation={row.original}\n                      selectableRow={showSelection ? row : undefined}\n                      showCreatedAt={table\n                        .getColumn(\"createdAt\")\n                        ?.getIsVisible()}\n                      showRole={table.getColumn(\"role\")?.getIsVisible()}\n                      showStatus={table.getColumn(\"status\")?.getIsVisible()}\n                    />\n                  ))\n              )}\n            </TableBody>\n          </Table>\n        </Card>\n\n        <OrganizationTablePagination\n          canNextPage={table.getCanNextPage()}\n          canPreviousPage={table.getCanPreviousPage()}\n          disabled={isPending}\n          localization={organizationLocalization}\n          onFirstPage={() => table.firstPage()}\n          onLastPage={() => table.lastPage()}\n          onNextPage={() => table.nextPage()}\n          onPageSizeChange={(pageSize) => table.setPageSize(pageSize)}\n          onPreviousPage={() => table.previousPage()}\n          pageCount={table.getPageCount()}\n          pageIndex={pagination.pageIndex}\n          pageSize={pagination.pageSize}\n          rowCount={table.getRowCount()}\n          visibleRowCount={table.getRowModel().rows.length}\n        />\n      </div>\n\n      {canInvite.data?.success && (\n        <InviteMemberDialog open={inviteOpen} onOpenChange={setInviteOpen} />\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-invitations.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-logo.tsx",
      "content": "\"use client\"\n\nimport type { Organization } from \"better-auth/client\"\nimport { Briefcase } from \"lucide-react\"\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\"\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { cn } from \"cn\"\n\nexport type OrganizationLogoSize = \"sm\" | \"md\" | \"lg\"\n\nexport type OrganizationLogoProps = {\n  className?: string\n  fallback?: ReactNode\n  isPending?: boolean\n  organization?: Partial<Organization> | null\n  size?: OrganizationLogoSize\n}\n\nconst sizeClasses: Record<OrganizationLogoSize, string> = {\n  sm: \"size-8\",\n  md: \"size-10\",\n  lg: \"size-12\"\n}\n\nconst fallbackTextClasses: Record<OrganizationLogoSize, string> = {\n  sm: \"text-sm\",\n  md: \"text-base\",\n  lg: \"text-xl\"\n}\n\nexport function OrganizationLogo({\n  className,\n  fallback,\n  isPending,\n  organization,\n  size = \"sm\",\n  style,\n  ...props\n}: OrganizationLogoProps &\n  Omit<ComponentProps<typeof Avatar>, \"children\" | \"size\" | \"style\"> & {\n    style?: CSSProperties\n  }) {\n  if (isPending && !organization) {\n    return (\n      <Skeleton\n        className={cn(\"rounded-full\", sizeClasses[size], className)}\n        style={style}\n      />\n    )\n  }\n\n  const initials = organization?.name?.slice(0, 2).toUpperCase()\n  const normalizedLogo = organization?.logo?.trim() || undefined\n\n  return (\n    <Avatar\n      className={cn(\"rounded-full\", sizeClasses[size], className)}\n      style={style}\n      {...props}\n    >\n      <AvatarImage\n        alt={organization?.name ?? \"Organization\"}\n        src={normalizedLogo}\n      />\n\n      <AvatarFallback\n        className={cn(\"text-muted-foreground!\", fallbackTextClasses[size])}\n      >\n        {fallback || initials || <Briefcase className=\"size-4\" />}\n      </AvatarFallback>\n    </Avatar>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-logo.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-member-row-skeleton.tsx",
      "content": "\"use client\"\n\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { TableCell, TableRow } from \"@/components/ui/table\"\nimport { UserView } from \"../user/user-view\"\n\n/**\n * Placeholder row matching `OrganizationMemberRow` while members load.\n */\nexport function OrganizationMemberRowSkeleton({\n  showTeams\n}: {\n  showTeams?: boolean\n}) {\n  return (\n    <TableRow>\n      <TableCell>\n        <UserView isPending />\n      </TableCell>\n\n      <TableCell>\n        <Skeleton className=\"h-4 w-18 rounded-md\" />\n      </TableCell>\n\n      {showTeams && (\n        <TableCell>\n          <Skeleton className=\"h-4 w-24 rounded-md\" />\n        </TableCell>\n      )}\n\n      <TableCell className=\"flex justify-end\">\n        <Skeleton className=\"size-8 rounded-md\" />\n      </TableCell>\n    </TableRow>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-member-row-skeleton.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-member-row.tsx",
      "content": "\"use client\"\n\nimport { formatAdditionalFieldValue } from \"@better-auth-ui/core\"\nimport {\n  hasMemberRole,\n  memberRoleLabels,\n  mergeOrganizationRoleLabels,\n  type OrganizationAuthClient,\n  type OrganizationRolesAuthClient,\n  type OrganizationTeamsAuthClient\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin, useSession } from \"@better-auth-ui/react\"\nimport {\n  useHasPermission,\n  useListRoles,\n  useListUserTeams\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Member, Organization, User } from \"better-auth/client\"\nimport { LogOut, Pencil, Trash2 } from \"lucide-react\"\nimport { useState } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { TableCell, TableRow } from \"@/components/ui/table\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { UserView } from \"../user/user-view\"\nimport { EditMemberRolesDialog } from \"./edit-member-roles-dialog\"\nimport { LeaveOrganizationDialog } from \"./leave-organization-dialog\"\nimport {\n  type OrganizationSelectableRow,\n  OrganizationTableSelectRow\n} from \"./organization-table-selection\"\nimport { RemoveMemberDialog } from \"./remove-member-dialog\"\n\nexport type OrganizationMemberRowProps = {\n  member: Member & { user: Partial<User> }\n  isOwner?: boolean\n  ownerCount?: number\n  organization: Organization\n  selectableRow?: OrganizationSelectableRow<Member & { user: Partial<User> }>\n  showRole?: boolean\n  showTeams?: boolean\n}\n\nexport function OrganizationMemberRow({\n  member,\n  isOwner,\n  ownerCount,\n  organization,\n  selectableRow,\n  showRole = true,\n  showTeams\n}: OrganizationMemberRowProps) {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const {\n    modelFields: { member: memberFields },\n    dynamicAccessControl,\n    creatorRole,\n    localization: organizationLocalization,\n    roles\n  } = useAuthPlugin(organizationPlugin)\n\n  const { data: session } = useSession(authClient)\n  const canReadRoles = useHasPermission(authClient, {\n    organizationId: organization.id,\n    permissions: { ac: [\"read\"] }\n  })\n  const dynamicRoles = useListRoles(authClient as OrganizationRolesAuthClient, {\n    query: { organizationId: organization.id },\n    enabled:\n      dynamicAccessControl?.enabled === true &&\n      canReadRoles.data?.success === true\n  })\n  const memberTeams = useListUserTeams(\n    authClient as OrganizationTeamsAuthClient,\n    {\n      query: {\n        organizationId: organization.id,\n        userId: member.userId\n      },\n      enabled: showTeams === true\n    }\n  )\n\n  const { data: hasUpdatePermission, isPending: updatePermissionPending } =\n    useHasPermission(authClient, {\n      organizationId: organization.id,\n      permissions: { member: [\"update\"] }\n    })\n\n  const { data: hasDeletePermission, isPending: deletePermissionPending } =\n    useHasPermission(authClient, {\n      organizationId: organization.id,\n      permissions: { member: [\"delete\"] }\n    })\n\n  const mergedRoles = mergeOrganizationRoleLabels(roles, dynamicRoles.data)\n  const roleLabel = memberRoleLabels(member.role, mergedRoles).join(\", \")\n  const teamNames = memberTeams.data?.map((team) => team.name).join(\", \")\n\n  const assignableRoles = Object.entries(mergedRoles).filter(\n    ([key]) => isOwner || key !== creatorRole\n  )\n\n  const isCurrentUser = session?.user.id === member.userId\n  const targetIsOwner = hasMemberRole(member.role, creatorRole)\n  const canManageTarget = isOwner || !targetIsOwner\n  const onlyOwnerActionDisabled =\n    targetIsOwner && (ownerCount === undefined || ownerCount <= 1)\n\n  const [removeOpen, setRemoveOpen] = useState(false)\n  const [leaveOpen, setLeaveOpen] = useState(false)\n  const [roleEditorOpen, setRoleEditorOpen] = useState(false)\n\n  return (\n    <TableRow\n      data-state={selectableRow?.getIsSelected() ? \"selected\" : undefined}\n    >\n      {selectableRow && (\n        <TableCell>\n          <OrganizationTableSelectRow\n            localization={organizationLocalization}\n            row={selectableRow}\n          />\n        </TableCell>\n      )}\n\n      <TableCell>\n        <div className=\"flex flex-col gap-1\">\n          <UserView user={member.user} />\n          {memberFields.map((field) => {\n            const value = formatAdditionalFieldValue(\n              (member as unknown as Record<string, unknown>)[field.name]\n            )\n            return value ? (\n              <span className=\"text-xs text-muted-foreground\" key={field.name}>\n                {field.label}: {value}\n              </span>\n            ) : null\n          })}\n        </div>\n      </TableCell>\n\n      {showRole && <TableCell>{roleLabel}</TableCell>}\n\n      {showTeams && (\n        <TableCell className=\"text-sm\">\n          {memberTeams.isPending ? (\n            <Skeleton className=\"h-4 w-24 rounded-md\" />\n          ) : memberTeams.isError ? null : teamNames ? (\n            teamNames\n          ) : (\n            <span className=\"text-muted-foreground\">\n              {organizationLocalization.noTeams}\n            </span>\n          )}\n        </TableCell>\n      )}\n\n      <TableCell>\n        <div className=\"flex items-center justify-end gap-1\">\n          {canManageTarget && updatePermissionPending && (\n            <Button\n              aria-label={organizationLocalization.changeMemberRole}\n              className=\"size-8\"\n              disabled\n              size=\"icon\"\n              variant=\"ghost\"\n            >\n              <Pencil />\n            </Button>\n          )}\n          {canManageTarget && hasUpdatePermission?.success && (\n            <Button\n              className=\"size-8\"\n              onClick={() => setRoleEditorOpen(true)}\n              size=\"icon\"\n              variant=\"ghost\"\n            >\n              <Pencil />\n              <span className=\"sr-only\">\n                {organizationLocalization.changeMemberRole}\n              </span>\n            </Button>\n          )}\n\n          {canManageTarget && hasUpdatePermission?.success && (\n            <EditMemberRolesDialog\n              member={member}\n              onOpenChange={setRoleEditorOpen}\n              open={roleEditorOpen}\n              organizationId={organization.id}\n              protectedRole={creatorRole}\n              protectedRoleRemovalDisabled={onlyOwnerActionDisabled}\n              roles={assignableRoles}\n            />\n          )}\n\n          {isCurrentUser ? (\n            <Button\n              size=\"icon\"\n              variant=\"outline\"\n              className=\"size-8 text-destructive\"\n              aria-label={organizationLocalization.leaveOrganization}\n              disabled={onlyOwnerActionDisabled}\n              title={\n                onlyOwnerActionDisabled\n                  ? organizationLocalization.onlyOwnerActionDisabled\n                  : undefined\n              }\n              onClick={() => setLeaveOpen(true)}\n            >\n              <LogOut />\n            </Button>\n          ) : canManageTarget && deletePermissionPending ? (\n            <Button\n              aria-label={organizationLocalization.removeMember}\n              className=\"size-8 text-destructive\"\n              disabled\n              size=\"icon\"\n              variant=\"outline\"\n            >\n              <Trash2 />\n            </Button>\n          ) : canManageTarget && hasDeletePermission?.success ? (\n            <Button\n              size=\"icon\"\n              variant=\"outline\"\n              className=\"size-8 text-destructive\"\n              aria-label={organizationLocalization.removeMember}\n              disabled={onlyOwnerActionDisabled}\n              title={\n                onlyOwnerActionDisabled\n                  ? organizationLocalization.onlyOwnerActionDisabled\n                  : undefined\n              }\n              onClick={() => setRemoveOpen(true)}\n            >\n              <Trash2 />\n            </Button>\n          ) : null}\n        </div>\n\n        {isCurrentUser && organization && !onlyOwnerActionDisabled ? (\n          <LeaveOrganizationDialog\n            open={leaveOpen}\n            onOpenChange={setLeaveOpen}\n            organization={organization}\n          />\n        ) : (\n          canManageTarget &&\n          hasDeletePermission?.success &&\n          !onlyOwnerActionDisabled && (\n            <RemoveMemberDialog\n              open={removeOpen}\n              onOpenChange={setRemoveOpen}\n              member={member}\n            />\n          )\n        )}\n      </TableCell>\n    </TableRow>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-member-row.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-members.tsx",
      "content": "\"use client\"\n\nimport { getClampedTablePageIndex } from \"@better-auth-ui/core\"\nimport {\n  hasMemberRole,\n  type OrganizationAuthClient\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin, useSession } from \"@better-auth-ui/react\"\nimport {\n  useActiveMemberRole,\n  useActiveOrganization,\n  useHasPermission,\n  useListOrganizationMembers,\n  useRemoveMember\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { keepPreviousData } from \"@tanstack/react-query\"\nimport type { Member, User } from \"better-auth/client\"\nimport { Filter, Search, X } from \"lucide-react\"\nimport { type ComponentProps, useEffect, useRef, useState } from \"react\"\nimport { toast } from \"sonner\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { Card } from \"@/components/ui/card\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput\n} from \"@/components/ui/input-group\"\nimport {\n  Table,\n  TableBody,\n  TableHead,\n  TableHeader,\n  TableRow\n} from \"@/components/ui/table\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport { InviteMemberDialog } from \"./invite-member-dialog\"\nimport { OrganizationMemberRow } from \"./organization-member-row\"\nimport { OrganizationMemberRowSkeleton } from \"./organization-member-row-skeleton\"\nimport { OrganizationSortableTableHead } from \"./organization-sortable-table-head\"\nimport {\n  createOrganizationColumnHelper,\n  ORGANIZATION_TABLE_PAGE_SIZE,\n  useOrganizationTable\n} from \"./organization-table\"\nimport { OrganizationTableBulkAction } from \"./organization-table-bulk-action\"\nimport { OrganizationTablePagination } from \"./organization-table-pagination\"\nimport { OrganizationTableSelectAll } from \"./organization-table-selection\"\nimport { useOrganizationTableState } from \"./organization-table-state\"\nimport { OrganizationTableViewOptions } from \"./organization-table-view-options\"\n\ntype MemberRow = Member & { user: Partial<User> }\n\nconst memberColumnHelper = createOrganizationColumnHelper<MemberRow>()\nconst memberColumns = memberColumnHelper.columns([\n  memberColumnHelper.accessor(\n    (member) => member.user.name || member.user.email || \"\",\n    { id: \"user\", enableHiding: false, filterFn: \"includesString\" }\n  ),\n  memberColumnHelper.accessor(\"role\", {\n    enableGlobalFilter: false,\n    filterFn: (row, columnId, value) =>\n      hasMemberRole(row.getValue<string>(columnId), String(value))\n  }),\n  memberColumnHelper.display({\n    id: \"teams\",\n    enableGlobalFilter: false,\n    enableSorting: false\n  })\n])\nconst memberColumnsWithoutTeams = memberColumns.filter(\n  (column) => column.id !== \"teams\"\n)\nconst EMPTY_MEMBERS: MemberRow[] = []\nconst MEMBER_COLUMN_IDS = [\"user\", \"role\", \"teams\"] as const\n\n/** Props for the `OrganizationMembers` component. */\nexport type OrganizationMembersProps = {\n  className?: string\n  /**\n   * Number of rows per page. This value must be a positive integer. Setting it\n   * moves paging, role filtering, and role sorting\n   * onto the server, which is what large organizations want: without it the\n   * endpoint caps the response at 100 members with no indication.\n   *\n   * Leave it unset to keep the whole list in memory and filter it in the\n   * browser.\n   */\n  pageSize?: number\n}\n\nfunction validatePageSize(pageSize?: number) {\n  if (\n    pageSize !== undefined &&\n    (!Number.isInteger(pageSize) || pageSize <= 0)\n  ) {\n    throw new RangeError(\"pageSize must be a positive integer\")\n  }\n\n  return pageSize\n}\n\n/**\n * Organization members table with title, invite control, and per-row actions.\n */\nexport function OrganizationMembers({\n  className,\n  pageSize,\n  ...props\n}: OrganizationMembersProps & ComponentProps<\"div\">) {\n  const validatedPageSize = validatePageSize(pageSize)\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const {\n    localization: organizationLocalization,\n    membershipLimit,\n    roles,\n    creatorRole,\n    teams\n  } = useAuthPlugin(organizationPlugin)\n\n  const { data: activeOrganization, isPending: activeOrganizationPending } =\n    useActiveOrganization(authClient)\n\n  const paged = validatedPageSize !== undefined\n  const tableState = useOrganizationTableState(\n    \"organizationMembers\",\n    validatedPageSize ?? ORGANIZATION_TABLE_PAGE_SIZE,\n    MEMBER_COLUMN_IDS\n  )\n  const { columnFilters, globalFilter, pagination, sorting } = tableState\n  const roleFilter = String(\n    columnFilters.find((filter) => filter.id === \"role\")?.value ?? \"all\"\n  )\n  const previousOrganizationId = useRef<string | undefined>(undefined)\n\n  useEffect(() => {\n    const organizationId = activeOrganization?.id\n    if (!organizationId) return\n    if (\n      previousOrganizationId.current &&\n      previousOrganizationId.current !== organizationId\n    ) {\n      tableState.setPagination((current) => ({ ...current, pageIndex: 0 }))\n    }\n    previousOrganizationId.current = organizationId\n  }, [activeOrganization?.id, tableState.setPagination])\n\n  const { data: membersData, isPending: membersPending } =\n    useListOrganizationMembers(authClient, {\n      enabled: !paged || tableState.ready,\n      placeholderData: paged ? keepPreviousData : undefined,\n      query: paged\n        ? {\n            limit: pagination.pageSize,\n            offset: pagination.pageIndex * pagination.pageSize,\n            ...(roleFilter === \"all\"\n              ? {}\n              : {\n                  filterField: \"role\",\n                  filterValue: roleFilter,\n                  // Roles are stored comma-joined, so an exact match would\n                  // drop anyone holding more than one.\n                  filterOperator: \"contains\" as const\n                }),\n            ...(sorting[0]?.id === \"role\"\n              ? {\n                  sortBy: \"role\",\n                  sortDirection: sorting[0].desc\n                    ? (\"desc\" as const)\n                    : (\"asc\" as const)\n                }\n              : {})\n          }\n        : undefined\n    })\n\n  // The signed-in user need not be on the loaded page, so their own role comes\n  // from a dedicated endpoint rather than from the member list.\n  const { data: activeMemberRole } = useActiveMemberRole(authClient)\n  const { data: session } = useSession(authClient)\n  const owners = useListOrganizationMembers(authClient, {\n    query: {\n      organizationId: activeOrganization?.id,\n      filterField: \"role\",\n      filterValue: creatorRole,\n      filterOperator: \"contains\",\n      limit: 1\n    },\n    enabled: Boolean(activeOrganization?.id)\n  })\n\n  const canInvite = useHasPermission(authClient, {\n    permissions: { invitation: [\"create\"] }\n  })\n  const canListMemberTeams = useHasPermission(authClient, {\n    organizationId: activeOrganization?.id,\n    permissions: { member: [\"update\"] },\n    enabled: teams && Boolean(activeOrganization?.id)\n  })\n  const canDeleteMembers = useHasPermission(authClient, {\n    organizationId: activeOrganization?.id,\n    permissions: { member: [\"delete\"] },\n    enabled: Boolean(activeOrganization?.id)\n  })\n\n  const isPending =\n    activeOrganizationPending ||\n    membersPending ||\n    owners.isPending ||\n    canDeleteMembers.isPending ||\n    (teams && canListMemberTeams.isPending)\n\n  const [inviteOpen, setInviteOpen] = useState(false)\n\n  const isOwner = hasMemberRole(activeMemberRole?.role, creatorRole)\n  const ownerCount = owners.data?.total ?? owners.data?.members.length\n  const showTeams = teams && canListMemberTeams.data?.success === true\n\n  const total = membersData?.total ?? membersData?.members.length ?? 0\n\n  useEffect(() => {\n    if (!paged || !tableState.ready || !membersData) return\n\n    const lastPageIndex = getClampedTablePageIndex(\n      pagination.pageIndex,\n      pagination.pageSize,\n      total\n    )\n    if (pagination.pageIndex > lastPageIndex) {\n      tableState.setPagination((current) => ({\n        ...current,\n        pageIndex: lastPageIndex\n      }))\n    }\n  }, [\n    membersData,\n    paged,\n    pagination.pageIndex,\n    pagination.pageSize,\n    tableState.ready,\n    tableState.setPagination,\n    total\n  ])\n\n  const table = useOrganizationTable<MemberRow, null>(\n    {\n      atoms: tableState.atoms,\n      columns: showTeams ? memberColumns : memberColumnsWithoutTeams,\n      data: membersData?.members ?? EMPTY_MEMBERS,\n      enableRowSelection: (row) => {\n        const targetIsOwner = hasMemberRole(row.original.role, creatorRole)\n        return (\n          canDeleteMembers.data?.success === true &&\n          row.original.userId !== session?.user.id &&\n          (isOwner || !targetIsOwner) &&\n          !(targetIsOwner && (ownerCount === undefined || ownerCount <= 1))\n        )\n      },\n      globalFilterFn: \"includesString\",\n      getRowId: (member) => member.id,\n      manualFiltering: paged,\n      manualPagination: paged,\n      manualSorting: paged,\n      rowCount: paged ? total : undefined\n    },\n    () => null\n  )\n\n  const removeMembers = useRemoveMember(authClient)\n  const roleFacetRows = table.getColumn(\"role\")?.getFacetedRowModel().flatRows\n  const selectedMembers = table.getSelectedRowModel().rows\n  const showSelection = canDeleteMembers.data?.success === true\n\n  async function removeSelectedMembers() {\n    if (!activeOrganization) return\n\n    const results = await Promise.allSettled(\n      selectedMembers.map((row) =>\n        removeMembers.mutateAsync({\n          memberIdOrEmail: row.original.id,\n          organizationId: activeOrganization.id\n        })\n      )\n    )\n    const removedCount = results.filter(\n      (result) => result.status === \"fulfilled\"\n    ).length\n\n    if (removedCount > 0) {\n      toast.success(\n        organizationLocalization.membersRemoved.replace(\n          \"{{count}}\",\n          String(removedCount)\n        )\n      )\n    }\n    table.resetRowSelection(true)\n  }\n\n  const atMembershipLimit =\n    membershipLimit !== undefined && total >= membershipLimit\n\n  return (\n    <div className={cn(\"flex flex-col gap-3\", className)} {...props}>\n      <div className=\"flex items-end justify-between gap-3\">\n        <h3 className=\"truncate text-sm font-semibold\">\n          {organizationLocalization.members}\n        </h3>\n\n        {(canInvite.isPending || canInvite.data?.success) && (\n          <Button\n            className=\"shrink-0\"\n            size=\"sm\"\n            disabled={canInvite.isPending || atMembershipLimit}\n            onClick={() => setInviteOpen(true)}\n          >\n            {organizationLocalization.inviteMember}\n          </Button>\n        )}\n      </div>\n\n      <div className=\"flex flex-col gap-4\">\n        <div className=\"flex flex-wrap items-center gap-3\">\n          {/* list-members has no search parameter, so a search box would\n              only ever filter the page in front of you. */}\n          {!paged && (\n            <InputGroup className=\"min-w-0 sm:w-[220px]\">\n              <InputGroupInput\n                type=\"search\"\n                value={globalFilter}\n                onChange={(event) => table.setGlobalFilter(event.target.value)}\n                aria-label={organizationLocalization.search}\n                placeholder={organizationLocalization.search}\n                disabled={isPending}\n              />\n\n              <InputGroupAddon>\n                <Search className=\"text-muted-foreground\" />\n              </InputGroupAddon>\n            </InputGroup>\n          )}\n\n          <DropdownMenu>\n            <DropdownMenuTrigger\n              className={cn(buttonVariants({ size: \"sm\", variant: \"outline\" }))}\n              disabled={isPending}\n            >\n              <Filter />\n\n              {organizationLocalization.role}\n            </DropdownMenuTrigger>\n\n            <DropdownMenuContent align=\"start\">\n              <DropdownMenuRadioGroup\n                value={roleFilter}\n                onValueChange={(value) =>\n                  table\n                    .getColumn(\"role\")\n                    ?.setFilterValue(value === \"all\" ? undefined : value)\n                }\n              >\n                <DropdownMenuRadioItem value=\"all\">\n                  {organizationLocalization.all}\n                </DropdownMenuRadioItem>\n\n                {Object.entries(roles).map(([role, label]) => (\n                  <DropdownMenuRadioItem key={role} value={role}>\n                    {label}\n                    {!paged &&\n                      ` (${\n                        roleFacetRows?.filter((row) =>\n                          hasMemberRole(row.original.role, role)\n                        ).length ?? 0\n                      })`}\n                  </DropdownMenuRadioItem>\n                ))}\n              </DropdownMenuRadioGroup>\n            </DropdownMenuContent>\n          </DropdownMenu>\n\n          <div className=\"ms-auto\">\n            <OrganizationTableViewOptions\n              columns={[\n                {\n                  id: \"role\",\n                  label: organizationLocalization.role,\n                  visible: table.getColumn(\"role\")?.getIsVisible() ?? true,\n                  onVisibleChange: (visible) =>\n                    table.getColumn(\"role\")?.toggleVisibility(visible)\n                },\n                ...(showTeams\n                  ? [\n                      {\n                        id: \"teams\",\n                        label: organizationLocalization.teams,\n                        visible:\n                          table.getColumn(\"teams\")?.getIsVisible() ?? true,\n                        onVisibleChange: (visible: boolean) =>\n                          table.getColumn(\"teams\")?.toggleVisibility(visible)\n                      }\n                    ]\n                  : [])\n              ]}\n              disabled={isPending}\n              localization={organizationLocalization}\n            />\n          </div>\n        </div>\n\n        {roleFilter !== \"all\" && (\n          <Badge variant=\"secondary\" className=\"w-fit gap-1\">\n            {organizationLocalization.role}:{\" \"}\n            <span className=\"capitalize\">\n              {roles?.[roleFilter] ?? roleFilter}\n            </span>\n            <Button\n              aria-label={organizationLocalization.clear}\n              className=\"size-4 rounded-sm text-muted-foreground\"\n              onClick={() => table.getColumn(\"role\")?.setFilterValue(undefined)}\n              size=\"icon-xs\"\n              type=\"button\"\n              variant=\"ghost\"\n            >\n              <X className=\"size-3\" />\n            </Button>\n          </Badge>\n        )}\n\n        {selectedMembers.length > 0 && (\n          <OrganizationTableBulkAction\n            actionLabel={organizationLocalization.removeSelectedMembers}\n            cancelLabel={localization.settings.cancel}\n            count={selectedMembers.length}\n            description={\n              organizationLocalization.removeSelectedMembersDescription\n            }\n            isPending={removeMembers.isPending}\n            onConfirm={removeSelectedMembers}\n            selectedLabel={organizationLocalization.selectedCount}\n          />\n        )}\n\n        <Card className=\"p-0\">\n          <Table aria-label={organizationLocalization.members}>\n            <TableHeader>\n              <TableRow>\n                {showSelection && (\n                  <TableHead className=\"w-10\">\n                    <OrganizationTableSelectAll\n                      allSelected={table.getIsAllPageRowsSelected()}\n                      disabled={isPending}\n                      localization={organizationLocalization}\n                      onCheckedChange={(checked) =>\n                        table.toggleAllPageRowsSelected(checked)\n                      }\n                      someSelected={table.getIsSomePageRowsSelected()}\n                    />\n                  </TableHead>\n                )}\n\n                {/* Name and email live on the joined user row, which\n                    list-members cannot sort by. */}\n                {paged ? (\n                  <TableHead>{organizationLocalization.member}</TableHead>\n                ) : (\n                  <OrganizationSortableTableHead\n                    column={table.getColumn(\"user\")}\n                  >\n                    {organizationLocalization.member}\n                  </OrganizationSortableTableHead>\n                )}\n\n                {table.getColumn(\"role\")?.getIsVisible() && (\n                  <OrganizationSortableTableHead\n                    column={table.getColumn(\"role\")}\n                  >\n                    {organizationLocalization.role}\n                  </OrganizationSortableTableHead>\n                )}\n\n                {showTeams && table.getColumn(\"teams\")?.getIsVisible() && (\n                  <TableHead>{organizationLocalization.teams}</TableHead>\n                )}\n\n                <TableHead className=\"text-end\">\n                  {organizationLocalization.actions}\n                </TableHead>\n              </TableRow>\n            </TableHeader>\n\n            <TableBody>\n              {isPending ? (\n                <OrganizationMemberRowSkeleton showTeams={showTeams} />\n              ) : (\n                !!activeOrganization &&\n                table\n                  .getRowModel()\n                  .rows.map((row) => (\n                    <OrganizationMemberRow\n                      key={row.original.id}\n                      member={row.original}\n                      isOwner={isOwner}\n                      ownerCount={ownerCount}\n                      organization={activeOrganization}\n                      selectableRow={showSelection ? row : undefined}\n                      showRole={table.getColumn(\"role\")?.getIsVisible()}\n                      showTeams={\n                        showTeams &&\n                        table.getColumn(\"teams\")?.getIsVisible() === true\n                      }\n                    />\n                  ))\n              )}\n            </TableBody>\n          </Table>\n        </Card>\n\n        <OrganizationTablePagination\n          canNextPage={table.getCanNextPage()}\n          canPreviousPage={table.getCanPreviousPage()}\n          disabled={isPending}\n          localization={organizationLocalization}\n          onFirstPage={() => table.firstPage()}\n          onLastPage={() => table.lastPage()}\n          onNextPage={() => table.nextPage()}\n          onPageSizeChange={(pageSize) => table.setPageSize(pageSize)}\n          onPreviousPage={() => table.previousPage()}\n          pageCount={table.getPageCount()}\n          pageIndex={pagination.pageIndex}\n          pageSize={pagination.pageSize}\n          rowCount={table.getRowCount()}\n          visibleRowCount={table.getRowModel().rows.length}\n        />\n      </div>\n\n      {canInvite.data?.success && (\n        <InviteMemberDialog open={inviteOpen} onOpenChange={setInviteOpen} />\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-members.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-people.tsx",
      "content": "\"use client\"\n\nimport type { ComponentProps } from \"react\"\n\nimport { cn } from \"cn\"\nimport { OrganizationInvitations } from \"./organization-invitations\"\nimport { OrganizationMembers } from \"./organization-members\"\n\n/** Props for the `OrganizationPeople` component. */\nexport type OrganizationPeopleProps = {\n  className?: string\n}\n\n/**\n * Organization people UI: members table (see `OrganizationMembers`), then org\n * invitations.\n */\nexport function OrganizationPeople({\n  className,\n  ...props\n}: OrganizationPeopleProps & ComponentProps<\"div\">) {\n  return (\n    <div className={cn(\"flex flex-col gap-4 md:gap-6\", className)} {...props}>\n      <OrganizationMembers />\n      <OrganizationInvitations />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-people.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-profile.tsx",
      "content": "\"use client\"\n\nimport {\n  fieldsWithModelValues,\n  getAdditionalFieldDefaultValues,\n  getAdditionalFieldSubmitValues,\n  validateStringLength\n} from \"@better-auth-ui/core\"\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useHasPermission,\n  useUpdateOrganization\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { useEffect } from \"react\"\nimport { toast } from \"sonner\"\n\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport {\n  getAuthAdditionalFieldValidators,\n  isAuthFormFieldInvalid,\n  useAuthForm\n} from \"../auth-form\"\nimport { ChangeOrganizationLogo } from \"./change-organization-logo\"\nimport { SlugField } from \"./slug-field\"\n\nexport type OrganizationProfileProps = {\n  className?: string\n  hideSlug?: boolean\n}\n\n/**\n * Profile card for the active organization: logo (when enabled), display name, and slug.\n */\nexport function OrganizationProfile({\n  className,\n  hideSlug: hideSlugProp\n}: OrganizationProfileProps) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const {\n    additionalFields,\n    localization: organizationLocalization,\n    hideSlug: pluginHideSlug\n  } = useAuthPlugin(organizationPlugin)\n  const hideSlug = hideSlugProp ?? pluginHideSlug ?? false\n\n  const { data: activeOrganization } = useActiveOrganization(authClient)\n  const canUpdate = useHasPermission(authClient, {\n    permissions: { organization: [\"update\"] }\n  })\n\n  const { mutateAsync: commitOrganizationUpdate, isPending } =\n    useUpdateOrganization(authClient, {\n      onSuccess: () =>\n        toast.success(organizationLocalization.organizationUpdatedSuccess)\n    })\n\n  const form = useAuthForm({\n    defaultValues: {\n      additionalFields: getAdditionalFieldDefaultValues(additionalFields),\n      name: \"\",\n      slug: \"\"\n    },\n    onSubmit: async ({ value }) => {\n      if (!activeOrganization || !canUpdate.data?.success) return\n      await commitOrganizationUpdate({\n        data: {\n          ...getAdditionalFieldSubmitValues(\n            additionalFields,\n            value.additionalFields\n          ),\n          name: value.name,\n          ...(!hideSlug && { slug: value.slug })\n        }\n      })\n    }\n  })\n\n  useEffect(() => {\n    if (!activeOrganization) return\n    form.reset({\n      additionalFields: getAdditionalFieldDefaultValues(\n        fieldsWithModelValues(\n          additionalFields,\n          activeOrganization as Record<string, unknown>\n        )\n      ),\n      name: activeOrganization.name,\n      slug: activeOrganization.slug\n    })\n  }, [activeOrganization, additionalFields, form])\n\n  const nameInputId = `${activeOrganization?.id ?? \"org\"}-name`\n  const slugInputId = `${activeOrganization?.id ?? \"org\"}-slug`\n  const formDisabled =\n    isPending || canUpdate.isPending || !canUpdate.data?.success\n\n  return (\n    <div>\n      <h2 className={cn(\"mb-3 text-sm font-semibold\")}>\n        {organizationLocalization.organizationProfile}\n      </h2>\n\n      <Card className={className}>\n        <CardContent>\n          <form.AppForm>\n            <form.AuthFormRoot className=\"flex flex-col gap-4\">\n              <ChangeOrganizationLogo />\n\n              <form.AppField\n                name=\"name\"\n                validators={{\n                  onChange: ({ value }) =>\n                    validateStringLength(value, {\n                      requiredMessage: localization.auth.fieldRequired,\n                      trim: true\n                    })\n                }}\n              >\n                {(field) => {\n                  const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n\n                  return (\n                    <Field data-invalid={isInvalid}>\n                      <FieldLabel htmlFor={nameInputId}>\n                        {organizationLocalization.name}\n                      </FieldLabel>\n\n                      {activeOrganization ? (\n                        <Input\n                          id={nameInputId}\n                          name={field.name}\n                          value={field.state.value}\n                          onBlur={field.handleBlur}\n                          onChange={(event) =>\n                            field.handleChange(event.target.value)\n                          }\n                          autoComplete=\"organization\"\n                          placeholder={organizationLocalization.namePlaceholder}\n                          disabled={formDisabled}\n                          aria-invalid={isInvalid}\n                        />\n                      ) : (\n                        <Skeleton className=\"h-8 w-full rounded-md\" />\n                      )}\n\n                      <field.AuthFormFieldError />\n                    </Field>\n                  )\n                }}\n              </form.AppField>\n\n              {!hideSlug &&\n                (activeOrganization ? (\n                  <form.AppField name=\"slug\">\n                    {(field) => (\n                      <SlugField\n                        id={slugInputId}\n                        value={field.state.value}\n                        onChange={field.handleChange}\n                        currentSlug={activeOrganization.slug}\n                        disabled={formDisabled}\n                      />\n                    )}\n                  </form.AppField>\n                ) : (\n                  <Field>\n                    <FieldLabel>{organizationLocalization.slug}</FieldLabel>\n                    <Skeleton className=\"h-8 w-full rounded-md\" />\n                  </Field>\n                ))}\n\n              {activeOrganization &&\n                additionalFields.map((configuredField) => (\n                  <form.AppField\n                    key={configuredField.name}\n                    name={`additionalFields.${configuredField.name}`}\n                    validators={getAuthAdditionalFieldValidators(\n                      configuredField,\n                      localization.auth.fieldRequired\n                    )}\n                  >\n                    {(field) => (\n                      <field.AuthFormAdditionalField\n                        field={configuredField}\n                        isPending={formDisabled}\n                        optionalLabel={localization.settings.optional}\n                      />\n                    )}\n                  </form.AppField>\n                ))}\n\n              {(canUpdate.isPending || canUpdate.data?.success) && (\n                <form.AuthFormSubmitButton\n                  disabled={formDisabled || !activeOrganization}\n                  size=\"sm\"\n                  className=\"mt-1 w-fit\"\n                >\n                  {localization.settings.saveChanges}\n                </form.AuthFormSubmitButton>\n              )}\n            </form.AuthFormRoot>\n          </form.AppForm>\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-profile.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-roles.tsx",
      "content": "\"use client\"\n\nimport {\n  type AdditionalFields,\n  fieldsWithModelValues,\n  getAdditionalFieldDefaultValues,\n  getAdditionalFieldSubmitValues,\n  validateStringLength\n} from \"@better-auth-ui/core\"\nimport type { OrganizationRolesAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useCreateRole,\n  useDeleteRole,\n  useHasPermission,\n  useListOrganizationMembers,\n  useListRoles,\n  useUpdateRole\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { Filter, Pencil, Plus, Search, Trash2, X } from \"lucide-react\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport { toast } from \"sonner\"\n\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogMedia,\n  AlertDialogTitle,\n  AlertDialogTrigger\n} from \"@/components/ui/alert-dialog\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput\n} from \"@/components/ui/input-group\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow\n} from \"@/components/ui/table\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport {\n  getAuthAdditionalFieldValidators,\n  isAuthFormFieldInvalid,\n  useAuthForm\n} from \"../auth-form\"\nimport { OrganizationSortableTableHead } from \"./organization-sortable-table-head\"\nimport {\n  createOrganizationColumnHelper,\n  ORGANIZATION_TABLE_PAGE_SIZE,\n  useOrganizationTable\n} from \"./organization-table\"\nimport { OrganizationTableBulkAction } from \"./organization-table-bulk-action\"\nimport { OrganizationTablePagination } from \"./organization-table-pagination\"\nimport {\n  type OrganizationSelectableRow,\n  OrganizationTableSelectAll,\n  OrganizationTableSelectRow\n} from \"./organization-table-selection\"\nimport { useOrganizationTableState } from \"./organization-table-state\"\nimport { OrganizationTableViewOptions } from \"./organization-table-view-options\"\n\ntype Role = {\n  id: string\n  role: string\n  permission: Record<string, string[]>\n  [key: string]: unknown\n}\n\nconst roleColumnHelper = createOrganizationColumnHelper<Role>()\nconst roleColumns = roleColumnHelper.columns([\n  roleColumnHelper.accessor(\"role\", {\n    enableHiding: false,\n    filterFn: \"includesString\"\n  }),\n  roleColumnHelper.accessor(\n    (role) =>\n      Object.values(role.permission).reduce(\n        (total, actions) => total + actions.length,\n        0\n      ),\n    { id: \"permissions\", enableGlobalFilter: false }\n  ),\n  roleColumnHelper.accessor((role) => Object.keys(role.permission), {\n    id: \"permissionResources\",\n    enableGlobalFilter: false,\n    enableHiding: false,\n    enableSorting: false,\n    filterFn: (row, columnId, value) =>\n      row.getValue<string[]>(columnId).includes(String(value))\n  })\n])\nconst ROLE_COLUMN_IDS = [\"role\", \"permissions\", \"permissionResources\"] as const\nconst EMPTY_ROLES: Role[] = []\n\nexport function OrganizationRoles({\n  organizationId\n}: {\n  organizationId: string\n}) {\n  const { authClient, localization: authLocalization } =\n    useAuth<OrganizationRolesAuthClient>()\n  const { dynamicAccessControl, localization, modelFields } =\n    useAuthPlugin(organizationPlugin)\n  const canRead = useHasPermission(authClient, {\n    organizationId,\n    permissions: { ac: [\"read\"] }\n  })\n  const roles = useListRoles(authClient, {\n    query: { organizationId },\n    enabled: !!organizationId && canRead.data?.success === true\n  })\n  const canCreate = useHasPermission(authClient, {\n    organizationId,\n    permissions: { ac: [\"create\"] }\n  })\n  const canUpdate = useHasPermission(authClient, {\n    organizationId,\n    permissions: { ac: [\"update\"] }\n  })\n  const canDelete = useHasPermission(authClient, {\n    organizationId,\n    permissions: { ac: [\"delete\"] }\n  })\n  const [editingRole, setEditingRole] = useState<Role | null>()\n  const tableState = useOrganizationTableState(\n    \"organizationRoles\",\n    ORGANIZATION_TABLE_PAGE_SIZE,\n    ROLE_COLUMN_IDS\n  )\n  const { globalFilter, pagination } = tableState\n  useEffect(() => {\n    tableState.setColumnVisibility((current) =>\n      current.permissionResources === false\n        ? current\n        : { ...current, permissionResources: false }\n    )\n  }, [tableState.setColumnVisibility])\n  const table = useOrganizationTable(\n    {\n      atoms: tableState.atoms,\n      columns: roleColumns,\n      data: roles.data ?? EMPTY_ROLES,\n      enableRowSelection: canDelete.data?.success === true,\n      globalFilterFn: (row, _columnId, value) => {\n        const query = String(value).toLowerCase()\n        return (\n          row.original.role.toLowerCase().includes(query) ||\n          Object.entries(row.original.permission).some(\n            ([resource, actions]) =>\n              resource.toLowerCase().includes(query) ||\n              actions.some((action) => action.toLowerCase().includes(query))\n          )\n        )\n      },\n      getRowId: (role) => role.id\n    },\n    () => null\n  )\n  const deleteRoles = useDeleteRole(authClient, organizationId)\n  const permissionFilter = String(\n    table.getColumn(\"permissionResources\")?.getFilterValue() ?? \"all\"\n  )\n  const permissionFacetRows = table\n    .getColumn(\"permissionResources\")\n    ?.getFacetedRowModel().flatRows\n  const permissionResources = Array.from(\n    new Set(\n      (roles.data ?? EMPTY_ROLES).flatMap((role) =>\n        Object.keys(role.permission)\n      )\n    )\n  ).sort()\n  const selectedRoles = table.getSelectedRowModel().rows\n  const showSelection = canDelete.data?.success === true\n\n  async function deleteSelectedRoles() {\n    const results = await Promise.allSettled(\n      selectedRoles.map((row) =>\n        deleteRoles.mutateAsync({ roleId: row.original.id, organizationId })\n      )\n    )\n    const deletedCount = results.filter(\n      (result) => result.status === \"fulfilled\"\n    ).length\n\n    if (deletedCount > 0) {\n      toast.success(\n        localization.rolesDeleted.replace(\"{{count}}\", String(deletedCount))\n      )\n    }\n    table.resetRowSelection(true)\n  }\n\n  return (\n    <div className=\"flex flex-col gap-4\">\n      <div className=\"flex items-end justify-between gap-3\">\n        <div>\n          <h2 className=\"text-sm font-semibold\">{localization.roles}</h2>\n          <p className=\"text-sm text-muted-foreground\">\n            {localization.rolesDescription}\n          </p>\n        </div>\n        {(canCreate.isPending || canCreate.data?.success) && (\n          <Button\n            disabled={canCreate.isPending}\n            onClick={() => setEditingRole(null)}\n          >\n            <Plus />\n            {localization.createRole}\n          </Button>\n        )}\n      </div>\n\n      <div className=\"flex flex-wrap items-center gap-3\">\n        <InputGroup className=\"min-w-0 sm:w-[220px]\">\n          <InputGroupInput\n            aria-label={localization.search}\n            disabled={roles.isLoading}\n            onChange={(event) => table.setGlobalFilter(event.target.value)}\n            placeholder={localization.search}\n            type=\"search\"\n            value={globalFilter}\n          />\n          <InputGroupAddon>\n            <Search className=\"text-muted-foreground\" />\n          </InputGroupAddon>\n        </InputGroup>\n\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            className={cn(buttonVariants({ size: \"sm\", variant: \"outline\" }))}\n            disabled={roles.isLoading}\n          >\n            <Filter />\n            {localization.permissions}\n          </DropdownMenuTrigger>\n          <DropdownMenuContent align=\"start\">\n            <DropdownMenuRadioGroup\n              onValueChange={(value) =>\n                table\n                  .getColumn(\"permissionResources\")\n                  ?.setFilterValue(value === \"all\" ? undefined : value)\n              }\n              value={permissionFilter}\n            >\n              <DropdownMenuRadioItem value=\"all\">\n                {localization.all}\n              </DropdownMenuRadioItem>\n              {permissionResources.map((resource) => (\n                <DropdownMenuRadioItem key={resource} value={resource}>\n                  {dynamicAccessControl?.permissions[resource]?.label ??\n                    resource}{\" \"}\n                  (\n                  {permissionFacetRows?.filter((row) =>\n                    Object.hasOwn(row.original.permission, resource)\n                  ).length ?? 0}\n                  )\n                </DropdownMenuRadioItem>\n              ))}\n            </DropdownMenuRadioGroup>\n          </DropdownMenuContent>\n        </DropdownMenu>\n\n        <div className=\"ms-auto\">\n          <OrganizationTableViewOptions\n            columns={[\n              {\n                id: \"permissions\",\n                label: localization.permissions,\n                visible: table.getColumn(\"permissions\")?.getIsVisible() ?? true,\n                onVisibleChange: (visible) =>\n                  table.getColumn(\"permissions\")?.toggleVisibility(visible)\n              }\n            ]}\n            disabled={roles.isLoading}\n            localization={localization}\n          />\n        </div>\n      </div>\n\n      {permissionFilter !== \"all\" && (\n        <Badge className=\"w-fit gap-1\" variant=\"secondary\">\n          {dynamicAccessControl?.permissions[permissionFilter]?.label ??\n            permissionFilter}\n          <Button\n            aria-label={localization.clear}\n            className=\"size-4 rounded-sm text-muted-foreground\"\n            onClick={() =>\n              table.getColumn(\"permissionResources\")?.setFilterValue(undefined)\n            }\n            size=\"icon-xs\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            <X className=\"size-3\" />\n          </Button>\n        </Badge>\n      )}\n\n      {selectedRoles.length > 0 && (\n        <OrganizationTableBulkAction\n          actionLabel={localization.deleteSelectedRoles}\n          cancelLabel={authLocalization.settings.cancel}\n          count={selectedRoles.length}\n          description={localization.deleteSelectedRolesDescription}\n          isPending={deleteRoles.isPending}\n          onConfirm={deleteSelectedRoles}\n          selectedLabel={localization.selectedCount}\n        />\n      )}\n\n      {canRead.isPending || roles.isLoading ? (\n        <Spinner />\n      ) : !canRead.data?.success ? null : roles.data?.length ? (\n        <Card>\n          <CardContent className=\"p-0\">\n            <Table>\n              <TableHeader>\n                <TableRow>\n                  {showSelection && (\n                    <TableHead className=\"w-10\">\n                      <OrganizationTableSelectAll\n                        allSelected={table.getIsAllPageRowsSelected()}\n                        disabled={roles.isLoading}\n                        localization={localization}\n                        onCheckedChange={(checked) =>\n                          table.toggleAllPageRowsSelected(checked)\n                        }\n                        someSelected={table.getIsSomePageRowsSelected()}\n                      />\n                    </TableHead>\n                  )}\n                  <OrganizationSortableTableHead\n                    column={table.getColumn(\"role\")}\n                  >\n                    {localization.roleName}\n                  </OrganizationSortableTableHead>\n                  {table.getColumn(\"permissions\")?.getIsVisible() && (\n                    <OrganizationSortableTableHead\n                      column={table.getColumn(\"permissions\")}\n                    >\n                      {localization.permissions}\n                    </OrganizationSortableTableHead>\n                  )}\n                  <TableHead className=\"text-right\">\n                    {localization.actions}\n                  </TableHead>\n                </TableRow>\n              </TableHeader>\n              <TableBody>\n                {table.getRowModel().rows.map((row) => (\n                  <OrganizationRoleRow\n                    key={row.original.id}\n                    authClient={authClient}\n                    canDelete={canDelete.data?.success === true}\n                    canDeletePending={canDelete.isPending}\n                    canUpdate={canUpdate.data?.success === true}\n                    canUpdatePending={canUpdate.isPending}\n                    onEdit={() => setEditingRole(row.original)}\n                    organizationId={organizationId}\n                    role={row.original}\n                    selectableRow={showSelection ? row : undefined}\n                    showPermissions={\n                      table.getColumn(\"permissions\")?.getIsVisible() === true\n                    }\n                  />\n                ))}\n              </TableBody>\n            </Table>\n          </CardContent>\n        </Card>\n      ) : (\n        <Card>\n          <CardContent className=\"flex flex-col gap-1\">\n            <p className=\"text-sm font-medium\">{localization.noRoles}</p>\n            <p className=\"text-sm text-muted-foreground\">\n              {localization.noRolesDescription}\n            </p>\n          </CardContent>\n        </Card>\n      )}\n\n      <OrganizationTablePagination\n        canNextPage={table.getCanNextPage()}\n        canPreviousPage={table.getCanPreviousPage()}\n        disabled={roles.isLoading}\n        localization={localization}\n        onFirstPage={() => table.firstPage()}\n        onLastPage={() => table.lastPage()}\n        onNextPage={() => table.nextPage()}\n        onPageSizeChange={(pageSize) => table.setPageSize(pageSize)}\n        onPreviousPage={() => table.previousPage()}\n        pageCount={table.getPageCount()}\n        pageIndex={pagination.pageIndex}\n        pageSize={pagination.pageSize}\n        rowCount={table.getRowCount()}\n        visibleRowCount={table.getRowModel().rows.length}\n      />\n\n      <RoleDialog\n        organizationId={organizationId}\n        open={editingRole !== undefined}\n        role={editingRole ?? undefined}\n        registry={dynamicAccessControl?.permissions ?? {}}\n        roleFields={modelFields.role}\n        onOpenChange={(open) => !open && setEditingRole(undefined)}\n      />\n    </div>\n  )\n}\n\nfunction OrganizationRoleRow({\n  authClient,\n  canDelete,\n  canDeletePending,\n  canUpdate,\n  canUpdatePending,\n  onEdit,\n  organizationId,\n  role,\n  selectableRow,\n  showPermissions\n}: {\n  authClient: OrganizationRolesAuthClient\n  canDelete: boolean\n  canDeletePending: boolean\n  canUpdate: boolean\n  canUpdatePending: boolean\n  onEdit: () => void\n  organizationId: string\n  role: Role\n  selectableRow?: OrganizationSelectableRow<Role>\n  showPermissions: boolean\n}) {\n  const { localization: authLocalization } = useAuth()\n  const { localization } = useAuthPlugin(organizationPlugin)\n  const [deleteOpen, setDeleteOpen] = useState(false)\n  const deleteRole = useDeleteRole(authClient, organizationId, {\n    onSuccess: () => {\n      setDeleteOpen(false)\n      toast.success(localization.roleDeleted)\n    }\n  })\n  const assignments = useListOrganizationMembers(authClient, {\n    query: {\n      organizationId,\n      filterField: \"role\",\n      filterOperator: \"contains\",\n      filterValue: role.role,\n      limit: 1\n    },\n    enabled: Boolean(organizationId && canDelete)\n  })\n  const assignedCount =\n    assignments.data?.total ?? assignments.data?.members.length ?? 0\n  const assignmentUnknown = canDelete && !assignments.data\n  const deleteDisabled =\n    assignmentUnknown || assignedCount > 0 || deleteRole.isPending\n\n  return (\n    <TableRow\n      data-state={selectableRow?.getIsSelected() ? \"selected\" : undefined}\n    >\n      {selectableRow && (\n        <TableCell>\n          <OrganizationTableSelectRow\n            localization={localization}\n            row={selectableRow}\n          />\n        </TableCell>\n      )}\n      <TableCell className=\"font-medium\">{role.role}</TableCell>\n      {showPermissions && (\n        <TableCell>\n          {Object.values(role.permission).reduce(\n            (total, actions) => total + actions.length,\n            0\n          )}\n        </TableCell>\n      )}\n      <TableCell>\n        <div className=\"flex justify-end gap-1\">\n          {canUpdatePending && (\n            <Button\n              aria-label={localization.editRole}\n              disabled\n              size=\"icon\"\n              variant=\"ghost\"\n            >\n              <Pencil />\n            </Button>\n          )}\n          {canUpdate && (\n            <Button\n              size=\"icon\"\n              variant=\"ghost\"\n              onClick={onEdit}\n              aria-label={localization.editRole}\n            >\n              <Pencil />\n            </Button>\n          )}\n          {canDelete && (\n            <AlertDialog\n              open={deleteOpen}\n              onOpenChange={(open) => {\n                if (!deleteRole.isPending) setDeleteOpen(open)\n              }}\n            >\n              <AlertDialogTrigger\n                className={buttonVariants({\n                  size: \"icon\",\n                  variant: \"ghost\",\n                  className: \"text-destructive\"\n                })}\n                disabled={deleteDisabled}\n                title={\n                  assignedCount > 0\n                    ? localization.roleInUse.replace(\n                        \"{{count}}\",\n                        String(assignedCount)\n                      )\n                    : localization.deleteRole\n                }\n                aria-label={localization.deleteRole}\n              >\n                <Trash2 />\n              </AlertDialogTrigger>\n              <AlertDialogContent>\n                <AlertDialogHeader>\n                  <AlertDialogMedia>\n                    <Trash2 />\n                  </AlertDialogMedia>\n                  <AlertDialogTitle>{localization.deleteRole}</AlertDialogTitle>\n                  <AlertDialogDescription>\n                    {localization.deleteRoleDescription}\n                  </AlertDialogDescription>\n                </AlertDialogHeader>\n                <p className=\"break-words text-sm font-medium\">{role.role}</p>\n                <AlertDialogFooter>\n                  <AlertDialogCancel disabled={deleteRole.isPending}>\n                    {authLocalization.settings.cancel}\n                  </AlertDialogCancel>\n                  <Button\n                    variant=\"destructive\"\n                    disabled={deleteDisabled}\n                    onClick={() =>\n                      deleteRole.mutate({ roleId: role.id, organizationId })\n                    }\n                  >\n                    {deleteRole.isPending && <Spinner />}\n                    {localization.deleteRole}\n                  </Button>\n                </AlertDialogFooter>\n              </AlertDialogContent>\n            </AlertDialog>\n          )}\n          {canDeletePending && (\n            <Button\n              aria-label={localization.deleteRole}\n              className=\"text-destructive\"\n              disabled\n              size=\"icon\"\n              variant=\"ghost\"\n            >\n              <Trash2 />\n            </Button>\n          )}\n        </div>\n      </TableCell>\n    </TableRow>\n  )\n}\n\nfunction RoleDialog({\n  onOpenChange,\n  open,\n  organizationId,\n  registry,\n  role,\n  roleFields\n}: {\n  onOpenChange: (open: boolean) => void\n  open: boolean\n  organizationId: string\n  registry: Record<string, { label?: string; actions: Record<string, string> }>\n  role?: Role\n  roleFields: AdditionalFields\n}) {\n  const { authClient, localization: authLocalization } =\n    useAuth<OrganizationRolesAuthClient>()\n  const { localization } = useAuthPlugin(organizationPlugin)\n  const createRole = useCreateRole(authClient, organizationId, {\n    onSuccess: () => {\n      toast.success(localization.roleCreated)\n      onOpenChange(false)\n    }\n  })\n  const updateRole = useUpdateRole(authClient, organizationId, {\n    onSuccess: () => {\n      toast.success(localization.roleUpdated)\n      onOpenChange(false)\n    }\n  })\n\n  const configuredRoleFields = useMemo(\n    () => fieldsWithModelValues(roleFields, role ?? {}),\n    [role, roleFields]\n  )\n  const form = useAuthForm({\n    defaultValues: {\n      additionalFields: getAdditionalFieldDefaultValues(configuredRoleFields),\n      name: role?.role ?? \"\",\n      permission: role?.permission ?? ({} as Record<string, string[]>)\n    },\n    onSubmit: async ({ value }) => {\n      const roleName = value.name.trim()\n      if (!roleName) return\n\n      try {\n        if (\n          Object.values(value.permission).some((actions) => actions.length > 0)\n        ) {\n          const access = await authClient.organization.hasPermission({\n            organizationId,\n            permissions: value.permission as Parameters<\n              OrganizationRolesAuthClient[\"organization\"][\"hasPermission\"]\n            >[0][\"permissions\"]\n          })\n\n          if (access.error || !access.data?.success) {\n            toast.error(localization.permissionsLimitedDescription)\n            return\n          }\n        }\n\n        const additionalFields = getAdditionalFieldSubmitValues(\n          configuredRoleFields,\n          value.additionalFields\n        )\n        if (role) {\n          await updateRole.mutateAsync({\n            organizationId,\n            roleId: role.id,\n            data: {\n              ...additionalFields,\n              roleName,\n              permission: value.permission\n            }\n          })\n        } else {\n          await createRole.mutateAsync({\n            organizationId,\n            role: roleName,\n            permission: value.permission,\n            additionalFields\n          })\n        }\n      } catch {\n        // The mutation reports the error through its configured handler.\n      }\n    }\n  })\n\n  useEffect(() => {\n    if (!open) return\n    form.reset({\n      additionalFields: getAdditionalFieldDefaultValues(configuredRoleFields),\n      name: role?.role ?? \"\",\n      permission: role?.permission ?? {}\n    })\n  }, [configuredRoleFields, form, open, role?.permission, role?.role])\n\n  const pending = createRole.isPending || updateRole.isPending\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n        <form.AppForm>\n          <form.AuthFormRoot className=\"flex flex-col gap-6\">\n            <DialogHeader>\n              <DialogTitle>\n                {role ? localization.editRole : localization.createRole}\n              </DialogTitle>\n              <DialogDescription>\n                {localization.rolesDescription}\n              </DialogDescription>\n            </DialogHeader>\n\n            <form.AppField\n              name=\"name\"\n              validators={{\n                onChange: ({ value }) =>\n                  validateStringLength(value, {\n                    requiredMessage: authLocalization.auth.fieldRequired,\n                    trim: true\n                  })\n              }}\n            >\n              {(field) => {\n                const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n                return (\n                  <Field data-invalid={isInvalid}>\n                    <FieldLabel htmlFor=\"organization-role-name\">\n                      {localization.roleName}\n                    </FieldLabel>\n                    <Input\n                      aria-invalid={isInvalid}\n                      disabled={pending}\n                      id=\"organization-role-name\"\n                      name={field.name}\n                      onBlur={field.handleBlur}\n                      onChange={(event) =>\n                        field.handleChange(event.target.value)\n                      }\n                      placeholder={localization.roleNamePlaceholder}\n                      value={field.state.value}\n                    />\n                    <field.AuthFormFieldError />\n                  </Field>\n                )\n              }}\n            </form.AppField>\n\n            {configuredRoleFields.map((configuredField) => (\n              <form.AppField\n                key={configuredField.name}\n                name={`additionalFields.${configuredField.name}`}\n                validators={getAuthAdditionalFieldValidators(\n                  configuredField,\n                  authLocalization.auth.fieldRequired\n                )}\n              >\n                {(field) => (\n                  <field.AuthFormAdditionalField\n                    field={configuredField}\n                    isPending={pending}\n                    optionalLabel={authLocalization.settings.optional}\n                  />\n                )}\n              </form.AppField>\n            ))}\n\n            <form.AppField name=\"permission\">\n              {(field) => (\n                <fieldset className=\"flex flex-col gap-4\">\n                  <legend className=\"text-sm font-medium\">\n                    {localization.permissions}\n                  </legend>\n                  {Object.entries(registry).map(([resource, definition]) => (\n                    <div className=\"flex flex-col gap-2\" key={resource}>\n                      <p className=\"text-sm font-medium\">\n                        {definition.label ?? resource}\n                      </p>\n                      <div className=\"grid gap-3 sm:grid-cols-2\">\n                        {Object.entries(definition.actions).map(\n                          ([action, label]) => (\n                            <RolePermissionCheckbox\n                              action={action}\n                              checked={\n                                field.state.value[resource]?.includes(action) ??\n                                false\n                              }\n                              key={action}\n                              label={label}\n                              onCheckedChange={(selected) =>\n                                field.handleChange({\n                                  ...field.state.value,\n                                  [resource]: selected\n                                    ? [\n                                        ...(field.state.value[resource] ?? []),\n                                        action\n                                      ]\n                                    : (\n                                        field.state.value[resource] ?? []\n                                      ).filter((entry) => entry !== action)\n                                })\n                              }\n                              organizationId={organizationId}\n                              pending={pending}\n                              resource={resource}\n                            />\n                          )\n                        )}\n                      </div>\n                    </div>\n                  ))}\n                </fieldset>\n              )}\n            </form.AppField>\n\n            <DialogFooter>\n              <Button\n                type=\"button\"\n                variant=\"outline\"\n                disabled={pending}\n                onClick={() => onOpenChange(false)}\n              >\n                {authLocalization.settings.cancel}\n              </Button>\n              <form.AuthFormSubmitButton disabled={pending}>\n                {authLocalization.settings.saveChanges}\n              </form.AuthFormSubmitButton>\n            </DialogFooter>\n          </form.AuthFormRoot>\n        </form.AppForm>\n      </DialogContent>\n    </Dialog>\n  )\n}\n\nfunction RolePermissionCheckbox({\n  action,\n  checked,\n  label,\n  onCheckedChange,\n  organizationId,\n  pending,\n  resource\n}: {\n  action: string\n  checked: boolean\n  label: string\n  onCheckedChange: (checked: boolean) => void\n  organizationId: string\n  pending: boolean\n  resource: string\n}) {\n  const { authClient } = useAuth<OrganizationRolesAuthClient>()\n  const canAssign = useHasPermission(authClient, {\n    organizationId,\n    permissions: { [resource]: [action] } as Parameters<\n      OrganizationRolesAuthClient[\"organization\"][\"hasPermission\"]\n    >[0][\"permissions\"]\n  })\n  const id = `role-permission-${resource}-${action}`\n  const disabled =\n    pending || canAssign.isPending || (!checked && !canAssign.data?.success)\n\n  return (\n    <label\n      className=\"flex items-center gap-2 text-sm\"\n      data-disabled={disabled || undefined}\n      htmlFor={id}\n    >\n      <Checkbox\n        checked={checked}\n        disabled={disabled}\n        id={id}\n        onCheckedChange={(selected) => onCheckedChange(selected === true)}\n      />\n      {label}\n    </label>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-roles.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-row.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useSetActiveOrganization } from \"@better-auth-ui/react/plugins/organization\"\nimport type { Organization } from \"better-auth/client\"\nimport { Settings as SettingsIcon } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Item, ItemActions } from \"@/components/ui/item\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { OrganizationView } from \"./organization-view\"\n\nexport type OrganizationRowProps = {\n  organization: Organization\n}\n\n/**\n * Single organization row: logo and labels via `OrganizationView`, plus a Manage action.\n */\nexport function OrganizationRow({ organization }: OrganizationRowProps) {\n  const { authClient, basePaths, navigate } = useAuth<OrganizationAuthClient>()\n  const {\n    localization: organizationLocalization,\n    viewPaths: organizationViewPaths,\n    slug,\n    slugPrefix\n  } = useAuthPlugin(organizationPlugin)\n\n  const { mutate: setActiveOrganization, isPending: setActivePending } =\n    useSetActiveOrganization(authClient, {\n      onSuccess: () => {\n        navigate({\n          to: `${basePaths.organization}/${organizationViewPaths.organization.settings}`\n        })\n      }\n    })\n\n  function manageOrganization() {\n    if (slug !== undefined) {\n      navigate({\n        to: `${basePaths.organization}/${slugPrefix}${organization.slug}/${organizationViewPaths.organization.settings}`\n      })\n    } else {\n      setActiveOrganization({ organizationId: organization.id })\n    }\n  }\n\n  return (\n    <Item>\n      <OrganizationView className=\"flex-1\" organization={organization} />\n      <ItemActions>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          disabled={setActivePending}\n          onClick={manageOrganization}\n          aria-label={organizationLocalization.manage}\n        >\n          {setActivePending ? <Spinner /> : <SettingsIcon />}\n\n          {organizationLocalization.manage}\n        </Button>\n      </ItemActions>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-row.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-settings.tsx",
      "content": "\"use client\"\n\nimport { getOrganizationCardKey, useAuth } from \"@better-auth-ui/react\"\nimport type { ComponentProps } from \"react\"\n\nimport { cn } from \"cn\"\nimport { OrganizationDangerZone } from \"./organization-danger-zone\"\nimport { OrganizationProfile } from \"./organization-profile\"\n\nexport type OrganizationSettingsProps = {\n  className?: string\n  organizationId: string\n  organizationSlug: string\n}\n\n/**\n * Organization settings UI: profile card, plugin-contributed cards\n * (`organizationCards`), then danger zone.\n */\nexport function OrganizationSettings({\n  className,\n  organizationId,\n  organizationSlug,\n  ...props\n}: OrganizationSettingsProps & ComponentProps<\"div\">) {\n  const { plugins } = useAuth()\n\n  return (\n    <div className={cn(\"flex flex-col gap-4 md:gap-6\", className)} {...props}>\n      <OrganizationProfile />\n\n      {plugins.flatMap((plugin) =>\n        plugin.organizationCards?.map((Card) => (\n          <Card\n            key={getOrganizationCardKey(plugin.id, Card)}\n            organizationId={organizationId}\n            organizationSlug={organizationSlug}\n          />\n        ))\n      )}\n\n      <OrganizationDangerZone />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-settings.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-sortable-table-head.tsx",
      "content": "\"use client\"\n\nimport { type Column, type RowData, Subscribe } from \"@tanstack/react-table\"\nimport { ChevronUp } from \"lucide-react\"\nimport type { MouseEvent, ReactNode } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { TableHead } from \"@/components/ui/table\"\nimport { cn } from \"cn\"\nimport type { organizationTableFeatures } from \"./organization-table\"\n\nexport function OrganizationSortableTableHead<TData extends RowData>({\n  children,\n  column\n}: {\n  children: ReactNode\n  column?: Column<typeof organizationTableFeatures, TData>\n}) {\n  if (!column) return <TableHead>{children}</TableHead>\n\n  const onClick = column.getToggleSortingHandler()\n\n  return (\n    <Subscribe\n      source={column.table.atoms.sorting}\n      selector={() => [column.getIsSorted(), column.getSortIndex()] as const}\n    >\n      {([sortDirection, sortIndex]) => (\n        <TableHead\n          aria-sort={\n            sortDirection === \"asc\"\n              ? \"ascending\"\n              : sortDirection === \"desc\"\n                ? \"descending\"\n                : \"none\"\n          }\n        >\n          <Button\n            className=\"h-auto w-full justify-start p-0 font-medium hover:bg-transparent\"\n            onClick={(event: MouseEvent<HTMLButtonElement>) => onClick?.(event)}\n            size=\"sm\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            {children}\n\n            {sortDirection ? (\n              <>\n                <ChevronUp\n                  className={cn(\n                    \"size-3 transition-transform duration-100 ease-out\",\n                    sortDirection === \"desc\" && \"rotate-180\"\n                  )}\n                />\n                <span className=\"text-muted-foreground text-[10px] tabular-nums\">\n                  {sortIndex + 1}\n                </span>\n              </>\n            ) : null}\n          </Button>\n        </TableHead>\n      )}\n    </Subscribe>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-sortable-table-head.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-switcher.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin, useSession } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useListOrganizations,\n  useSetActiveOrganization\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Organization } from \"better-auth/client\"\nimport {\n  ChevronsUpDown,\n  PlusCircle,\n  Settings as SettingsIcon\n} from \"lucide-react\"\nimport { type ComponentProps, type ReactElement, useState } from \"react\"\n\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport { UserView } from \"../user/user-view\"\nimport { CreateOrganizationDialog } from \"./create-organization-dialog\"\nimport { OrganizationView } from \"./organization-view\"\n\n/** Props for the `OrganizationSwitcher` component. */\nexport type OrganizationSwitcherProps = {\n  className?: string\n  align?: \"center\" | \"end\" | \"start\"\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n  sideOffset?: number\n  trigger?: ReactElement<ComponentProps<typeof DropdownMenuTrigger>>\n  hideCreate?: boolean\n  hidePersonal?: boolean\n  hideSettings?: boolean\n  hideSlug?: boolean\n  setActive?: (organization: Organization | null) => void\n}\n\n/**\n * Renders an organizations dropdown with a trigger button,\n * header summary, and a menu of organizations to switch to.\n */\nexport function OrganizationSwitcher({\n  className,\n  align,\n  side,\n  sideOffset,\n  hideCreate,\n  hidePersonal,\n  hideSettings,\n  hideSlug: hideSlugProp,\n  setActive,\n  trigger\n}: OrganizationSwitcherProps) {\n  const { authClient, navigate, basePaths, localization, viewPaths, Link } =\n    useAuth<OrganizationAuthClient>()\n  const { data: session, isPending: sessionPending } = useSession(authClient)\n  const {\n    localization: organizationLocalization,\n    viewPaths: organizationViewPaths,\n    slug,\n    slugPrefix,\n    hideSlug: pluginHideSlug\n  } = useAuthPlugin(organizationPlugin)\n  const hideSlug = hideSlugProp ?? pluginHideSlug ?? true\n\n  const { data: activeOrganization, isPending: activeOrganizationPending } =\n    useActiveOrganization(authClient)\n\n  const { data: organizations, isPending: organizationsPending } =\n    useListOrganizations(authClient)\n\n  const { mutate: setActiveOrganization } = useSetActiveOrganization(authClient)\n\n  const isPending =\n    sessionPending ||\n    (!!session && (organizationsPending || activeOrganizationPending))\n\n  const [createOpen, setCreateOpen] = useState(false)\n  const [dropdownOpen, setDropdownOpen] = useState(false)\n\n  const otherOrganizations =\n    organizations?.filter(\n      (organization) => organization.id !== activeOrganization?.id\n    ) ?? []\n\n  const hasOtherEntries =\n    otherOrganizations.length > 0 || (!!activeOrganization && !hidePersonal)\n\n  function handleSetActive(organization: Organization | null) {\n    setDropdownOpen(false)\n\n    if (setActive) {\n      setActive(organization)\n    } else if (slug !== undefined) {\n      navigate({\n        to: organization\n          ? `${basePaths.organization}/${slugPrefix}${organization.slug}/${organizationViewPaths.organization.settings}`\n          : `${basePaths.settings}/${viewPaths.settings.account}`\n      })\n    } else {\n      setActiveOrganization({ organizationId: organization?.id ?? null })\n    }\n  }\n\n  return (\n    <>\n      <DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>\n        {trigger ?? (\n          <DropdownMenuTrigger\n            className={cn(\n              buttonVariants({ variant: \"ghost\" }),\n              \"h-auto px-2 py-2 text-left\",\n              className\n            )}\n            disabled={!session || isPending}\n          >\n            {isPending ? (\n              <OrganizationView isPending hideRole hideSlug={hideSlug} />\n            ) : activeOrganization ? (\n              <OrganizationView hideRole hideSlug={hideSlug} />\n            ) : session && !hidePersonal ? (\n              <UserView hideSubtitle={hideSlug} />\n            ) : (\n              <OrganizationView\n                hideRole\n                hideSlug={hideSlug}\n                organization={{ name: organizationLocalization.organization }}\n              />\n            )}\n\n            <ChevronsUpDown className=\"size-4 shrink-0 text-muted-foreground\" />\n          </DropdownMenuTrigger>\n        )}\n\n        <DropdownMenuContent\n          align={align}\n          side={side}\n          sideOffset={sideOffset}\n          className=\"min-w-64 max-w-svw\"\n        >\n          {activeOrganization ? (\n            <div className=\"flex items-center justify-between gap-4 px-2 py-2\">\n              <OrganizationView\n                hideRole\n                hideSlug={hideSlug}\n                organization={activeOrganization}\n              />\n\n              {!hideSettings && (\n                <Link\n                  href={\n                    slug\n                      ? `${basePaths.organization}/${slugPrefix}${slug}/${organizationViewPaths.organization.settings}`\n                      : `${basePaths.organization}/${organizationViewPaths.organization.settings}`\n                  }\n                  className={cn(\n                    buttonVariants({ variant: \"outline\", size: \"sm\" })\n                  )}\n                >\n                  <SettingsIcon className=\"text-muted-foreground\" />\n\n                  {organizationLocalization.manage}\n                </Link>\n              )}\n            </div>\n          ) : !isPending && session?.user && !hidePersonal ? (\n            <div className=\"flex items-center justify-between gap-4 px-2 py-2\">\n              <UserView hideSubtitle={hideSlug} />\n\n              {!hideSettings && (\n                <Link\n                  href={`${basePaths.settings}/${viewPaths.settings.account}`}\n                  className={cn(\n                    buttonVariants({ variant: \"outline\", size: \"sm\" })\n                  )}\n                >\n                  <SettingsIcon className=\"text-muted-foreground\" />\n\n                  {localization.settings.settings}\n                </Link>\n              )}\n            </div>\n          ) : null}\n\n          <DropdownMenuSeparator />\n\n          {!!activeOrganization && !hidePersonal && (\n            <DropdownMenuItem onClick={() => handleSetActive(null)}>\n              <UserView hideSubtitle={hideSlug} />\n            </DropdownMenuItem>\n          )}\n\n          {otherOrganizations.map((organization) => (\n            <DropdownMenuItem\n              key={organization.id}\n              onClick={() => handleSetActive(organization)}\n            >\n              <OrganizationView\n                hideRole\n                hideSlug={hideSlug}\n                organization={organization}\n              />\n            </DropdownMenuItem>\n          ))}\n\n          {!hideCreate && (\n            <>\n              {hasOtherEntries && <DropdownMenuSeparator />}\n\n              <DropdownMenuItem\n                onClick={() => {\n                  setDropdownOpen(false)\n                  setCreateOpen(true)\n                }}\n              >\n                <PlusCircle className=\"text-muted-foreground\" />\n\n                {organizationLocalization.createOrganization}\n              </DropdownMenuItem>\n            </>\n          )}\n        </DropdownMenuContent>\n      </DropdownMenu>\n\n      <CreateOrganizationDialog\n        hideSlug={hideSlug}\n        open={createOpen}\n        onOpenChange={setCreateOpen}\n      />\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-switcher.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-table-bulk-action.tsx",
      "content": "\"use client\"\n\nimport { Trash2 } from \"lucide-react\"\nimport { useState } from \"react\"\n\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogMedia,\n  AlertDialogTitle,\n  AlertDialogTrigger\n} from \"@/components/ui/alert-dialog\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { Spinner } from \"@/components/ui/spinner\"\n\nexport function OrganizationTableBulkAction({\n  actionLabel,\n  cancelLabel,\n  count,\n  description,\n  disabled,\n  isPending,\n  onConfirm,\n  selectedLabel\n}: {\n  actionLabel: string\n  cancelLabel: string\n  count: number\n  description: string\n  disabled?: boolean\n  isPending: boolean\n  onConfirm: () => Promise<void>\n  selectedLabel: string\n}) {\n  const [open, setOpen] = useState(false)\n\n  return (\n    <div className=\"flex flex-wrap items-center gap-2\">\n      <span className=\"text-muted-foreground text-sm tabular-nums\">\n        {selectedLabel.replace(\"{{count}}\", String(count))}\n      </span>\n      <AlertDialog\n        open={open}\n        onOpenChange={(nextOpen) => !isPending && setOpen(nextOpen)}\n      >\n        <AlertDialogTrigger\n          className={buttonVariants({ size: \"sm\", variant: \"destructive\" })}\n          disabled={disabled || count === 0}\n        >\n          <Trash2 data-icon=\"inline-start\" />\n          {actionLabel}\n        </AlertDialogTrigger>\n        <AlertDialogContent>\n          <AlertDialogHeader>\n            <AlertDialogMedia>\n              <Trash2 />\n            </AlertDialogMedia>\n            <AlertDialogTitle>{actionLabel}</AlertDialogTitle>\n            <AlertDialogDescription>{description}</AlertDialogDescription>\n          </AlertDialogHeader>\n          <AlertDialogFooter>\n            <AlertDialogCancel disabled={isPending}>\n              {cancelLabel}\n            </AlertDialogCancel>\n            <Button\n              disabled={isPending}\n              onClick={async () => {\n                await onConfirm()\n                setOpen(false)\n              }}\n              variant=\"destructive\"\n            >\n              {isPending && <Spinner data-icon=\"inline-start\" />}\n              {actionLabel}\n            </Button>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-table-bulk-action.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-table-pagination.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationLocalization } from \"@better-auth-ui/core/plugins/organization\"\nimport {\n  ChevronLeft,\n  ChevronRight,\n  ChevronsLeft,\n  ChevronsRight\n} from \"lucide-react\"\n\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuRadioGroup,\n  DropdownMenuRadioItem,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { cn } from \"cn\"\nimport { ORGANIZATION_TABLE_PAGE_SIZE_OPTIONS } from \"./organization-table-state\"\n\nexport function OrganizationTablePagination({\n  disabled,\n  onFirstPage,\n  onLastPage,\n  onNextPage,\n  onPageSizeChange,\n  onPreviousPage,\n  pageCount,\n  pageIndex,\n  pageSize,\n  rowCount,\n  visibleRowCount,\n  canNextPage,\n  canPreviousPage,\n  localization\n}: {\n  disabled?: boolean\n  onFirstPage: () => void\n  onLastPage: () => void\n  onNextPage: () => void\n  onPageSizeChange: (pageSize: number) => void\n  onPreviousPage: () => void\n  pageCount: number\n  pageIndex: number\n  pageSize: number\n  rowCount: number\n  visibleRowCount: number\n  canNextPage: boolean\n  canPreviousPage: boolean\n  localization: OrganizationLocalization\n}) {\n  if (rowCount === 0) return null\n\n  const pageStart = pageIndex * pageSize\n  const from = pageStart + 1\n  const to = Math.min(pageStart + visibleRowCount, rowCount)\n\n  return (\n    <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n      <p className=\"text-muted-foreground text-sm tabular-nums\">\n        {localization.paginationRange\n          .replace(\"{{from}}\", String(from))\n          .replace(\"{{to}}\", String(to))\n          .replace(\"{{total}}\", String(rowCount))}\n      </p>\n\n      <div className=\"flex flex-wrap items-center gap-2\">\n        <DropdownMenu>\n          <DropdownMenuTrigger\n            className={cn(buttonVariants({ size: \"sm\", variant: \"outline\" }))}\n            disabled={disabled}\n          >\n            {localization.rowsPerPage}: {pageSize}\n          </DropdownMenuTrigger>\n          <DropdownMenuContent align=\"end\">\n            <DropdownMenuRadioGroup\n              value={String(pageSize)}\n              onValueChange={(value) => onPageSizeChange(Number(value))}\n            >\n              {Array.from(\n                new Set([...ORGANIZATION_TABLE_PAGE_SIZE_OPTIONS, pageSize])\n              )\n                .sort((left, right) => left - right)\n                .map((size) => (\n                  <DropdownMenuRadioItem key={size} value={String(size)}>\n                    {size}\n                  </DropdownMenuRadioItem>\n                ))}\n            </DropdownMenuRadioGroup>\n          </DropdownMenuContent>\n        </DropdownMenu>\n\n        <span className=\"text-muted-foreground text-sm tabular-nums\">\n          {localization.pageOf\n            .replace(\"{{page}}\", String(pageIndex + 1))\n            .replace(\"{{pages}}\", String(Math.max(1, pageCount)))}\n        </span>\n\n        <Button\n          aria-label={localization.firstPage}\n          size=\"icon-sm\"\n          variant=\"outline\"\n          disabled={disabled || !canPreviousPage}\n          onClick={onFirstPage}\n        >\n          <ChevronsLeft />\n        </Button>\n\n        <Button\n          aria-label={localization.previousPage}\n          size=\"icon-sm\"\n          variant=\"outline\"\n          disabled={disabled || !canPreviousPage}\n          onClick={onPreviousPage}\n        >\n          <ChevronLeft />\n        </Button>\n\n        <Button\n          aria-label={localization.nextPage}\n          size=\"icon-sm\"\n          variant=\"outline\"\n          disabled={disabled || !canNextPage}\n          onClick={onNextPage}\n        >\n          <ChevronRight />\n        </Button>\n\n        <Button\n          aria-label={localization.lastPage}\n          size=\"icon-sm\"\n          variant=\"outline\"\n          disabled={disabled || !canNextPage}\n          onClick={onLastPage}\n        >\n          <ChevronsRight />\n        </Button>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-table-pagination.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-table-selection.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationLocalization } from \"@better-auth-ui/core/plugins/organization\"\nimport { type Row, type RowData, Subscribe } from \"@tanstack/react-table\"\nimport type { MouseEvent } from \"react\"\n\nimport { Checkbox } from \"@/components/ui/checkbox\"\nimport type { organizationTableFeatures } from \"./organization-table\"\n\nexport type OrganizationSelectableRow<TData extends RowData> = Row<\n  typeof organizationTableFeatures,\n  TData\n>\n\nexport function OrganizationTableSelectAll({\n  allSelected,\n  disabled,\n  onCheckedChange,\n  someSelected,\n  localization\n}: {\n  allSelected: boolean\n  disabled?: boolean\n  onCheckedChange: (checked: boolean) => void\n  someSelected: boolean\n  localization: OrganizationLocalization\n}) {\n  return (\n    <Checkbox\n      aria-label={localization.selectAllRows}\n      aria-checked={someSelected && !allSelected ? \"mixed\" : allSelected}\n      checked={allSelected}\n      disabled={disabled}\n      onCheckedChange={(checked) => onCheckedChange(checked === true)}\n    />\n  )\n}\n\nexport function OrganizationTableSelectRow<TData extends RowData>({\n  disabled,\n  localization,\n  row\n}: {\n  disabled?: boolean\n  localization: OrganizationLocalization\n  row: OrganizationSelectableRow<TData>\n}) {\n  return (\n    <Subscribe\n      source={row.table.atoms.rowSelection}\n      selector={(selection) => selection[row.id] === true}\n    >\n      {(selected) => (\n        <Checkbox\n          aria-label={localization.selectRow}\n          checked={selected}\n          disabled={disabled || !row.getCanSelect()}\n          onClick={(event: MouseEvent<HTMLElement>) =>\n            row.getToggleSelectedHandler()({\n              nativeEvent: event.nativeEvent,\n              shiftKey: event.shiftKey,\n              target: { checked: !selected }\n            })\n          }\n        />\n      )}\n    </Subscribe>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-table-selection.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-table-state.ts",
      "content": "\"use client\"\n\nimport {\n  createBrowserTablePersistenceAdapters,\n  parseTableColumnVisibility,\n  parseTableUrlState,\n  serializeTableColumnVisibility,\n  serializeTableUrlState,\n  type TablePersistenceAdapters\n} from \"@better-auth-ui/core\"\nimport { useCreateAtom, useSelector } from \"@tanstack/react-store\"\nimport {\n  type ColumnFiltersState,\n  type ColumnVisibilityState,\n  functionalUpdate,\n  type PaginationState,\n  type RowSelectionState,\n  type SortingState,\n  type Updater\n} from \"@tanstack/react-table\"\nimport {\n  type MutableRefObject,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState\n} from \"react\"\nimport { ORGANIZATION_TABLE_PAGE_SIZE } from \"./organization-table\"\n\nconst TABLE_STATE_STORAGE_PREFIX = \"better-auth-ui:organization-table\"\n\ntype OrganizationTableUrlState = {\n  columnFilters: ColumnFiltersState\n  globalFilter: string\n  pagination: PaginationState\n  sorting: SortingState\n}\n\nfunction readColumnVisibility(\n  adapters: TablePersistenceAdapters,\n  stateKey: string,\n  allowedColumnIds?: readonly string[]\n): ColumnVisibilityState {\n  try {\n    const value =\n      adapters.storage?.read(\n        `${TABLE_STATE_STORAGE_PREFIX}:${stateKey}:columns`\n      ) ?? null\n    return parseTableColumnVisibility(value, allowedColumnIds)\n  } catch {\n    return {}\n  }\n}\n\nfunction writeUrlState(\n  adapters: TablePersistenceAdapters,\n  stateKey: string,\n  defaultPageSize: number,\n  state: OrganizationTableUrlState,\n  syncedSearch: MutableRefObject<string>\n) {\n  const next = serializeTableUrlState(\n    adapters.search.read(),\n    stateKey,\n    defaultPageSize,\n    state\n  )\n  const nextSearch = next.toString()\n  if (nextSearch === syncedSearch.current) return\n\n  syncedSearch.current = nextSearch\n  adapters.search.replace(next)\n}\n\nexport function useOrganizationTableState(\n  stateKey: string,\n  defaultPageSize = ORGANIZATION_TABLE_PAGE_SIZE,\n  allowedColumnIds?: readonly string[],\n  persistenceAdapters?: TablePersistenceAdapters\n) {\n  const allowedColumnIdsKey = allowedColumnIds?.join(\"\\u0000\")\n  const stableAllowedColumnIds = useMemo(\n    () => allowedColumnIdsKey?.split(\"\\u0000\"),\n    [allowedColumnIdsKey]\n  )\n  const adapters = useMemo(\n    () => persistenceAdapters ?? createBrowserTablePersistenceAdapters(),\n    [persistenceAdapters]\n  )\n  const urlStateToken = useMemo(\n    () => ({ adapters, defaultPageSize, stableAllowedColumnIds, stateKey }),\n    [adapters, defaultPageSize, stableAllowedColumnIds, stateKey]\n  )\n  const columnFiltersAtom = useCreateAtom<ColumnFiltersState>([])\n  const columnVisibilityAtom = useCreateAtom<ColumnVisibilityState>({})\n  const globalFilterAtom = useCreateAtom(\"\")\n  const paginationAtom = useCreateAtom<PaginationState>({\n    pageIndex: 0,\n    pageSize: defaultPageSize\n  })\n  const rowSelectionAtom = useCreateAtom<RowSelectionState>({})\n  const sortingAtom = useCreateAtom<SortingState>([])\n  const columnFilters = useSelector(columnFiltersAtom)\n  const columnVisibility = useSelector(columnVisibilityAtom)\n  const globalFilter = useSelector(globalFilterAtom)\n  const pagination = useSelector(paginationAtom)\n  const rowSelection = useSelector(rowSelectionAtom)\n  const sorting = useSelector(sortingAtom)\n  const [restoredUrlStateToken, setRestoredUrlStateToken] =\n    useState<typeof urlStateToken>()\n  const [visibilityReady, setVisibilityReady] = useState(false)\n  const urlReady = restoredUrlStateToken === urlStateToken\n  const restoringUrlState = useRef(false)\n  const syncedSearch = useRef(\"\")\n  const atoms = useMemo(\n    () => ({\n      columnFilters: columnFiltersAtom,\n      columnVisibility: columnVisibilityAtom,\n      globalFilter: globalFilterAtom,\n      pagination: paginationAtom,\n      rowSelection: rowSelectionAtom,\n      sorting: sortingAtom\n    }),\n    [\n      columnFiltersAtom,\n      columnVisibilityAtom,\n      globalFilterAtom,\n      paginationAtom,\n      rowSelectionAtom,\n      sortingAtom\n    ]\n  )\n\n  const setPagination = useCallback(\n    (updater: Updater<PaginationState>) => {\n      paginationAtom.set((current) => functionalUpdate(updater, current))\n    },\n    [paginationAtom]\n  )\n  const setColumnVisibility = useCallback(\n    (updater: Updater<ColumnVisibilityState>) => {\n      columnVisibilityAtom.set((current) => functionalUpdate(updater, current))\n    },\n    [columnVisibilityAtom]\n  )\n  const setColumnFilters = useCallback(\n    (updater: Updater<ColumnFiltersState>) => {\n      columnFiltersAtom.set((current) => functionalUpdate(updater, current))\n    },\n    [columnFiltersAtom]\n  )\n  const setGlobalFilter = useCallback(\n    (updater: Updater<string>) => {\n      globalFilterAtom.set((current) => functionalUpdate(updater, current))\n    },\n    [globalFilterAtom]\n  )\n  const setSorting = useCallback(\n    (updater: Updater<SortingState>) => {\n      sortingAtom.set((current) => functionalUpdate(updater, current))\n    },\n    [sortingAtom]\n  )\n\n  useEffect(() => {\n    const resetPage = () => {\n      if (restoringUrlState.current) return\n      const current = paginationAtom.get()\n      if (current.pageIndex === 0) rowSelectionAtom.set({})\n      else paginationAtom.set({ ...current, pageIndex: 0 })\n    }\n    const subscriptions = [\n      columnFiltersAtom.subscribe(resetPage),\n      globalFilterAtom.subscribe(resetPage),\n      sortingAtom.subscribe(resetPage),\n      paginationAtom.subscribe(() => rowSelectionAtom.set({}))\n    ]\n\n    return () => {\n      for (const subscription of subscriptions) subscription.unsubscribe()\n    }\n  }, [\n    columnFiltersAtom,\n    globalFilterAtom,\n    paginationAtom,\n    rowSelectionAtom,\n    sortingAtom\n  ])\n\n  useEffect(() => {\n    if (!urlReady) return\n    writeUrlState(\n      adapters,\n      stateKey,\n      defaultPageSize,\n      {\n        columnFilters,\n        globalFilter,\n        pagination,\n        sorting\n      },\n      syncedSearch\n    )\n  }, [\n    adapters,\n    columnFilters,\n    defaultPageSize,\n    globalFilter,\n    pagination,\n    sorting,\n    stateKey,\n    urlReady\n  ])\n\n  useEffect(() => {\n    if (!urlReady || !visibilityReady) return\n    try {\n      adapters.storage?.write(\n        `${TABLE_STATE_STORAGE_PREFIX}:${stateKey}:columns`,\n        serializeTableColumnVisibility(columnVisibility)\n      )\n    } catch {\n      // Browsers can disable storage while still allowing the table to work.\n    }\n  }, [adapters, columnVisibility, stateKey, urlReady, visibilityReady])\n\n  useEffect(() => {\n    setRestoredUrlStateToken(undefined)\n    setVisibilityReady(false)\n    columnVisibilityAtom.set(\n      readColumnVisibility(adapters, stateKey, stableAllowedColumnIds)\n    )\n    setVisibilityReady(true)\n    const restoreUrlState = () => {\n      restoringUrlState.current = true\n      const search = adapters.search.read()\n      syncedSearch.current = search.toString()\n      const next = parseTableUrlState(\n        search,\n        stateKey,\n        defaultPageSize,\n        ORGANIZATION_TABLE_PAGE_SIZE_OPTIONS,\n        stableAllowedColumnIds\n      )\n      columnFiltersAtom.set(next.columnFilters)\n      globalFilterAtom.set(next.globalFilter)\n      sortingAtom.set(next.sorting)\n      paginationAtom.set(next.pagination)\n      restoringUrlState.current = false\n      setRestoredUrlStateToken(urlStateToken)\n    }\n    restoreUrlState()\n    return adapters.search.subscribe(restoreUrlState)\n  }, [\n    adapters,\n    columnFiltersAtom,\n    columnVisibilityAtom,\n    defaultPageSize,\n    globalFilterAtom,\n    paginationAtom,\n    sortingAtom,\n    stableAllowedColumnIds,\n    stateKey,\n    urlStateToken\n  ])\n\n  return {\n    atoms,\n    columnFilters,\n    columnVisibility,\n    globalFilter,\n    pagination,\n    ready: urlReady && visibilityReady,\n    rowSelection,\n    sorting,\n    setColumnFilters,\n    setColumnVisibility,\n    setGlobalFilter,\n    setPagination,\n    setRowSelection: rowSelectionAtom.set,\n    setSorting\n  }\n}\n\nexport const ORGANIZATION_TABLE_PAGE_SIZE_OPTIONS = [10, 20, 50] as const\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-table-state.ts"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-table-view-options.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationLocalization } from \"@better-auth-ui/core/plugins/organization\"\nimport { Settings2 } from \"lucide-react\"\n\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuCheckboxItem,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { cn } from \"cn\"\n\nexport type OrganizationTableViewColumn = {\n  id: string\n  label: string\n  visible: boolean\n  onVisibleChange: (visible: boolean) => void\n}\n\nexport function OrganizationTableViewOptions({\n  columns,\n  disabled,\n  localization\n}: {\n  columns: OrganizationTableViewColumn[]\n  disabled?: boolean\n  localization: OrganizationLocalization\n}) {\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger\n        className={cn(buttonVariants({ size: \"sm\", variant: \"outline\" }))}\n        disabled={disabled}\n      >\n        <Settings2 data-icon=\"inline-start\" />\n        {localization.columns}\n      </DropdownMenuTrigger>\n      <DropdownMenuContent align=\"end\">\n        <DropdownMenuGroup>\n          {columns.map((column) => (\n            <DropdownMenuCheckboxItem\n              checked={column.visible}\n              key={column.id}\n              onCheckedChange={(checked) =>\n                column.onVisibleChange(checked === true)\n              }\n            >\n              {column.label}\n            </DropdownMenuCheckboxItem>\n          ))}\n        </DropdownMenuGroup>\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-table-view-options.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-table.ts",
      "content": "\"use client\"\n\nimport {\n  columnFacetingFeature,\n  columnFilteringFeature,\n  columnVisibilityFeature,\n  createFacetedRowModel,\n  createFacetedUniqueValues,\n  createFilteredRowModel,\n  createPaginatedRowModel,\n  createSortedRowModel,\n  createTableHook,\n  filterFn_includesString,\n  globalFilteringFeature,\n  rowPaginationFeature,\n  rowSelectionFeature,\n  rowSortingFeature,\n  tableFeatures\n} from \"@tanstack/react-table\"\n\nexport const ORGANIZATION_TABLE_PAGE_SIZE = 10\n\nexport const organizationTableFeatures = tableFeatures({\n  columnFilteringFeature,\n  globalFilteringFeature,\n  filteredRowModel: createFilteredRowModel(),\n  filterFns: { includesString: filterFn_includesString },\n  columnFacetingFeature,\n  facetedRowModel: createFacetedRowModel(),\n  facetedUniqueValues: createFacetedUniqueValues(),\n  columnVisibilityFeature,\n  rowSortingFeature,\n  sortedRowModel: createSortedRowModel(),\n  rowPaginationFeature,\n  paginatedRowModel: createPaginatedRowModel(),\n  rowSelectionFeature\n})\n\nexport const {\n  createAppColumnHelper: createOrganizationColumnHelper,\n  useAppTable: useOrganizationTable\n} = createTableHook({\n  enableMultiSort: true,\n  sortDescFirst: false,\n  features: organizationTableFeatures\n})\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-table.ts"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-teams.tsx",
      "content": "\"use client\"\n\nimport {\n  type AdditionalFields,\n  fieldsWithModelValues,\n  getAdditionalFieldDefaultValues,\n  getAdditionalFieldSubmitValues,\n  validateStringLength\n} from \"@better-auth-ui/core\"\nimport type { OrganizationTeamsAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin, useSession } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useAddTeamMember,\n  useCreateTeam,\n  useHasPermission,\n  useListOrganizationMembers,\n  useListTeamMembers,\n  useListTeams,\n  useListUserTeams,\n  useRemoveTeam,\n  useRemoveTeamMember,\n  useUpdateTeam\n} from \"@better-auth-ui/react/plugins/organization\"\nimport {\n  Pencil,\n  Plus,\n  Trash2,\n  UserPlus,\n  UserRoundMinus,\n  Users\n} from \"lucide-react\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport { toast } from \"sonner\"\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n  AlertDialogTrigger\n} from \"@/components/ui/alert-dialog\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue\n} from \"@/components/ui/select\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport {\n  getAuthAdditionalFieldValidators,\n  isAuthFormFieldInvalid,\n  useAuthForm\n} from \"../auth-form\"\n\ntype Team = { id: string; name: string; [key: string]: unknown }\n\nexport function OrganizationTeams() {\n  const { authClient } = useAuth<OrganizationTeamsAuthClient>()\n  const { data: activeOrganization } = useActiveOrganization(authClient)\n  const { data: session } = useSession(authClient)\n  const { localization, modelFields, teamPolicy } =\n    useAuthPlugin(organizationPlugin)\n  const teams = useListTeams(authClient, {\n    query: { organizationId: activeOrganization?.id }\n  })\n  const members = useListOrganizationMembers(authClient)\n  const userTeams = useListUserTeams(authClient, {\n    query: { organizationId: activeOrganization?.id ?? \"\" }\n  })\n  const userTeamIds = new Set(userTeams.data?.map((team) => team.id))\n  const activeTeamId = (\n    session?.session as { activeTeamId?: string | null } | undefined\n  )?.activeTeamId\n  const canCreate = useHasPermission(authClient, {\n    organizationId: activeOrganization?.id,\n    permissions: { team: [\"create\"] }\n  })\n  const teamLimitReached =\n    teamPolicy.maximumTeams !== undefined &&\n    (teams.data?.length ?? 0) >= teamPolicy.maximumTeams\n  const [dialogTeam, setDialogTeam] = useState<Team | null>()\n\n  return (\n    <div className=\"flex flex-col gap-4\">\n      <div className=\"flex flex-col justify-between gap-3 sm:flex-row sm:items-end\">\n        <div>\n          <h2 className=\"text-sm font-semibold\">{localization.teams}</h2>\n          <p className=\"text-sm text-muted-foreground\">\n            {localization.teamsDescription}\n          </p>\n        </div>\n        {(canCreate.isPending || canCreate.data?.success) && (\n          <Button\n            disabled={canCreate.isPending || teamLimitReached}\n            onClick={() => setDialogTeam(null)}\n            title={teamLimitReached ? localization.teamLimitReached : undefined}\n          >\n            <Plus data-icon=\"inline-start\" />\n            {localization.createTeam}\n          </Button>\n        )}\n      </div>\n\n      {teamLimitReached && canCreate.data?.success && (\n        <p className=\"text-sm text-destructive\" role=\"alert\">\n          {localization.teamLimitReached}\n        </p>\n      )}\n\n      {teams.isPending ? (\n        <Spinner />\n      ) : teams.data?.length ? (\n        <Card>\n          <CardContent className=\"p-0\">\n            {teams.data.map((team) => (\n              <div\n                className=\"flex items-center justify-between gap-4 border-b px-4 py-3 last:border-b-0\"\n                key={team.id}\n              >\n                <div className=\"min-w-0\">\n                  <p className=\"truncate text-sm font-medium\">{team.name}</p>\n                  <p className=\"text-sm text-muted-foreground\">\n                    {localization.team}\n                  </p>\n                </div>\n                <Button\n                  onClick={() => setDialogTeam(team)}\n                  size=\"sm\"\n                  variant=\"outline\"\n                >\n                  <Pencil data-icon=\"inline-start\" />\n                  {localization.manage}\n                </Button>\n              </div>\n            ))}\n          </CardContent>\n        </Card>\n      ) : (\n        <Card>\n          <CardContent className=\"flex flex-col gap-1\">\n            <p className=\"text-sm font-medium\">{localization.noTeams}</p>\n            <p className=\"text-sm text-muted-foreground\">\n              {localization.noTeamsDescription}\n            </p>\n          </CardContent>\n        </Card>\n      )}\n\n      <TeamDialog\n        activeTeamId={activeTeamId}\n        allowRemovingAllTeams={teamPolicy.allowRemovingAllTeams}\n        canListMembers={dialogTeam ? userTeamIds.has(dialogTeam.id) : false}\n        maximumMembersPerTeam={teamPolicy.maximumMembersPerTeam}\n        onOpenChange={(open) => !open && setDialogTeam(undefined)}\n        open={dialogTeam !== undefined}\n        organizationId={activeOrganization?.id ?? \"\"}\n        organizationMembers={members.data?.members ?? []}\n        team={dialogTeam ?? undefined}\n        teamCount={teams.data?.length ?? 0}\n        teamFields={modelFields.team}\n        teamLimitReached={teamLimitReached}\n      />\n    </div>\n  )\n}\n\nfunction TeamDialog({\n  activeTeamId,\n  allowRemovingAllTeams,\n  canListMembers,\n  maximumMembersPerTeam,\n  onOpenChange,\n  open,\n  organizationId,\n  organizationMembers,\n  team,\n  teamCount,\n  teamFields,\n  teamLimitReached\n}: {\n  activeTeamId?: string | null\n  allowRemovingAllTeams: boolean\n  canListMembers: boolean\n  maximumMembersPerTeam?: number\n  onOpenChange: (open: boolean) => void\n  open: boolean\n  organizationId: string\n  organizationMembers: Array<{\n    userId: string\n    user: { name: string; email: string }\n  }>\n  team?: Team\n  teamCount: number\n  teamFields: AdditionalFields\n  teamLimitReached: boolean\n}) {\n  const { authClient, localization: authLocalization } =\n    useAuth<OrganizationTeamsAuthClient>()\n  const { localization } = useAuthPlugin(organizationPlugin)\n  const teamMembers = useListTeamMembers(authClient, {\n    query: { teamId: team?.id ?? \"\" },\n    enabled: open && !!team && canListMembers\n  })\n  const canUpdate = useHasPermission(authClient, {\n    organizationId,\n    permissions: { team: [\"update\"] }\n  })\n  const canDelete = useHasPermission(authClient, {\n    organizationId,\n    permissions: { team: [\"delete\"] }\n  })\n  const canAddMember = useHasPermission(authClient, {\n    organizationId,\n    permissions: { member: [\"update\"] }\n  })\n  const canRemoveMember = useHasPermission(authClient, {\n    organizationId,\n    permissions: { member: [\"delete\"] }\n  })\n  const [userId, setUserId] = useState(\"\")\n  const createTeam = useCreateTeam(authClient, {\n    onSuccess: () => {\n      toast.success(localization.teamCreated)\n      onOpenChange(false)\n    }\n  })\n  const updateTeam = useUpdateTeam(authClient, {\n    onSuccess: () => {\n      toast.success(localization.teamUpdated)\n      onOpenChange(false)\n    }\n  })\n  const removeTeam = useRemoveTeam(authClient, {\n    onSuccess: () => {\n      toast.success(localization.teamDeleted)\n      onOpenChange(false)\n    }\n  })\n  const addMember = useAddTeamMember(authClient, {\n    onSuccess: () => setUserId(\"\")\n  })\n  const removeMember = useRemoveTeamMember(authClient)\n  const memberIds = new Set(teamMembers.data?.map((member) => member.userId))\n  const memberLimitReached =\n    maximumMembersPerTeam !== undefined &&\n    (teamMembers.data?.length ?? 0) >= maximumMembersPerTeam\n  const isActiveTeam = activeTeamId === team?.id\n  const canRemoveFinalTeam = allowRemovingAllTeams || teamCount > 1\n  const canRemoveTeam = canRemoveFinalTeam && !isActiveTeam\n  const teamRemovalDisabledReason = isActiveTeam\n    ? localization.activeTeamRemovalDisabled\n    : localization.lastTeamRemovalDisabled\n\n  const configuredTeamFields = useMemo(\n    () => fieldsWithModelValues(teamFields, team ?? {}),\n    [team, teamFields]\n  )\n  const form = useAuthForm({\n    defaultValues: {\n      additionalFields: getAdditionalFieldDefaultValues(configuredTeamFields),\n      name: team?.name ?? \"\"\n    },\n    onSubmit: async ({ value }) => {\n      const name = value.name.trim()\n      if (!name || !organizationId || (!team && teamLimitReached)) return\n      if (team && !canUpdate.data?.success) return\n\n      const data = {\n        ...getAdditionalFieldSubmitValues(\n          configuredTeamFields,\n          value.additionalFields\n        ),\n        name,\n        organizationId\n      }\n\n      try {\n        if (team) await updateTeam.mutateAsync({ teamId: team.id, data })\n        else await createTeam.mutateAsync(data)\n      } catch {\n        // The mutation reports the error through its configured handler.\n      }\n    }\n  })\n\n  useEffect(() => {\n    if (!open) return\n    form.reset({\n      additionalFields: getAdditionalFieldDefaultValues(configuredTeamFields),\n      name: team?.name ?? \"\"\n    })\n    setUserId(\"\")\n  }, [configuredTeamFields, form, open, team?.name])\n\n  const pending = createTeam.isPending || updateTeam.isPending\n  const canEdit = !team || canUpdate.data?.success === true\n\n  return (\n    <Dialog open={open} onOpenChange={onOpenChange}>\n      <DialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n        <form.AppForm>\n          <form.AuthFormRoot className=\"flex flex-col gap-6\">\n            <DialogHeader>\n              <DialogTitle className=\"flex items-center gap-2\">\n                <Users />\n                {team ? localization.renameTeam : localization.createTeam}\n              </DialogTitle>\n              <DialogDescription>\n                {localization.teamsDescription}\n              </DialogDescription>\n            </DialogHeader>\n\n            <div className=\"flex flex-col gap-4\">\n              <form.AppField\n                name=\"name\"\n                validators={{\n                  onChange: ({ value }) =>\n                    validateStringLength(value, {\n                      requiredMessage: authLocalization.auth.fieldRequired,\n                      trim: true\n                    })\n                }}\n              >\n                {(field) => {\n                  const isInvalid = isAuthFormFieldInvalid(field.state.meta)\n                  return (\n                    <Field data-invalid={isInvalid}>\n                      <FieldLabel htmlFor=\"organization-team-name\">\n                        {localization.name}\n                      </FieldLabel>\n                      <Input\n                        aria-invalid={isInvalid}\n                        autoFocus\n                        disabled={pending || !canEdit}\n                        id=\"organization-team-name\"\n                        name={field.name}\n                        onBlur={field.handleBlur}\n                        onChange={(event) =>\n                          field.handleChange(event.target.value)\n                        }\n                        value={field.state.value}\n                      />\n                      <field.AuthFormFieldError />\n                    </Field>\n                  )\n                }}\n              </form.AppField>\n\n              {configuredTeamFields.map((configuredField) => (\n                <form.AppField\n                  key={configuredField.name}\n                  name={`additionalFields.${configuredField.name}`}\n                  validators={getAuthAdditionalFieldValidators(\n                    configuredField,\n                    authLocalization.auth.fieldRequired\n                  )}\n                >\n                  {(field) => (\n                    <field.AuthFormAdditionalField\n                      field={configuredField}\n                      isPending={pending || !canEdit}\n                      optionalLabel={authLocalization.settings.optional}\n                    />\n                  )}\n                </form.AppField>\n              ))}\n            </div>\n\n            {team && canListMembers && (\n              <div className=\"flex flex-col gap-4 border-t pt-5\">\n                <div>\n                  <h3 className=\"text-sm font-medium\">\n                    {localization.teamMembers}\n                  </h3>\n                  <p className=\"text-sm text-muted-foreground\">\n                    {localization.addTeamMember}\n                  </p>\n                </div>\n\n                {(canAddMember.isPending || canAddMember.data?.success) && (\n                  <div className=\"flex flex-col items-stretch gap-2 sm:flex-row sm:items-end\">\n                    <Field className=\"flex-1\">\n                      <FieldLabel>{localization.addTeamMember}</FieldLabel>\n                      <Select\n                        disabled={canAddMember.isPending || memberLimitReached}\n                        onValueChange={(value) => setUserId(value ?? \"\")}\n                        value={userId}\n                      >\n                        <SelectTrigger className=\"w-full\">\n                          <SelectValue\n                            placeholder={localization.selectMember}\n                          />\n                        </SelectTrigger>\n                        <SelectContent>\n                          {organizationMembers\n                            .filter((member) => !memberIds.has(member.userId))\n                            .map((member) => (\n                              <SelectItem\n                                key={member.userId}\n                                value={member.userId}\n                              >\n                                {member.user.name || member.user.email}\n                              </SelectItem>\n                            ))}\n                        </SelectContent>\n                      </Select>\n                    </Field>\n                    <Button\n                      disabled={\n                        canAddMember.isPending ||\n                        !userId ||\n                        addMember.isPending ||\n                        memberLimitReached\n                      }\n                      onClick={() => {\n                        if (!canAddMember.data?.success || !userId) return\n                        addMember.mutate({\n                          teamId: team.id,\n                          userId,\n                          organizationId\n                        })\n                      }}\n                      type=\"button\"\n                    >\n                      <UserPlus data-icon=\"inline-start\" />\n                      {localization.addTeamMember}\n                    </Button>\n                  </div>\n                )}\n\n                {canAddMember.data?.success && memberLimitReached && (\n                  <p className=\"text-sm text-destructive\" role=\"alert\">\n                    {localization.teamMemberLimitReached}\n                  </p>\n                )}\n\n                <div className=\"flex flex-col gap-2\">\n                  {teamMembers.isPending && <Spinner />}\n                  {teamMembers.data?.map((teamMember) => {\n                    const member = organizationMembers.find(\n                      (candidate) => candidate.userId === teamMember.userId\n                    )\n                    return (\n                      <div\n                        className=\"flex items-center justify-between gap-3 rounded-md border px-3 py-2\"\n                        key={teamMember.id}\n                      >\n                        <span className=\"truncate text-sm\">\n                          {member?.user.name ||\n                            member?.user.email ||\n                            teamMember.userId}\n                        </span>\n                        {(canRemoveMember.isPending ||\n                          canRemoveMember.data?.success) && (\n                          <Button\n                            aria-label={localization.removeTeamMember}\n                            disabled={\n                              canRemoveMember.isPending ||\n                              removeMember.isPending\n                            }\n                            onClick={() => {\n                              if (!canRemoveMember.data?.success) return\n                              removeMember.mutate({\n                                teamId: team.id,\n                                userId: teamMember.userId,\n                                organizationId\n                              })\n                            }}\n                            size=\"icon-sm\"\n                            type=\"button\"\n                            variant=\"ghost\"\n                          >\n                            <UserRoundMinus />\n                          </Button>\n                        )}\n                      </div>\n                    )\n                  })}\n                </div>\n              </div>\n            )}\n\n            {team && !canRemoveTeam && canDelete.data?.success && (\n              <p className=\"text-sm text-muted-foreground\">\n                {teamRemovalDisabledReason}\n              </p>\n            )}\n\n            <DialogFooter className=\"sm:justify-between\">\n              {team && (canDelete.isPending || canDelete.data?.success) && (\n                <AlertDialog>\n                  <AlertDialogTrigger asChild>\n                    <Button\n                      disabled={\n                        canDelete.isPending ||\n                        removeTeam.isPending ||\n                        !canRemoveTeam\n                      }\n                      type=\"button\"\n                      variant=\"destructive\"\n                    >\n                      <Trash2 data-icon=\"inline-start\" />\n                      {localization.deleteTeam}\n                    </Button>\n                  </AlertDialogTrigger>\n                  <AlertDialogContent>\n                    <AlertDialogHeader>\n                      <AlertDialogTitle>\n                        {localization.deleteTeam}\n                      </AlertDialogTitle>\n                      <AlertDialogDescription>\n                        {localization.deleteTeamDescription}\n                      </AlertDialogDescription>\n                    </AlertDialogHeader>\n                    <AlertDialogFooter>\n                      <AlertDialogCancel>\n                        {authLocalization.settings.cancel}\n                      </AlertDialogCancel>\n                      <AlertDialogAction\n                        className={buttonVariants({ variant: \"destructive\" })}\n                        onClick={() =>\n                          removeTeam.mutate({\n                            teamId: team.id,\n                            organizationId\n                          })\n                        }\n                      >\n                        <Trash2 data-icon=\"inline-start\" />\n                        {localization.deleteTeam}\n                      </AlertDialogAction>\n                    </AlertDialogFooter>\n                  </AlertDialogContent>\n                </AlertDialog>\n              )}\n\n              <div className=\"flex flex-col-reverse gap-2 sm:flex-row\">\n                <DialogClose\n                  className={buttonVariants({ variant: \"outline\" })}\n                  disabled={pending}\n                  type=\"button\"\n                >\n                  {authLocalization.settings.cancel}\n                </DialogClose>\n                {canEdit && (\n                  <form.AuthFormSubmitButton\n                    disabled={pending || (teamLimitReached && !team)}\n                  >\n                    {team\n                      ? authLocalization.settings.saveChanges\n                      : localization.createTeam}\n                  </form.AuthFormSubmitButton>\n                )}\n              </div>\n            </DialogFooter>\n          </form.AuthFormRoot>\n        </form.AppForm>\n      </DialogContent>\n    </Dialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-teams.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-view-skeleton.tsx",
      "content": "\"use client\"\n\nimport type { ComponentProps } from \"react\"\n\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { cn } from \"cn\"\nimport {\n  OrganizationLogo,\n  type OrganizationLogoSize\n} from \"./organization-logo\"\n\nexport type OrganizationViewSkeletonProps = {\n  className?: string\n  hideSlug?: boolean\n  size?: OrganizationLogoSize\n}\n\n/**\n * Placeholder matching `OrganizationView` while organization data loads.\n */\nexport function OrganizationViewSkeleton({\n  className,\n  hideSlug,\n  size = \"md\",\n  ...props\n}: OrganizationViewSkeletonProps & ComponentProps<\"div\">) {\n  return (\n    <div\n      className={cn(\"flex min-w-0 items-center gap-2\", className)}\n      {...props}\n    >\n      <OrganizationLogo\n        isPending\n        className={size === \"sm\" ? \"size-5\" : undefined}\n        size={size === \"lg\" ? \"md\" : \"sm\"}\n      />\n\n      <div className=\"flex flex-col min-w-0 gap-1\">\n        <Skeleton className=\"h-3.5 w-20 rounded-md\" />\n\n        {!hideSlug && <Skeleton className=\"h-3 w-28 rounded-md\" />}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-view-skeleton.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization-view.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin, useSession } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useListOrganizationMembers\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Organization } from \"better-auth/client\"\nimport type { ComponentProps } from \"react\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport {\n  OrganizationLogo,\n  type OrganizationLogoSize\n} from \"./organization-logo\"\nimport { OrganizationViewSkeleton } from \"./organization-view-skeleton\"\n\nexport type OrganizationViewProps = {\n  className?: string\n  isPending?: boolean\n  size?: OrganizationLogoSize\n  hideRole?: boolean\n  hideSlug?: boolean\n  organization?: Partial<Organization>\n}\n\n/**\n * Compact organization row: logo, primary name, secondary slug — analogous to `UserView`.\n */\nexport function OrganizationView({\n  className,\n  isPending,\n  size = \"md\",\n  hideSlug: hideSlugProp,\n  hideRole,\n  organization,\n  ...props\n}: OrganizationViewProps & ComponentProps<\"div\">) {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const {\n    roles,\n    slugPrefix,\n    hideSlug: pluginHideSlug\n  } = useAuthPlugin(organizationPlugin)\n  const hideSlug = hideSlugProp ?? pluginHideSlug ?? false\n\n  const { data: session } = useSession(authClient)\n\n  const { data: activeOrganization, isPending: activeOrganizationPending } =\n    useActiveOrganization(authClient, {\n      enabled: !organization && !isPending\n    })\n\n  const resolvedOrganization = organization ?? activeOrganization\n\n  const { data: membersList, isPending: membersPending } =\n    useListOrganizationMembers(authClient, {\n      query: {\n        organizationId: resolvedOrganization?.id\n      },\n      enabled: !!resolvedOrganization?.id && !hideRole\n    })\n\n  const membership = membersList?.members?.find(\n    (member) => member.userId === session?.user.id\n  )\n\n  if (\n    isPending ||\n    (!organization && activeOrganizationPending) ||\n    (!hideRole && !!resolvedOrganization?.id && membersPending)\n  ) {\n    return (\n      <OrganizationViewSkeleton\n        className={className}\n        hideSlug={hideSlug}\n        size={size}\n        {...props}\n      />\n    )\n  }\n\n  return (\n    <div\n      className={cn(\"flex min-w-0 items-center gap-2\", className)}\n      {...props}\n    >\n      <OrganizationLogo\n        organization={resolvedOrganization}\n        className={size === \"sm\" ? \"size-5\" : undefined}\n        size={size === \"lg\" ? \"md\" : \"sm\"}\n      />\n\n      <div className=\"flex min-w-0 flex-col\">\n        <div className=\"flex min-w-0 items-center gap-2\">\n          <p className=\"truncate text-sm font-medium leading-tight text-foreground\">\n            {resolvedOrganization?.name}\n          </p>\n\n          {!hideRole && !!membership && (\n            <Badge variant=\"secondary\" className=\"-my-0.5 shrink-0\">\n              {roles?.[membership.role] ?? membership.role}\n            </Badge>\n          )}\n        </div>\n\n        {!hideSlug && !!resolvedOrganization?.slug && (\n          <p className=\"truncate overflow-x-hidden text-muted-foreground text-xs font-mono leading-tight\">\n            {slugPrefix}\n            {resolvedOrganization.slug}\n          </p>\n        )}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization-view.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organization.tsx",
      "content": "\"use client\"\n\nimport type {\n  OrganizationAuthClient,\n  OrganizationView\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthenticate, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useActiveOrganization,\n  useHasPermission\n} from \"@better-auth-ui/react/plugins/organization\"\nimport {\n  ShieldCheck as RolesIcon,\n  Settings as SettingsIcon,\n  UsersRound as TeamsIcon,\n  User2 as UserIcon\n} from \"lucide-react\"\nimport { useEffect, useMemo } from \"react\"\n\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\nimport { OrganizationPeople } from \"./organization-people\"\nimport { OrganizationRoles } from \"./organization-roles\"\nimport { OrganizationSettings } from \"./organization-settings\"\nimport { OrganizationTeams } from \"./organization-teams\"\n\nexport type OrganizationProps = {\n  className?: string\n  hideNav?: boolean\n  path?: string\n  /** @remarks `OrganizationView` */\n  view?: OrganizationView | string\n}\n\n/**\n * Organization management shell: tabs for profile / danger zone and for\n * people (members / invitations). Path segments come from\n * `useAuthPlugin(organizationPlugin).viewPaths.organization`.\n */\nexport function Organization({\n  className,\n  hideNav,\n  path,\n  view\n}: OrganizationProps) {\n  if (!view && !path) {\n    throw new Error(\"[Better Auth UI] Either `view` or `path` must be provided\")\n  }\n\n  const { authClient, basePaths, localization, navigate, plugins } =\n    useAuth<OrganizationAuthClient>()\n  useAuthenticate(authClient)\n\n  const {\n    localization: organizationLocalization,\n    viewPaths: organizationViewPaths,\n    slug,\n    slugPrefix,\n    teams,\n    dynamicAccessControl\n  } = useAuthPlugin(organizationPlugin)\n\n  const { data: activeOrganization, isPending } =\n    useActiveOrganization(authClient)\n  const extensionTabs = useMemo(\n    () => plugins.flatMap((plugin) => plugin.organizationTabs ?? []),\n    [plugins]\n  )\n  const rolesEnabled = dynamicAccessControl?.enabled === true\n  const canReadRoles = useHasPermission(authClient, {\n    organizationId: activeOrganization?.id,\n    permissions: { ac: [\"read\"] },\n    enabled: rolesEnabled && Boolean(activeOrganization?.id)\n  })\n\n  useEffect(() => {\n    if (!isPending && !activeOrganization) {\n      navigate({\n        to: `${basePaths.settings}/${organizationViewPaths.settings?.organizations}`,\n        replace: true\n      })\n    }\n  }, [\n    basePaths.settings,\n    isPending,\n    navigate,\n    organizationViewPaths.settings?.organizations,\n    activeOrganization\n  ])\n\n  const currentView = useMemo(() => {\n    if (view) return view === \"roles\" && !rolesEnabled ? undefined : view\n\n    const match = [\n      ...Object.entries(organizationViewPaths.organization).filter(\n        ([name]) => rolesEnabled || name !== \"roles\"\n      ),\n      ...extensionTabs.map((tab) => [tab.id, tab.path] as const)\n    ].find(([, segment]) => segment === path)\n\n    return match?.[0] as OrganizationView | undefined\n  }, [\n    extensionTabs,\n    view,\n    path,\n    organizationViewPaths.organization,\n    rolesEnabled\n  ])\n\n  if (!currentView) {\n    const validPaths = Object.entries(organizationViewPaths.organization)\n      .filter(([name]) => rolesEnabled || name !== \"roles\")\n      .map(([, segment]) => segment)\n      .join(\", \")\n    throw new Error(\n      `[Better Auth UI] Unknown organization path \"${path}\". Valid paths are: ${validPaths}`\n    )\n  }\n\n  if (!isPending && !activeOrganization) {\n    return null\n  }\n\n  const selectedView =\n    currentView === \"roles\" &&\n    !canReadRoles.isPending &&\n    !canReadRoles.data?.success\n      ? \"settings\"\n      : currentView\n\n  return (\n    <Tabs\n      value={selectedView}\n      className={cn(\"w-full gap-4 md:gap-6\", className)}\n    >\n      <div className={cn(hideNav && \"hidden\")}>\n        <TabsList aria-label={localization.settings.settings}>\n          <TabsTrigger\n            value=\"settings\"\n            className=\"gap-1\"\n            onClick={() =>\n              navigate({\n                to: slug\n                  ? `${basePaths.organization}/${slugPrefix}${slug}/${organizationViewPaths.organization.settings}`\n                  : `${basePaths.organization}/${organizationViewPaths.organization.settings}`\n              })\n            }\n          >\n            <SettingsIcon className=\"text-muted-foreground\" />\n\n            {localization.settings.settings}\n          </TabsTrigger>\n\n          {teams && (\n            <TabsTrigger\n              value=\"teams\"\n              className=\"gap-1\"\n              onClick={() =>\n                navigate({\n                  to: slug\n                    ? `${basePaths.organization}/${slugPrefix}${slug}/${organizationViewPaths.organization.teams}`\n                    : `${basePaths.organization}/${organizationViewPaths.organization.teams}`\n                })\n              }\n            >\n              <TeamsIcon className=\"text-muted-foreground\" />\n              {organizationLocalization.teams}\n            </TabsTrigger>\n          )}\n\n          {rolesEnabled && (\n            <TabsTrigger\n              disabled={canReadRoles.isPending || !canReadRoles.data?.success}\n              value=\"roles\"\n              className=\"gap-1\"\n              onClick={() =>\n                navigate({\n                  to: slug\n                    ? `${basePaths.organization}/${slugPrefix}${slug}/${organizationViewPaths.organization.roles}`\n                    : `${basePaths.organization}/${organizationViewPaths.organization.roles}`\n                })\n              }\n            >\n              <RolesIcon className=\"text-muted-foreground\" />\n              {organizationLocalization.roles}\n            </TabsTrigger>\n          )}\n\n          {extensionTabs.map((tab) => (\n            <TabsTrigger\n              key={tab.id}\n              value={tab.id}\n              onClick={() =>\n                navigate({\n                  to: slug\n                    ? `${basePaths.organization}/${slugPrefix}${slug}/${tab.path}`\n                    : `${basePaths.organization}/${tab.path}`\n                })\n              }\n            >\n              {tab.label}\n            </TabsTrigger>\n          ))}\n\n          <TabsTrigger\n            value=\"people\"\n            className=\"gap-1\"\n            onClick={() =>\n              navigate({\n                to: slug\n                  ? `${basePaths.organization}/${slugPrefix}${slug}/${organizationViewPaths.organization.people}`\n                  : `${basePaths.organization}/${organizationViewPaths.organization.people}`\n              })\n            }\n          >\n            <UserIcon className=\"text-muted-foreground\" />\n\n            {organizationLocalization.people}\n          </TabsTrigger>\n        </TabsList>\n      </div>\n\n      <TabsContent value=\"settings\" tabIndex={-1}>\n        {activeOrganization && (\n          <OrganizationSettings\n            organizationId={activeOrganization.id}\n            organizationSlug={activeOrganization.slug}\n          />\n        )}\n      </TabsContent>\n\n      <TabsContent value=\"people\" tabIndex={-1}>\n        <OrganizationPeople />\n      </TabsContent>\n\n      {teams && (\n        <TabsContent value=\"teams\" tabIndex={-1}>\n          <OrganizationTeams />\n        </TabsContent>\n      )}\n\n      {rolesEnabled && (\n        <TabsContent value=\"roles\" tabIndex={-1}>\n          {canReadRoles.data?.success ? (\n            <OrganizationRoles organizationId={activeOrganization?.id ?? \"\"} />\n          ) : null}\n        </TabsContent>\n      )}\n\n      {extensionTabs.map((tab) => {\n        const Extension = tab.component\n        return (\n          <TabsContent key={tab.id} value={tab.id} tabIndex={-1}>\n            <Extension\n              organizationId={activeOrganization?.id ?? \"\"}\n              organizationSlug={activeOrganization?.slug ?? \"\"}\n            />\n          </TabsContent>\n        )\n      })}\n    </Tabs>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organization.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organizations-empty.tsx",
      "content": "\"use client\"\n\nimport { useAuthPlugin } from \"@better-auth-ui/react\"\nimport { Briefcase } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Empty,\n  EmptyContent,\n  EmptyDescription,\n  EmptyHeader,\n  EmptyMedia,\n  EmptyTitle\n} from \"@/components/ui/empty\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\n\nexport type OrganizationsEmptyProps = {\n  onCreatePress: () => void\n  canCreate?: boolean\n}\n\nexport function OrganizationsEmpty({\n  onCreatePress,\n  canCreate = true\n}: OrganizationsEmptyProps) {\n  const { localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n\n  return (\n    <Empty>\n      <EmptyHeader>\n        <EmptyMedia variant=\"icon\">\n          <Briefcase />\n        </EmptyMedia>\n        <EmptyTitle>{organizationLocalization.noOrganizations}</EmptyTitle>\n        <EmptyDescription>\n          {organizationLocalization.organizationsDescription}\n        </EmptyDescription>\n      </EmptyHeader>\n      <EmptyContent>\n        <Button size=\"sm\" disabled={!canCreate} onClick={onCreatePress}>\n          {organizationLocalization.createOrganization}\n        </Button>\n      </EmptyContent>\n    </Empty>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organizations-empty.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organizations-settings.tsx",
      "content": "\"use client\"\n\nimport type { ComponentProps } from \"react\"\n\nimport { cn } from \"cn\"\nimport { Organizations } from \"./organizations\"\nimport { UserInvitations } from \"./user-invitations\"\n\nexport type OrganizationsSettingsProps = {\n  className?: string\n}\n\n/**\n * Renders the organizations settings panel.\n *\n * Displays all organizations the user belongs to with an empty state and\n * create button, followed by a card for invitations to the user.\n */\nexport function OrganizationsSettings({\n  className,\n  ...props\n}: OrganizationsSettingsProps & ComponentProps<\"div\">) {\n  return (\n    <div\n      className={cn(\"flex w-full flex-col gap-4 md:gap-6\", className)}\n      {...props}\n    >\n      <Organizations />\n      <UserInvitations />\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organizations-settings.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/organizations.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useListOrganizations } from \"@better-auth-ui/react/plugins/organization\"\nimport { Fragment, useState } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Item, ItemGroup, ItemSeparator } from \"@/components/ui/item\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { CreateOrganizationDialog } from \"./create-organization-dialog\"\nimport { OrganizationRow } from \"./organization-row\"\nimport { OrganizationViewSkeleton } from \"./organization-view-skeleton\"\nimport { OrganizationsEmpty } from \"./organizations-empty\"\n\nexport type OrganizationsProps = {\n  className?: string\n}\n\n/**\n * Lists organizations the user belongs to (via `useListOrganizations`): loading skeleton,\n * empty state with create, or a card of rows with a Manage control per organization.\n * Owns `CreateOrganizationDialog` open state and the create actions.\n */\nexport function Organizations({ className }: OrganizationsProps) {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const {\n    allowOrganizationCreation,\n    localization: organizationLocalization,\n    organizationLimit\n  } = useAuthPlugin(organizationPlugin)\n\n  const [createOpen, setCreateOpen] = useState(false)\n\n  const { data: organizations, isPending: organizationsPending } =\n    useListOrganizations(authClient)\n  const canCreate =\n    allowOrganizationCreation &&\n    (organizationLimit === undefined ||\n      (organizations?.length ?? 0) < organizationLimit)\n\n  return (\n    <>\n      <div className={className}>\n        <div className=\"flex flex-col gap-3\">\n          <div className=\"flex items-end justify-between gap-3\">\n            <h2 className=\"truncate text-sm font-semibold\">\n              {organizationLocalization.organizations}\n            </h2>\n\n            {allowOrganizationCreation && (\n              <Button\n                className=\"shrink-0\"\n                size=\"sm\"\n                disabled={organizationsPending || !canCreate}\n                onClick={() => setCreateOpen(true)}\n              >\n                {organizationLocalization.createOrganization}\n              </Button>\n            )}\n          </div>\n\n          <Card className=\"p-0\">\n            <CardContent className=\"p-0\">\n              {organizationsPending ? (\n                <ItemGroup>\n                  <Item>\n                    <OrganizationViewSkeleton />\n                  </Item>\n                </ItemGroup>\n              ) : !organizations?.length ? (\n                <OrganizationsEmpty\n                  canCreate={canCreate}\n                  onCreatePress={() => setCreateOpen(true)}\n                />\n              ) : (\n                <ItemGroup className=\"gap-0\">\n                  {organizations.map((organization, index) => (\n                    <Fragment key={organization.id}>\n                      {index > 0 && <ItemSeparator />}\n                      <OrganizationRow organization={organization} />\n                    </Fragment>\n                  ))}\n                </ItemGroup>\n              )}\n            </CardContent>\n          </Card>\n        </div>\n      </div>\n\n      {canCreate && (\n        <CreateOrganizationDialog\n          open={createOpen}\n          onOpenChange={setCreateOpen}\n        />\n      )}\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/organizations.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/remove-member-dialog.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useRemoveMember } from \"@better-auth-ui/react/plugins/organization\"\nimport type { Member, User } from \"better-auth/client\"\nimport { Trash2 } from \"lucide-react\"\nimport { toast } from \"sonner\"\n\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogMedia,\n  AlertDialogTitle\n} from \"@/components/ui/alert-dialog\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { UserView } from \"../user/user-view\"\n\nexport type RemoveMemberDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  member: Member & { user: Partial<User> }\n}\n\nexport function RemoveMemberDialog({\n  open,\n  onOpenChange,\n  member\n}: RemoveMemberDialogProps) {\n  const { authClient, localization } = useAuth<OrganizationAuthClient>()\n  const { localization: organizationLocalization, roles } =\n    useAuthPlugin(organizationPlugin)\n\n  const { mutate: removeMember, isPending } = useRemoveMember(authClient, {\n    onSuccess: () => {\n      onOpenChange(false)\n      toast.success(organizationLocalization.memberRemoved)\n    }\n  })\n\n  return (\n    <AlertDialog open={open} onOpenChange={onOpenChange}>\n      <AlertDialogContent>\n        <AlertDialogHeader>\n          <AlertDialogMedia className=\"bg-destructive/10 text-destructive\">\n            <Trash2 />\n          </AlertDialogMedia>\n\n          <AlertDialogTitle>\n            {organizationLocalization.removeMember}\n          </AlertDialogTitle>\n\n          <AlertDialogDescription>\n            {organizationLocalization.removeMemberWarning}\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n\n        <Card>\n          <CardContent className=\"flex flex-row items-center justify-between gap-2\">\n            <UserView user={member.user} />\n\n            <Badge variant=\"outline\">\n              {roles?.[member.role] ?? member.role}\n            </Badge>\n          </CardContent>\n        </Card>\n\n        <AlertDialogFooter>\n          <AlertDialogCancel disabled={isPending}>\n            {localization.settings.cancel}\n          </AlertDialogCancel>\n\n          <Button\n            variant=\"destructive\"\n            disabled={isPending}\n            onClick={() =>\n              removeMember({\n                memberIdOrEmail: member.id,\n                organizationId: member.organizationId\n              })\n            }\n          >\n            {isPending && <Spinner />}\n\n            {organizationLocalization.removeMember}\n          </Button>\n        </AlertDialogFooter>\n      </AlertDialogContent>\n    </AlertDialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/remove-member-dialog.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/slug-field.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { useCheckSlug } from \"@better-auth-ui/react/plugins/organization\"\nimport { useDebouncer } from \"@tanstack/react-pacer\"\nimport { Check, X } from \"lucide-react\"\nimport { useEffect, useState } from \"react\"\n\nimport { Field, FieldError, FieldLabel } from \"@/components/ui/field\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput\n} from \"@/components/ui/input-group\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\n\n/** Props for the `SlugField` component. */\nexport type SlugFieldProps = {\n  value: string\n  onChange: (value: string) => void\n  currentSlug?: string\n  disabled?: boolean\n  id?: string\n}\n\n/**\n * Sanitize a slug value so it only contains lowercase alphanumeric characters\n * and dashes. Runs of disallowed characters are collapsed to a single dash, but\n * leading/trailing dashes are preserved while the user is still typing.\n */\nexport function sanitizeSlug(value: string) {\n  return value.toLowerCase().replace(/[^a-z0-9]+/g, \"-\")\n}\n\n/**\n * Organization slug field with debounced availability checking.\n */\nexport function SlugField({\n  value,\n  onChange,\n  currentSlug,\n  disabled,\n  id = \"slug\"\n}: SlugFieldProps) {\n  const { authClient, localization: authLocalization } =\n    useAuth<OrganizationAuthClient>()\n  const {\n    localization,\n    checkSlug: checkSlugEnabled,\n    slugPrefix\n  } = useAuthPlugin(organizationPlugin)\n\n  const [slugError, setSlugError] = useState<string>()\n\n  const {\n    mutate: checkSlug,\n    data: checkSlugData,\n    error: checkSlugError,\n    reset: resetCheckSlug\n  } = useCheckSlug(authClient)\n\n  const debouncer = useDebouncer(\n    (next: string) => {\n      if (!checkSlugEnabled || !next.trim() || next.trim() === currentSlug)\n        return\n\n      checkSlug({ slug: next.trim() })\n    },\n    { wait: 500 }\n  )\n\n  useEffect(() => {\n    // Clear stale validation errors when the controlled value changes\n    // externally (e.g. the parent resets the form), not just via this\n    // input's onChange.\n    setSlugError(undefined)\n\n    if (!checkSlugEnabled) return\n\n    resetCheckSlug()\n    debouncer.maybeExecute(value)\n  }, [checkSlugEnabled, value, debouncer.maybeExecute, resetCheckSlug])\n\n  return (\n    <Field data-invalid={!!slugError}>\n      <FieldLabel htmlFor={id}>{localization.slug}</FieldLabel>\n\n      <InputGroup>\n        {slugPrefix && (\n          <InputGroupAddon align=\"inline-start\">{slugPrefix}</InputGroupAddon>\n        )}\n\n        <InputGroupInput\n          id={id}\n          name=\"slug\"\n          value={value}\n          onChange={(e) => {\n            onChange(sanitizeSlug(e.target.value))\n            setSlugError(undefined)\n          }}\n          onInvalid={(e) => {\n            e.preventDefault()\n            setSlugError(authLocalization.auth.fieldRequired)\n          }}\n          aria-invalid={!!slugError}\n          placeholder={localization.slugPlaceholder}\n          required\n          disabled={disabled}\n        />\n\n        {checkSlugEnabled && !!value.trim() && value.trim() !== currentSlug && (\n          <InputGroupAddon align=\"inline-end\">\n            {checkSlugData?.status ? (\n              <Check className=\"size-4 text-foreground\" />\n            ) : checkSlugError ? (\n              <X className=\"size-4 text-destructive\" />\n            ) : (\n              <Spinner />\n            )}\n          </InputGroupAddon>\n        )}\n      </InputGroup>\n\n      <FieldError>{slugError}</FieldError>\n    </Field>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/slug-field.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/team-switcher.tsx",
      "content": "\"use client\"\n\nimport type {\n  ListedUserTeam,\n  OrganizationTeamsAuthClient\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useListUserTeams,\n  useSetActiveTeam\n} from \"@better-auth-ui/react/plugins/organization\"\nimport { Check, ChevronsUpDown, Users } from \"lucide-react\"\nimport { type ReactElement, useState } from \"react\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger\n} from \"@/components/ui/dropdown-menu\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { cn } from \"cn\"\n\nexport type TeamSwitcherProps = {\n  organizationId: string\n  teamId?: string | null\n  onTeamChange?: (team: ListedUserTeam | null) => void\n  syncSession?: boolean\n  allowClear?: boolean\n  className?: string\n  align?: \"center\" | \"end\" | \"start\"\n  trigger?: ReactElement\n}\n\nexport function TeamSwitcher({\n  organizationId,\n  teamId,\n  onTeamChange,\n  syncSession = false,\n  allowClear = true,\n  className,\n  align,\n  trigger\n}: TeamSwitcherProps) {\n  const { authClient } = useAuth<OrganizationTeamsAuthClient>()\n  const { localization } = useAuthPlugin(organizationPlugin)\n  const [open, setOpen] = useState(false)\n  const teams = useListUserTeams(authClient, {\n    query: { organizationId }\n  })\n  const setActiveTeam = useSetActiveTeam(authClient)\n  const selectedTeam = teams.data?.find((team) => team.id === teamId)\n\n  function selectTeam(team: ListedUserTeam | null) {\n    onTeamChange?.(team)\n    setOpen(false)\n\n    if (syncSession) {\n      setActiveTeam.mutate({ organizationId, teamId: team?.id ?? null })\n    }\n  }\n\n  return (\n    <DropdownMenu open={open} onOpenChange={setOpen}>\n      {trigger ? (\n        <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>\n      ) : (\n        <DropdownMenuTrigger\n          className={cn(\n            buttonVariants({ variant: \"outline\" }),\n            \"justify-between gap-3\",\n            className\n          )}\n          disabled={teams.isPending || setActiveTeam.isPending}\n        >\n          <span className=\"flex min-w-0 items-center gap-2\">\n            <Users className=\"size-4 shrink-0 text-muted-foreground\" />\n            {teams.isPending ? (\n              <Skeleton className=\"h-4 w-24\" />\n            ) : (\n              <span className=\"truncate\">\n                {selectedTeam?.name ?? localization.selectTeam}\n              </span>\n            )}\n          </span>\n          <ChevronsUpDown className=\"size-4 shrink-0 text-muted-foreground\" />\n        </DropdownMenuTrigger>\n      )}\n      <DropdownMenuContent align={align} className=\"min-w-56\">\n        {allowClear && (\n          <DropdownMenuItem onClick={() => selectTeam(null)}>\n            <span className=\"min-w-0 flex-1 truncate\">\n              {localization.allTeams}\n            </span>\n            {!teamId && <Check className=\"size-4\" />}\n          </DropdownMenuItem>\n        )}\n        {teams.data?.map((team) => (\n          <DropdownMenuItem key={team.id} onClick={() => selectTeam(team)}>\n            <span className=\"min-w-0 flex-1 truncate\">{team.name}</span>\n            {team.id === teamId && <Check className=\"size-4\" />}\n          </DropdownMenuItem>\n        ))}\n        {!teams.isPending && teams.data?.length === 0 && (\n          <div className=\"px-2 py-1.5 text-sm text-muted-foreground\">\n            {localization.noTeams}\n          </div>\n        )}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/team-switcher.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/user-invitation-row-skeleton.tsx",
      "content": "\"use client\"\n\nimport { Item, ItemContent, ItemMedia } from \"@/components/ui/item\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\n\n/**\n * Placeholder row matching `UserInvitationRow` while invitations load.\n */\nexport function UserInvitationRowSkeleton() {\n  return (\n    <Item>\n      <ItemMedia>\n        <Skeleton className=\"size-10 shrink-0 rounded-md\" />\n      </ItemMedia>\n      <ItemContent>\n        <Skeleton className=\"h-4 w-40 rounded-md\" />\n        <Skeleton className=\"h-3 w-28 rounded-md\" />\n      </ItemContent>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/user-invitation-row-skeleton.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/user-invitation-row.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport {\n  useAcceptInvitation,\n  useRejectInvitation\n} from \"@better-auth-ui/react/plugins/organization\"\nimport type { Invitation } from \"better-auth/client\"\nimport { Check, Clock, X } from \"lucide-react\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Item,\n  ItemActions,\n  ItemContent,\n  ItemDescription,\n  ItemMedia,\n  ItemTitle\n} from \"@/components/ui/item\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\n\nexport type UserInvitationRowProps = {\n  invitation: Invitation & { organizationName?: string }\n}\n\n/**\n * Single invitation row with accept/reject actions for the current user.\n */\nexport function UserInvitationRow({ invitation }: UserInvitationRowProps) {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const { localization: organizationLocalization, roles } =\n    useAuthPlugin(organizationPlugin)\n\n  const { mutate: acceptInvitation, isPending: isAccepting } =\n    useAcceptInvitation(authClient)\n\n  const { mutate: rejectInvitation, isPending: isRejecting } =\n    useRejectInvitation(authClient)\n\n  return (\n    <Item>\n      <ItemMedia variant=\"icon\">\n        <Clock />\n      </ItemMedia>\n      <ItemContent>\n        <ItemTitle>\n          {invitation.organizationName}\n          <Badge variant=\"secondary\">\n            {roles?.[invitation.role] ?? invitation.role}\n          </Badge>\n        </ItemTitle>\n        <ItemDescription>\n          {new Date(invitation.createdAt).toLocaleString(undefined, {\n            dateStyle: \"medium\",\n            timeStyle: \"short\"\n          })}\n        </ItemDescription>\n      </ItemContent>\n      <ItemActions>\n        <Button\n          variant=\"outline\"\n          size=\"sm\"\n          disabled={isAccepting || isRejecting}\n          onClick={() => acceptInvitation({ invitationId: invitation.id })}\n        >\n          {isAccepting ? <Spinner /> : <Check />}\n\n          {organizationLocalization.accept}\n        </Button>\n\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          className=\"size-8 text-destructive\"\n          disabled={isAccepting || isRejecting}\n          onClick={() => rejectInvitation({ invitationId: invitation.id })}\n          aria-label={organizationLocalization.rejectInvitation}\n        >\n          {isRejecting ? <Spinner /> : <X />}\n        </Button>\n      </ItemActions>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/user-invitation-row.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/user-invitations-empty.tsx",
      "content": "\"use client\"\n\nimport { useAuthPlugin } from \"@better-auth-ui/react\"\nimport { MailWarning, Send } from \"lucide-react\"\n\nimport {\n  Empty,\n  EmptyDescription,\n  EmptyHeader,\n  EmptyMedia,\n  EmptyTitle\n} from \"@/components/ui/empty\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\n\n/**\n * Empty state for `UserInvitations`.\n */\nexport function UserInvitationsEmpty({\n  verificationRequired = false\n}: {\n  verificationRequired?: boolean\n}) {\n  const { localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n\n  return (\n    <Empty>\n      <EmptyHeader>\n        <EmptyMedia variant=\"icon\">\n          {verificationRequired ? <MailWarning /> : <Send />}\n        </EmptyMedia>\n        <EmptyTitle>\n          {verificationRequired\n            ? organizationLocalization.verifyEmailToViewInvitations\n            : organizationLocalization.noInvitations}\n        </EmptyTitle>\n        <EmptyDescription>\n          {verificationRequired\n            ? organizationLocalization.verifyEmailToViewInvitationsDescription\n            : organizationLocalization.userInvitationsEmptyDescription}\n        </EmptyDescription>\n      </EmptyHeader>\n    </Empty>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/user-invitations-empty.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/organization/user-invitations.tsx",
      "content": "\"use client\"\n\nimport type { OrganizationAuthClient } from \"@better-auth-ui/core/plugins/organization\"\nimport { useAuth, useAuthPlugin, useSession } from \"@better-auth-ui/react\"\nimport { useListUserInvitations } from \"@better-auth-ui/react/plugins/organization\"\nimport { Fragment } from \"react\"\n\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { ItemGroup, ItemSeparator } from \"@/components/ui/item\"\nimport { organizationPlugin } from \"@/lib/auth/organization-plugin\"\nimport { UserInvitationRow } from \"./user-invitation-row\"\nimport { UserInvitationRowSkeleton } from \"./user-invitation-row-skeleton\"\nimport { UserInvitationsEmpty } from \"./user-invitations-empty\"\n\nexport type UserInvitationsProps = {\n  className?: string\n}\n\n/**\n * Organization invitations for the signed-in user. Always renders the section\n * card; uses `UserInvitationsEmpty` when there are no pending invitations.\n */\nexport function UserInvitations({ className }: UserInvitationsProps) {\n  const { authClient } = useAuth<OrganizationAuthClient>()\n  const { localization: organizationLocalization } =\n    useAuthPlugin(organizationPlugin)\n  const session = useSession(authClient)\n  const emailVerified = session.data?.user.emailVerified === true\n\n  const { data: invitations, isPending } = useListUserInvitations(authClient, {\n    enabled: emailVerified\n  })\n\n  return (\n    <div className={className}>\n      <div className=\"flex flex-col gap-3\">\n        <h2 className=\"truncate text-sm font-semibold\">\n          {organizationLocalization.invitations}\n        </h2>\n\n        <Card className=\"p-0\">\n          <CardContent className=\"p-0\">\n            {session.isPending || (emailVerified && isPending) ? (\n              <ItemGroup>\n                <UserInvitationRowSkeleton />\n              </ItemGroup>\n            ) : !invitations?.length ? (\n              <UserInvitationsEmpty verificationRequired={!emailVerified} />\n            ) : (\n              <ItemGroup className=\"gap-0\">\n                {invitations.map((invitation, index) => (\n                  <Fragment key={invitation.id}>\n                    {index > 0 && <ItemSeparator />}\n                    <UserInvitationRow invitation={invitation} />\n                  </Fragment>\n                ))}\n              </ItemGroup>\n            )}\n          </CardContent>\n        </Card>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/organization/user-invitations.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/use-is-hydrated.ts",
      "content": "\"use client\"\n\nimport { useSyncExternalStore } from \"react\"\n\n/**\n * Returns `true` once the component is mounted on the client (hydrated) and\n * `false` while rendering on the server, so client-only reads (e.g.\n * `sessionStorage`) stay safe during SSR.\n *\n * @returns Whether the component has hydrated on the client.\n */\nexport function useIsHydrated() {\n  const subscribe = () => () => {}\n  return useSyncExternalStore(\n    subscribe,\n    () => true,\n    () => false\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/use-is-hydrated.ts"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/user/user-avatar.tsx",
      "content": "\"use client\"\n\nimport type { UsernameAuthClient } from \"@better-auth-ui/core/plugins/username\"\nimport { useAuth, useSession } from \"@better-auth-ui/react\"\nimport type { User } from \"better-auth\"\nimport { User2 } from \"lucide-react\"\nimport type { ReactNode } from \"react\"\n\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { cn } from \"cn\"\n\nexport type UserAvatarProps = {\n  className?: string\n  fallback?: ReactNode\n  isPending?: boolean\n  /** @remarks `User` */\n  user?: User & { username?: string | null; displayUsername?: string | null }\n}\n\n/**\n * Display a user's avatar using session information or an explicit user prop.\n *\n * Renders a circular avatar that shows the user's image when available, a fallback node if provided, or the user's first two initials; while the session is loading (or when `isPending` is true) and no `user` prop is supplied, renders a skeleton placeholder.\n *\n * @param className - Additional CSS classes applied to the avatar container\n * @param user - Optional user object to display instead of the session user\n * @param isPending - When true, treat the component as loading and show the skeleton if no `user` is provided\n * @param fallback - Node to render inside the avatar fallback area before initials or the default icon\n * @returns The avatar element to render (JSX)\n */\nexport function UserAvatar({\n  className,\n  user,\n  isPending,\n  fallback\n}: UserAvatarProps) {\n  const { authClient } = useAuth<UsernameAuthClient>()\n  const { data: session, isPending: sessionPending } = useSession(authClient, {\n    enabled: !user && !isPending\n  })\n\n  if ((isPending || sessionPending) && !user) {\n    return <Skeleton className={cn(\"size-8 rounded-full\", className)} />\n  }\n\n  const resolvedUser = user ?? session?.user\n\n  const initials = (\n    resolvedUser?.username ||\n    resolvedUser?.name ||\n    resolvedUser?.email\n  )\n    ?.slice(0, 2)\n    .toUpperCase()\n\n  return (\n    <Avatar\n      className={cn(\n        \"size-8 bg-muted text-foreground text-sm rounded-full\",\n        className\n      )}\n    >\n      <AvatarImage\n        src={resolvedUser?.image ?? undefined}\n        alt={\n          resolvedUser?.displayUsername ||\n          resolvedUser?.name ||\n          resolvedUser?.email\n        }\n      />\n\n      <AvatarFallback className=\"text-muted-foreground!\">\n        {fallback || initials || <User2 className=\"size-4\" />}\n      </AvatarFallback>\n    </Avatar>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/user/user-avatar.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/user/user-view.tsx",
      "content": "\"use client\"\n\nimport type { UsernameAuthClient } from \"@better-auth-ui/core/plugins/username\"\nimport { useAuth, useSession } from \"@better-auth-ui/react\"\nimport type { User } from \"better-auth\"\n\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { cn } from \"cn\"\nimport { UserAvatar } from \"./user-avatar\"\n\nexport type UserViewProps = {\n  className?: string\n  isPending?: boolean\n  /**\n   * When true, the subtitle line (email when name/username is shown) is hidden.\n   * @default false\n   */\n  hideSubtitle?: boolean\n  /** @remarks `User` */\n  user?: Partial<User> & {\n    username?: string | null\n    displayUsername?: string | null\n  }\n}\n\n/**\n * Render a compact user item with an avatar, a primary label (display username, name, or email), and an optional subtitle (email).\n *\n * @param isPending - If true and no `user` prop is provided, renders a loading skeleton instead of user details\n * @param className - Additional CSS classes applied to the outer container\n * @param hideSubtitle - When true, omits the muted subtitle row under the primary label\n * @param user - Optional user object to display; when omitted the current session user is used\n * @returns A React element showing the user's avatar with their identifying information\n */\nexport function UserView({\n  className,\n  isPending,\n  hideSubtitle = false,\n  user\n}: UserViewProps) {\n  const { authClient } = useAuth<UsernameAuthClient>()\n  const { data: session, isPending: sessionPending } = useSession(authClient, {\n    enabled: !user && !isPending\n  })\n\n  const resolvedUser = user ?? session?.user\n\n  if ((isPending || sessionPending) && !user) {\n    return (\n      <div className={cn(\"flex items-center gap-2 min-w-0\", className)}>\n        <UserAvatar isPending />\n\n        <div className=\"grid flex-1 gap-1 text-left text-sm\">\n          <Skeleton className=\"h-4 w-24\" />\n\n          {!hideSubtitle && <Skeleton className=\"h-3 w-32\" />}\n        </div>\n      </div>\n    )\n  }\n\n  return (\n    <div className={cn(\"flex items-center gap-2 min-w-0\", className)}>\n      <UserAvatar user={resolvedUser as User | undefined} />\n\n      <div className=\"grid min-w-0 flex-1 text-left text-sm leading-tight\">\n        <span className=\"truncate font-medium text-foreground\">\n          {resolvedUser?.displayUsername ||\n            resolvedUser?.name ||\n            resolvedUser?.email}\n        </span>\n\n        {!hideSubtitle &&\n          (resolvedUser?.displayUsername || resolvedUser?.name) && (\n            <span className=\"text-muted-foreground truncate text-xs\">\n              {resolvedUser?.email}\n            </span>\n          )}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/user/user-view.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/lib/auth/auth-plugin.ts",
      "content": "import type {\n  AuthPluginComponents,\n  AuthPlugin as AuthPluginPrimitive\n} from \"@better-auth-ui/react\"\n\ndeclare module \"@better-auth-ui/core\" {\n  /** Widens `useAuth().plugins` to the shadcn-typed `AuthPlugin`. */\n  interface AuthPluginRegister {\n    shadcn: AuthPlugin\n  }\n}\n\n/** Props the shadcn `<Auth>` router spreads onto plugin-contributed auth views. */\nexport type AuthViewProps = {\n  className?: string\n  socialLayout?: \"auto\" | \"horizontal\" | \"vertical\" | \"grid\"\n  socialPosition?: \"top\" | \"bottom\"\n}\n\n/** Props the shadcn `<Settings>` router spreads onto plugin-contributed settings views. */\nexport type SettingsViewProps = {\n  className?: string\n}\n\n/** Shadcn plugin type. Plugin authors import this from `@/lib/auth/auth-plugin`. */\nexport type AuthPlugin = AuthPluginPrimitive<\n  AuthPluginComponents,\n  AuthViewProps,\n  SettingsViewProps\n>\n",
      "type": "registry:lib",
      "target": "@lib/auth/auth-plugin.ts"
    },
    {
      "path": "examples/start-shadcn-example/src/lib/auth/organization-plugin.tsx",
      "content": "import { createAuthPlugin } from \"@better-auth-ui/core\"\nimport {\n  organizationPlugin as coreOrganizationPlugin,\n  type OrganizationLocalization,\n  type OrganizationPluginOptions\n} from \"@better-auth-ui/core/plugins/organization\"\nimport { Briefcase } from \"lucide-react\"\n\nimport { AcceptInvitation } from \"@/components/auth/organization/accept-invitation\"\nimport { OrganizationsSettings } from \"@/components/auth/organization/organizations-settings\"\n\nexport const organizationPlugin = createAuthPlugin(\n  coreOrganizationPlugin.id,\n  (options: OrganizationPluginOptions = {}) => {\n    const core = coreOrganizationPlugin(options)\n\n    return {\n      ...core,\n      localization: core.localization as OrganizationLocalization,\n      views: {\n        auth: { acceptInvitation: AcceptInvitation }\n      },\n      settingsTabs: [\n        {\n          view: \"organizations\",\n          label: (\n            <>\n              <Briefcase className=\"text-muted-foreground\" />\n              {core.localization.organizations}\n            </>\n          ),\n          component: OrganizationsSettings\n        }\n      ]\n    }\n  }\n)\n",
      "type": "registry:lib",
      "target": "@lib/auth/organization-plugin.tsx"
    }
  ],
  "type": "registry:component"
}