feat(ui): improve mobile responsive layouts

This commit is contained in:
CaIon
2026-04-30 19:53:02 +08:00
parent aa730395f1
commit d46df94f05
84 changed files with 1174 additions and 731 deletions
@@ -206,8 +206,8 @@ export function DynamicPricingBreakdown({
})
return (
<section className='min-w-0 py-4'>
<div className='mb-4 flex items-start gap-2'>
<section className='min-w-0 py-3 sm:py-4'>
<div className='mb-3 flex items-start gap-2 sm:mb-4'>
<span className='mt-0.5 inline-flex size-6 items-center justify-center rounded-full bg-amber-100 text-amber-700 shadow-sm dark:bg-amber-500/20 dark:text-amber-300'>
<TagIcon className='size-3.5' />
</span>
@@ -222,11 +222,71 @@ export function DynamicPricingBreakdown({
</div>
{hasTiers && (
<div className='mb-4'>
<div className='mb-3 sm:mb-4'>
<div className='text-foreground mb-2 text-sm font-semibold'>
{t('Tiered price table')}
</div>
<div className='-mx-4 max-w-[calc(100%+2rem)] overflow-x-auto sm:mx-0 sm:max-w-full'>
<div className='space-y-1.5 sm:hidden'>
{tiers.map((tier, i) => {
const condSummary = formatConditionSummary(tier.conditions, t)
const isMatched =
matchedTierLabel != null &&
matchedTierLabel !== '' &&
tier.label === matchedTierLabel
return (
<div
key={`tier-mobile-${i}`}
className={cn(
'rounded-md border p-2',
isMatched &&
'border-emerald-500/40 bg-emerald-500/10'
)}
>
<div className='mb-1.5 flex flex-wrap items-center gap-1.5'>
<Badge
variant='secondary'
className='bg-blue-100 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300'
>
{tier.label || t('Default')}
</Badge>
{isMatched && (
<Badge
variant='secondary'
className='bg-emerald-100 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300'
>
{t('Matched')}
</Badge>
)}
</div>
{condSummary && (
<div className='text-muted-foreground mb-1.5 text-xs'>
{condSummary}
</div>
)}
<div className='grid grid-cols-2 gap-x-3 gap-y-1.5'>
{visiblePriceFields.map((v) => {
const value = Number(
tier[v.field as string as keyof ParsedTier] || 0
)
return (
<div key={v.field} className='min-w-0'>
<div className='text-muted-foreground truncate text-[10px] font-medium tracking-wider uppercase'>
{t(v.shortLabel)}
</div>
<div className='truncate font-mono text-sm font-semibold'>
{value > 0
? `${symbol}${(value * rate).toFixed(4)}`
: '-'}
</div>
</div>
)
})}
</div>
</div>
)
})}
</div>
<div className='hidden overflow-x-auto sm:block'>
<Table className='text-sm'>
<TableHeader>
<TableRow className='hover:bg-transparent'>
+6 -6
View File
@@ -620,16 +620,16 @@ export function FilterBar(props: FilterBarProps) {
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
<SheetContent
side='right'
className='flex w-full flex-col overflow-hidden p-0 sm:max-w-md'
className='flex h-dvh w-full flex-col overflow-hidden p-0 sm:max-w-md'
>
<SheetHeader className='border-b px-6 py-4'>
<SheetHeader className='border-b px-4 py-3 sm:px-6 sm:py-4'>
<SheetTitle>{t('Filters')}</SheetTitle>
<SheetDescription className='sr-only'>
{t('Filter models by type, endpoint, vendor, group and tags')}
</SheetDescription>
</SheetHeader>
<div className='flex-1 space-y-6 overflow-y-auto px-6 py-5'>
<div className='flex-1 space-y-4 overflow-y-auto px-4 py-4 sm:space-y-6 sm:px-6 sm:py-5'>
<MobileFilterGroup
title={t('Pricing Type')}
value={props.quotaTypeFilter}
@@ -671,7 +671,7 @@ export function FilterBar(props: FilterBarProps) {
<h3 className='text-foreground mb-3 text-sm font-semibold'>
{t('Display Options')}
</h3>
<div className='space-y-4'>
<div className='space-y-3 sm:space-y-4'>
<div className='space-y-2'>
<p className='text-muted-foreground text-xs'>
{t('Price display')}
@@ -704,8 +704,8 @@ export function FilterBar(props: FilterBarProps) {
</div>
</div>
<SheetFooter className='border-t px-6 py-4'>
<div className='flex w-full gap-3'>
<SheetFooter className='border-t px-4 py-3 sm:px-6 sm:py-4'>
<div className='grid w-full grid-cols-2 gap-2 sm:flex sm:gap-3'>
{props.hasActiveFilters && (
<Button
variant='outline'
+3 -3
View File
@@ -68,7 +68,7 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
return (
<div
className={cn(
'group flex flex-col rounded-xl border p-4 transition-colors sm:p-5',
'group flex flex-col rounded-xl border p-3 transition-colors sm:p-5',
'hover:bg-muted/20'
)}
>
@@ -175,12 +175,12 @@ export const ModelCard = memo(function ModelCard(props: ModelCardProps) {
</div>
{/* Description */}
<p className='text-muted-foreground mt-3 line-clamp-1 flex-1 text-[13px] leading-relaxed sm:mt-4 sm:line-clamp-2 sm:min-h-[2.5rem]'>
<p className='text-muted-foreground mt-2 line-clamp-1 flex-1 text-[13px] leading-relaxed sm:mt-4 sm:line-clamp-2 sm:min-h-[2.5rem]'>
{props.model.description || t('No description available.')}
</p>
{/* Footer row 1: group + billing type */}
<div className='mt-3 flex flex-wrap items-center gap-x-2 gap-y-1 sm:mt-4'>
<div className='mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 sm:mt-4'>
{primaryGroup && (
<span className='text-muted-foreground text-xs font-medium'>
{primaryGroup} {t('Groups')}
@@ -783,13 +783,13 @@ export function ModelDetailsDrawer(props: ModelDetailsDrawerProps) {
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent
side='right'
className='flex w-full overflow-hidden p-0 sm:max-w-2xl xl:max-w-3xl'
className='flex h-dvh w-full overflow-hidden p-0 sm:max-w-2xl xl:max-w-3xl'
>
<SheetHeader className='sr-only'>
<SheetTitle>{props.model.model_name}</SheetTitle>
<SheetDescription>{t('Model details')}</SheetDescription>
</SheetHeader>
<div className='flex-1 overflow-y-auto px-5 pt-12 pb-6 sm:px-6'>
<div className='flex-1 overflow-y-auto px-4 pt-11 pb-5 sm:px-6 sm:pt-12 sm:pb-6'>
<ModelDetailsContent {...contentProps} />
</div>
</SheetContent>
@@ -259,15 +259,15 @@ export function PricingToolbar(props: PricingToolbarProps) {
<Sheet open={mobileFiltersOpen} onOpenChange={setMobileFiltersOpen}>
<SheetContent
side='right'
className='flex w-full flex-col overflow-hidden p-0 sm:max-w-md'
className='flex h-dvh w-full flex-col overflow-hidden p-0 sm:max-w-md'
>
<SheetHeader className='border-b px-6 py-4'>
<SheetHeader className='border-b px-4 py-3 sm:px-6 sm:py-4'>
<SheetTitle>{t('Filter')}</SheetTitle>
<SheetDescription>
{t('Filter models by provider, group, type, endpoint, and tags.')}
</SheetDescription>
</SheetHeader>
<div className='flex-1 overflow-y-auto p-4'>
<div className='flex-1 overflow-y-auto p-3 sm:p-4'>
<PricingSidebar
quotaTypeFilter={props.quotaTypeFilter}
endpointTypeFilter={props.endpointTypeFilter}