Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"@emoji-mart/data": "1.2.1",
"@emoji-mart/react": "1.1.1",
"@emotion/cache": "11.14.0",
"@emotion/css": "11.13.5",
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.1",
"@fontsource-variable/geist": "5.3.0",
Expand Down
16 changes: 0 additions & 16 deletions site/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions site/src/components/GitDeviceAuth/GitDeviceAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ export const GitDeviceAuth: FC<GitDeviceAuthProps> = ({
<br />
Then open the link below and paste it:
</p>
<div className="m-4 flex flex-col gap-1">
<div className="m-4 flex justify-center">
<Link
className="flex items-center justify-center gap-2 text-base"
className="inline-flex items-center gap-2 p-0 text-base font-medium [&_svg]:size-icon-xs [&_svg]:p-0"
href={externalAuthDevice.verification_uri}
target="_blank"
rel="noreferrer"
Expand Down
135 changes: 74 additions & 61 deletions site/src/pages/CreateTokenPage/CreateTokenForm.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { css } from "@emotion/css";
import MenuItem from "@mui/material/MenuItem";
import TextField from "@mui/material/TextField";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import type { FormikContextType } from "formik";
import { type FC, useEffect, useState } from "react";
import { type FC, useEffect, useId, useState } from "react";
import { useNavigate } from "react-router";
import { Button } from "#/components/Button/Button";
import {
Expand All @@ -13,6 +10,16 @@ import {
FormSection,
HorizontalForm,
} from "#/components/Form/Form";
import { FormField } from "#/components/FormField/FormField";
import { Input } from "#/components/Input/Input";
import { Label } from "#/components/Label/Label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "#/components/Select/Select";
import { Spinner } from "#/components/Spinner/Spinner";
import { getFormHelpers, onChangeTrimmed } from "#/utils/formUtils";
import {
Expand Down Expand Up @@ -45,6 +52,8 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
now,
}) => {
const navigate = useNavigate();
const lifetimeId = useId();
const expiresOnId = useId();

const [expDays, setExpDays] = useState<number>(1);
const [lifetimeDays, setLifetimeDays] = useState<number | string>(
Expand All @@ -68,16 +77,16 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
<FormSection
title="Name"
description="What is this token for?"
classes={{ sectionInfo: classNames.sectionInfo }}
classes={{ sectionInfo: "min-w-[300px]" }}
>
<FormFields>
<TextField
{...getFieldHelpers("name")}
<FormField
field={getFieldHelpers("name")}
label="Name"
required
onChange={onChangeTrimmed(form, () => setFormError(undefined))}
autoFocus
fullWidth
className="w-full"
/>
</FormFields>
</FormSection>
Expand All @@ -98,59 +107,69 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
"Please set a token expiration."
)
}
classes={{ sectionInfo: classNames.sectionInfo }}
classes={{ sectionInfo: "min-w-[300px]" }}
>
<FormFields>
<div className="flex flex-row gap-4">
<TextField
select
label="Lifetime"
required
defaultValue={determineDefaultLtValue(maxTokenLifetime)}
onChange={(event) => {
void setLifetimeDays(event.target.value);
}}
fullWidth
>
{filterByMaxTokenLifetime(maxTokenLifetime).map((lt) => (
<MenuItem key={lt.label} value={lt.value}>
{lt.label}
</MenuItem>
))}
<MenuItem
key={customLifetimeDay.label}
value={customLifetimeDay.value}
<div className="flex flex-col gap-2 flex-1">
<Label htmlFor={lifetimeId}>
Lifetime{" "}
<span className="text-xs font-bold text-content-destructive">
*
</span>
</Label>
<Select
value={String(lifetimeDays)}
onValueChange={setLifetimeDays}
>
{customLifetimeDay.label}
</MenuItem>
</TextField>
<SelectTrigger id={lifetimeId} className="w-full">
Comment on lines +121 to +125

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add an interaction story for the new lifetime selector

When this form replaces the MUI lifetime control with the Radix selector and conditionally reveals the custom expiration input, the existing CreateTokenPage story still only renders the default state and has no play function. Add a story interaction that opens the selector, chooses a preset and Custom, and verifies the expiration input appears so this user-visible control migration has the required behavioral coverage.

AGENTS.md reference: site/AGENTS.md:L9-L10

Useful? React with πŸ‘Β / πŸ‘Ž.

<SelectValue />
</SelectTrigger>
<SelectContent>
{filterByMaxTokenLifetime(maxTokenLifetime).map((lt) => (
<SelectItem key={lt.label} value={String(lt.value)}>
{lt.label}
</SelectItem>
))}
<SelectItem value={String(customLifetimeDay.value)}>
{customLifetimeDay.label}
</SelectItem>
</SelectContent>
</Select>
</div>

{lifetimeDays === "custom" && (
<TextField
type="date"
label="Expires on"
defaultValue={dayjs().add(expDays, "day").format("YYYY-MM-DD")}
onChange={(event) => {
const lt = Math.ceil(
dayjs(event.target.value).diff(dayjs(), "day", true),
);
setExpDays(lt);
}}
inputProps={{
"data-pixel": "ignore",
min: dayjs().add(1, "day").format("YYYY-MM-DD"),
max: maxTokenLifetime
? dayjs()
.add(maxTokenLifetime / NANO_HOUR / 24, "day")
.format("YYYY-MM-DD")
: undefined,
required: true,
}}
fullWidth
InputLabelProps={{
required: true,
}}
/>
<div className="flex flex-col gap-2 flex-1">
<Label htmlFor={expiresOnId}>
Expires on{" "}
<span className="text-xs font-bold text-content-destructive">
*
</span>
</Label>
<Input
id={expiresOnId}
type="date"
data-pixel="ignore"
defaultValue={dayjs()
.add(expDays, "day")
.format("YYYY-MM-DD")}
min={dayjs().add(1, "day").format("YYYY-MM-DD")}
max={
maxTokenLifetime
? dayjs()
.add(maxTokenLifetime / NANO_HOUR / 24, "day")
.format("YYYY-MM-DD")
: undefined
}
required
onChange={(event) => {
const lt = Math.ceil(
dayjs(event.target.value).diff(dayjs(), "day", true),
);
setExpDays(lt);
}}
/>
</div>
)}
</div>
</FormFields>
Expand All @@ -168,9 +187,3 @@ export const CreateTokenForm: FC<CreateTokenFormProps> = ({
</HorizontalForm>
);
};

const classNames = {
sectionInfo: css`
min-width: 300px;
`,
};
14 changes: 10 additions & 4 deletions site/src/pages/ExternalAuthPage/ExternalAuthPageView.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Link from "@mui/material/Link";
import { ExternalLinkIcon, RotateCwIcon } from "lucide-react";
import type { FC, ReactNode } from "react";
import type { ApiErrorResponse } from "#/api/errors";
import type { ExternalAuth, ExternalAuthDevice } from "#/api/typesGenerated";
import { Alert } from "#/components/Alert/Alert";
import { Avatar } from "#/components/Avatar/Avatar";
import { GitDeviceAuth } from "#/components/GitDeviceAuth/GitDeviceAuth";
import { Link } from "#/components/Link/Link";
import { SignInLayout } from "#/components/SignInLayout/SignInLayout";
import {
Tooltip,
Expand Down Expand Up @@ -56,6 +56,8 @@ const ExternalAuthPageView: FC<ExternalAuthPageViewProps> = ({
href={externalAuth.app_install_url}
target="_blank"
rel="noreferrer"
showExternalIcon={false}
className="p-0 text-sm font-medium"
>
{installTheApp}
</Link>
Expand Down Expand Up @@ -88,6 +90,8 @@ const ExternalAuthPageView: FC<ExternalAuthPageViewProps> = ({
href={install.account.profile_url}
target="_blank"
rel="noreferrer"
showExternalIcon={false}
className="p-0 after:hover:content-none"
>
<Avatar
src={install.account.avatar_url}
Expand All @@ -107,7 +111,7 @@ const ExternalAuthPageView: FC<ExternalAuthPageViewProps> = ({
</div>
)}

<div className="m-4 flex flex-col gap-1">
<div className="m-4 flex flex-col items-center gap-1">
{!hasInstallations && externalAuth.app_installable && (
<Alert severity="warning" className="m-4">
You must {installTheApp} to clone private repositories. Accounts
Expand All @@ -122,16 +126,18 @@ const ExternalAuthPageView: FC<ExternalAuthPageViewProps> = ({
href={externalAuth.app_install_url}
target="_blank"
rel="noreferrer"
className="flex items-center justify-center gap-2 text-base"
showExternalIcon={false}
className="inline-flex items-center gap-2 p-0 text-base font-medium [&_svg]:size-icon-xs [&_svg]:p-0"
>
<ExternalLinkIcon className="size-icon-xs" />
{externalAuth.installations.length > 0 ? "Configure" : "Install"}{" "}
the {externalAuth.display_name} App
</Link>
)}
<Link
className="flex items-center justify-center gap-2 text-base"
className="inline-flex items-center gap-2 p-0 text-base font-medium [&_svg]:size-icon-xs [&_svg]:p-0"
href="#"
showExternalIcon={false}
onClick={() => {
onReauthenticate();
}}
Expand Down
Loading
Loading