drive-reauth-card.tsx3.8 KBView on GitHub
'use client';

import { authClient } from '@/modules/auth/utils/auth-client';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

/**
 * Why this Drive folder can only show pinned files — the card a user without the browsing
 * scope must see instead of a silently empty mount.
 *
 * `drive.file` is a per-file scope that can see nothing except files Cedar created or the
 * user hand-picked through the Picker; `drive.readonly` is what turns the mount into a real
 * listing. Which of those a user has is not something re-authorizing can always change, so
 * the card has two modes and `canUpgrade` picks between them:
 *
 *  - `canUpgrade` true — Cedar is requesting the browsing scope and this user's stored grant
 *    predates it. Re-linking genuinely upgrades them, so offer the button.
 *  - `canUpgrade` false — Cedar is not requesting the browsing scope at all (withheld pending
 *    Google's restricted-scope verification). Re-linking would return the identical grant, so
 *    explain the limit and point at the Picker rather than sending the user through Google to
 *    no effect.
 *
 * The `'none'` case always keeps its button: connecting Google is worthwhile either way, it
 * just yields the Picker rather than a full listing.
 *
 * Same `linkSocial` re-link flow as `google-slides-manager.tsx`; only the copy and the
 * callback differ.
 */
export function DriveReauthCard({
  accessLevel,
  canUpgrade = false,
  callbackURL,
  className,
}: {
  /** `'file'` = picker-only grant, `'none'` = no Google Drive scope at all. */
  accessLevel: 'none' | 'file';
  /**
   * Whether re-linking would actually raise the grant — the server's `needsReauth`. Defaults
   * to `false` so a caller that has not plumbed it through cannot accidentally show a button
   * that does nothing.
   */
  canUpgrade?: boolean;
  /** Where Google returns the user. Defaults to the page they are standing on. */
  callbackURL?: string;
  className?: string;
}) {
  const resolvedCallback =
    callbackURL ??
    (typeof window !== 'undefined'
      ? `${window.location.origin}${window.location.pathname}${window.location.search}`
      : '/');

  // Connecting a Google account is always worth offering; re-linking an already-connected
  // one is only worth offering when it would actually widen the grant.
  const showButton = accessLevel === 'none' || canUpgrade;

  return (
    <div
      data-testid="drive-reauth-card"
      data-access-level={accessLevel}
      data-shows-action={showButton ? 'true' : 'false'}
      className={cn(
        'rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-800 dark:bg-amber-950/30',
        className,
      )}
    >
      <p className="text-sm font-medium text-amber-900 dark:text-amber-200">
        {showButton
          ? 'Google Drive browsing needs authorization'
          : 'Google Drive browsing is unavailable'}
      </p>
      <p className="mt-1 text-sm text-amber-700 dark:text-amber-400">
        {accessLevel === 'none'
          ? 'Cedar has no Google Drive access on this account. Connect Google to add Drive files to this deal.'
          : canUpgrade
            ? 'Your Google account was connected before Drive browsing was added, so Cedar can only see files you hand it through the picker. Re-authorize to list your Drive here.'
            : 'Cedar can only see Drive files you hand it through the picker. Browsing your whole Drive needs a Google permission we are still getting verified — until then, use "Add file" to pick files yourself.'}
      </p>
      {showButton && (
        <Button
          size="sm"
          className="mt-3 cursor-pointer"
          onClick={() =>
            authClient.linkSocial({ provider: 'google', callbackURL: resolvedCallback })
          }
        >
          {accessLevel === 'none' ? 'Connect Google' : 'Re-authorize Google'}
        </Button>
      )}
    </div>
  );
}