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>
);
}