Files
cddb430310 feat(studio): scoped pat root branch (#48384)
## Description

This is the Scoped PAT stacked PRs root branch

## How to test

### With the `scopedPAT` enabled (default on staging)

Go to
https://studio-staging-git-scopedpat-merge-token-lists-supabase.vercel.app/dashboard/account/tokens.
- You shouldn't see two tabs anymore
- If you had classic tokens, they should have the _Legacy_ badge
- You can create scoped tokens
- You have a way to copy newly created tokens before closing the form
side panel

### With the `scopedPAT` disabled (use the devtool to override)
- You shouldn't see two tabs anymore
- If you had classic tokens, they should **not** have the _Legacy_ badge
- You can create classic tokens
- You have a way to copy newly created tokens above the list upon form
submission

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Show classic and scoped access tokens together in one list, with
classic tokens labeled “Legacy” when the scoped experience is enabled.
* Add scoped access token creation with a two-step configure → review →
success flow (when enabled).
* Add a dismissible migration notice about scoped tokens with a link to
API docs.
  * Show “View permissions” only for scoped tokens.
* **Bug Fixes**
* Token deletion now supports both classic and scoped tokens with the
correct confirmation and success handling.
* The scoped tokens page now redirects to the unified access tokens
page.
* **Accessibility**
* Improved accessibility by adding a label to the token “more options”
action.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ali Waseem <waseema393@gmail.com>
Co-authored-by: kemal.earth <606977+kemaldotearth@users.noreply.github.com>
2026-08-06 07:40:56 -06:00

73 lines
1.8 KiB
TypeScript

import dayjs from 'dayjs'
import { Badge, TableCell } from 'ui'
import { TimestampInfo } from 'ui-patterns/TimestampInfo'
interface TokenNameCellProps {
name: string
tokenAlias: string
isClassic: boolean
scopedTokensEnabled?: boolean
}
export const TokenNameCell = ({
name,
tokenAlias,
isClassic,
scopedTokensEnabled,
}: TokenNameCellProps) => (
<TableCell className="w-auto max-w-96">
<div className="flex items-center gap-x-2">
<p className="truncate" title={name}>
{name}
</p>
{isClassic && scopedTokensEnabled && <Badge variant="default">Legacy</Badge>}
</div>
<p
className="font-mono text-foreground-lighter truncate text-xs mt-1 max-w-32 sm:max-w-48 lg:max-w-full"
title={tokenAlias}
>
{tokenAlias}
</p>
</TableCell>
)
interface LastUsedCellProps {
lastUsedAt: string | null | undefined
}
export const LastUsedCell = ({ lastUsedAt }: LastUsedCellProps) => (
<TableCell className="text-foreground-light min-w-28">
{lastUsedAt ? (
<TimestampInfo
utcTimestamp={lastUsedAt}
label={dayjs(lastUsedAt).fromNow()}
className="text-sm"
/>
) : (
<p className="text-foreground-light text-sm">Never used</p>
)}
</TableCell>
)
interface ExpiresCellProps {
expiresAt: string | null | undefined
}
export const ExpiresCell = ({ expiresAt }: ExpiresCellProps) => (
<TableCell className="min-w-28 text-foreground-light">
{expiresAt ? (
dayjs(expiresAt).isBefore(dayjs()) ? (
<TimestampInfo utcTimestamp={expiresAt} label="Expired" className="text-sm" />
) : (
<TimestampInfo
utcTimestamp={expiresAt}
label={dayjs(expiresAt).format('DD MMM YYYY')}
className="text-sm"
/>
)
) : (
<p className="text-foreground-light text-sm">Never</p>
)}
</TableCell>
)