mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 18:37:24 +00:00
fix(team): aria-disabled buttons look disabled; guarded plan handlers; Send/Create disabled when empty; Leave back in the SSO card with a divider; warning tone on the blocking bar; cap hint under the row; switcher icons
This commit is contained in:
@@ -76,7 +76,11 @@ export function UsageSummary({
|
||||
</div>
|
||||
<Progress
|
||||
value={includedUsagePercent(entitlement)}
|
||||
tone={includedUsagePercent(entitlement) >= 100 ? 'warning' : 'default'}
|
||||
tone={
|
||||
includedUsagePercent(entitlement) >= 100 && !entitlement.overage.optedIn
|
||||
? 'warning'
|
||||
: 'default'
|
||||
}
|
||||
aria-label={t('auto.components.billing.sandboxHoursUsedLabel', 'Sandbox hours used')}
|
||||
/>
|
||||
{entitlement.overageSeconds === 0 &&
|
||||
@@ -167,6 +171,14 @@ export function OverageControls({
|
||||
value2: formatCents(entitlement.offers?.team.defaultSpendCapCents ?? 2500, locale)
|
||||
}
|
||||
)
|
||||
const descriptionOn = t(
|
||||
'auto.components.billing.overageDescriptionOn',
|
||||
'Optional overage at {{value0}}/hour ({{value1}}/hour on Large), up to a monthly spend cap you set.',
|
||||
{
|
||||
value0: formatCents(overage.centsPerHour, locale),
|
||||
value1: formatCents(Math.round(overage.centsPerHour * largeWeight), locale)
|
||||
}
|
||||
)
|
||||
const capPercent =
|
||||
overage.spendCapCents > 0
|
||||
? Math.min(100, Math.round((overage.spentCents / overage.spendCapCents) * 100))
|
||||
@@ -175,6 +187,7 @@ export function OverageControls({
|
||||
<div className="space-y-1.5">
|
||||
<Progress
|
||||
value={capPercent}
|
||||
tone={capPercent >= 100 ? 'warning' : 'default'}
|
||||
aria-label={t('auto.components.billing.capUsedLabel', 'Overage spend cap used')}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground tabular-nums">
|
||||
@@ -222,7 +235,7 @@ export function OverageControls({
|
||||
{t('auto.components.billing.overageTitle', 'Overage')}
|
||||
</p>
|
||||
<p id={descriptionId} className="text-xs text-muted-foreground">
|
||||
{description}
|
||||
{optedIn ? descriptionOn : description}
|
||||
</p>
|
||||
</div>
|
||||
<SettingsDisabledControlTooltip
|
||||
@@ -287,14 +300,6 @@ export function OverageControls({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{capValid && entitlement.reason === 'overage_cap_reached' ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(
|
||||
'auto.components.billing.capReachedHint',
|
||||
'Raise the cap to keep starting sandboxes. Your files are safe.'
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
{capValid ? null : (
|
||||
<p id={capErrorId} className="text-xs text-destructive">
|
||||
{t('auto.components.billing.capInvalid', 'Enter a whole dollar amount.')}
|
||||
@@ -318,6 +323,14 @@ export function OverageControls({
|
||||
{t('auto.components.billing.saveCap', 'Save')}
|
||||
</Button>
|
||||
) : null}
|
||||
{capValid && entitlement.reason === 'overage_cap_reached' ? (
|
||||
<p className="basis-full text-xs text-muted-foreground">
|
||||
{t(
|
||||
'auto.components.billing.capReachedHint',
|
||||
'Raise the cap to keep starting sandboxes. Your files are safe.'
|
||||
)}
|
||||
</p>
|
||||
) : null}
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -151,7 +151,7 @@ export function TeamPlanCard({
|
||||
type="button"
|
||||
className="w-full"
|
||||
aria-disabled={busy || undefined}
|
||||
onClick={onCheckout}
|
||||
onClick={busy ? undefined : onCheckout}
|
||||
data-plans-primary=""
|
||||
>
|
||||
{busy ? <LoadingSpinner /> : null}
|
||||
@@ -188,7 +188,7 @@ export function TeamPlanCard({
|
||||
variant="outline"
|
||||
className="w-full"
|
||||
aria-disabled={busy || undefined}
|
||||
onClick={onManage}
|
||||
onClick={busy ? undefined : onManage}
|
||||
data-plans-primary=""
|
||||
>
|
||||
{t('auto.components.billing.manageBilling', 'Manage billing')}
|
||||
|
||||
@@ -53,7 +53,7 @@ export function TeamCreateView(props: TeamCreateViewProps): React.JSX.Element {
|
||||
className="mt-4 space-y-2"
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
if (trimmed && !props.creating) {
|
||||
if (trimmed && !props.creating && !props.unavailable) {
|
||||
props.onCreate(trimmed)
|
||||
}
|
||||
}}
|
||||
@@ -79,7 +79,8 @@ export function TeamCreateView(props: TeamCreateViewProps): React.JSX.Element {
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
aria-disabled={!trimmed || props.creating || props.unavailable || undefined}
|
||||
disabled={!props.creating && (!trimmed || props.unavailable)}
|
||||
aria-disabled={props.creating || undefined}
|
||||
>
|
||||
{props.creating ? <LoadingSpinner className="size-3.5" /> : null}
|
||||
{t('auto.components.billing.team.createCta', 'Create team')}
|
||||
|
||||
@@ -315,7 +315,8 @@ export function TeamInviteComposer(props: TeamInviteComposerProps): React.JSX.El
|
||||
type="submit"
|
||||
size="sm"
|
||||
// aria-disabled keeps focus here while sending; submit ignores it meanwhile.
|
||||
aria-disabled={sending || valid.length === 0 || undefined}
|
||||
disabled={!sending && valid.length === 0}
|
||||
aria-disabled={sending || undefined}
|
||||
>
|
||||
{sending ? <LoadingSpinner className="size-3.5" /> : <Send />}
|
||||
{valid.length === 0
|
||||
|
||||
@@ -293,8 +293,8 @@ export function TeamPageView(props: TeamPageViewProps): React.JSX.Element {
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
{roster?.canManageMembers && enterprise ? (
|
||||
<div className="rounded-xl border border-border">
|
||||
{(roster?.canManageMembers && enterprise) || selfRow ? (
|
||||
<div className="divide-y divide-border/60 rounded-xl border border-border">
|
||||
{roster?.canManageMembers && enterprise ? (
|
||||
<section aria-labelledby={ssoTitleId} className="flex flex-wrap items-center gap-3 p-4">
|
||||
<div className="min-w-[14rem] flex-1 space-y-0.5">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { Users } from 'lucide-react'
|
||||
import { ChevronsUpDown, User } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
DropdownMenuItem,
|
||||
@@ -87,7 +87,7 @@ export function SidebarTeamSubmenu({
|
||||
)
|
||||
}
|
||||
>
|
||||
<Users className="size-3.5" />
|
||||
{activeName ? <ChevronsUpDown className="size-3.5" /> : <User className="size-3.5" />}
|
||||
<span className="min-w-0 flex-1 truncate">
|
||||
{activeName ?? t('auto.components.sidebar.teamSwitcher.personal', 'Personal account')}
|
||||
</span>
|
||||
|
||||
@@ -14,7 +14,7 @@ const pressClasses = 'motion-safe:active:scale-[0.98]'
|
||||
const disabledSolidClasses = 'disabled:bg-muted disabled:text-muted-foreground'
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md cursor-pointer text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-focus-ring disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md cursor-pointer text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-focus-ring disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
Reference in New Issue
Block a user