Files
supabase/apps/studio/components/interfaces/Organization/OrganizationCard.tsx
T
Joshen LimandKevin Grüneberg 5b8ca1bb66 Chore/fix redeem code UI (#42615)
## Context

Main fix was to address the size of the `OrganizationCard` on the redeem
code page - was taking up the height of the flex div when the number of
cards is <= 3 (Main fix is `max-h-min` in `OrganizationCard`)

### Before
<img width="1270" height="411" alt="image"
src="https://github.com/user-attachments/assets/73228b62-f9db-43c8-813b-cb04c9f6cb2b"
/>

### After
<img width="1255" height="420" alt="image"
src="https://github.com/user-attachments/assets/a1d82753-daba-4c3d-a64d-3bb913ded161"
/>

## Other changes
- Small improvement to perceived loading speed by only rendering the
cards as loaders, text content on the left should render immediately on
page load
- Loading state to consider if the feature flag has been loaded as well
to prevent flicker of UI
- Hide the "Go to org" button when redeemed on the org page already


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

* **Bug Fixes**
* Improved loading/gating so the redemption UI waits for feature flags
and profile data before showing content.
* Disabled submit when the redemption form is invalid to prevent empty
submissions; submit button respects form validity, permissions, and
loading state.

* **UI**
* Conditionally shows upgrade and "Go to organization" options based on
organization plan and current path.
  * Adjusted card layout to better constrain content height.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
2026-02-10 10:36:30 +08:00

72 lines
2.2 KiB
TypeScript

import { useIsMFAEnabled } from 'common'
import { ActionCard } from 'components/ui/ActionCard'
import { useOrgProjectsInfiniteQuery } from 'data/projects/org-projects-infinite-query'
import { Boxes, Lock } from 'lucide-react'
import Link from 'next/link'
import { Fragment } from 'react'
import type { Organization } from 'types'
import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
export const OrganizationCard = ({
organization,
href,
isLink = true,
className,
onClick,
}: {
organization: Organization
href?: string
isLink?: boolean
className?: string
onClick?: () => void
}) => {
const isUserMFAEnabled = useIsMFAEnabled()
const { data } = useOrgProjectsInfiniteQuery({ slug: organization.slug })
const numProjects = data?.pages[0].pagination.count ?? 0
const isMfaRequired = organization.organization_requires_mfa
const renderContent = () => (
<ActionCard
bgColor="bg border"
className={cn(
'flex items-center min-h-[70px] [&>div]:w-full [&>div]:items-center max-h-min',
className
)}
icon={<Boxes size={18} strokeWidth={1} className="text-foreground" />}
title={organization.name}
onClick={onClick}
description={
<div className="flex items-center justify-between text-xs text-foreground-light font-sans">
<div className="flex items-center gap-x-1.5">
<span>{organization.plan.name} Plan</span>
{numProjects > 0 && (
<>
<span>•</span>
<span>
{numProjects} project{numProjects > 1 ? 's' : ''}
</span>
</>
)}
</div>
{isMfaRequired && (
<Tooltip>
<TooltipTrigger className="cursor-default">
<Lock size={12} />
</TooltipTrigger>
<TooltipContent side="bottom" className={!isUserMFAEnabled ? 'w-80' : ''}>
MFA enforced
</TooltipContent>
</Tooltip>
)}
</div>
}
/>
)
if (isLink) {
return <Link href={href ?? `/org/${organization.slug}`}>{renderContent()}</Link>
} else {
return <Fragment>{renderContent()}</Fragment>
}
}