fix(cabinet): stop long user data overflowing flex rows on mobile

Same flex-overflow class as the Connected-Accounts card — a long
user-controlled string with no truncation pushes its sibling badge/button
out of the card on narrow screens. Found via a scan of the high-risk
screens; fixed with min-w-0 + truncate/break-words on the text column and
shrink-0 on the fixed siblings:
- Balance: promocode-target tariff name vs days-left badge
- ReferralNetwork CampaignDetailPanel: start_parameter + top-referrer username
- ServerManagementSheet: country/server name vs flag emoji
- Contests / Polls: contest/poll title vs prize/reward badge
This commit is contained in:
c0mrade
2026-06-04 23:03:42 +03:00
parent c39c164d51
commit 63863a048b
5 changed files with 30 additions and 26 deletions

View File

@@ -168,15 +168,15 @@ export function ServerManagementSheet({
: 'border-champagne-300/60 bg-champagne-200/40 hover:border-champagne-400' : 'border-champagne-300/60 bg-champagne-200/40 hover:border-champagne-400'
} ${!country.is_available && !isCurrentlyConnected ? 'cursor-not-allowed opacity-50' : ''}`} } ${!country.is_available && !isCurrentlyConnected ? 'cursor-not-allowed opacity-50' : ''}`}
> >
<div className="flex items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<span className="text-lg"> <span className="shrink-0 text-lg">
{willBeAdded ? '' : willBeRemoved ? '' : isSelected ? '✅' : '⚪'} {willBeAdded ? '' : willBeRemoved ? '' : isSelected ? '✅' : '⚪'}
</span> </span>
<div> <div className="min-w-0">
<div className="flex items-center gap-2 font-medium text-dark-100"> <div className="flex min-w-0 items-center gap-2 font-medium text-dark-100">
{country.name} <span className="truncate">{country.name}</span>
{country.has_discount && !isCurrentlyConnected && ( {country.has_discount && !isCurrentlyConnected && (
<span className="rounded bg-success-500/20 px-1.5 py-0.5 text-xs text-success-400"> <span className="shrink-0 rounded bg-success-500/20 px-1.5 py-0.5 text-xs text-success-400">
-{country.discount_percent}% -{country.discount_percent}%
</span> </span>
)} )}
@@ -217,7 +217,7 @@ export function ServerManagementSheet({
</div> </div>
</div> </div>
{country.country_code && ( {country.country_code && (
<span className="text-xl">{getFlagEmoji(country.country_code)}</span> <span className="shrink-0 text-xl">{getFlagEmoji(country.country_code)}</span>
)} )}
</button> </button>
); );

View File

@@ -275,10 +275,10 @@ export default function Balance() {
key={sub.id} key={sub.id}
onClick={() => handlePromocodeActivate(sub.id)} onClick={() => handlePromocodeActivate(sub.id)}
disabled={promocodeLoading} disabled={promocodeLoading}
className="flex w-full items-center justify-between rounded-linear border border-dark-600 bg-dark-700 px-3 py-2 text-sm text-dark-200 transition-colors hover:border-accent-500/50 hover:bg-dark-600" className="flex w-full min-w-0 items-center justify-between gap-3 rounded-linear border border-dark-600 bg-dark-700 px-3 py-2 text-sm text-dark-200 transition-colors hover:border-accent-500/50 hover:bg-dark-600"
> >
<span>{sub.tariff_name}</span> <span className="truncate">{sub.tariff_name}</span>
<span className="text-dark-400"> <span className="shrink-0 text-dark-400">
{t('balance.promocode.daysLeft', '{{count}} дн.', { count: sub.days_left })} {t('balance.promocode.daysLeft', '{{count}} дн.', { count: sub.days_left })}
</span> </span>
</button> </button>

View File

@@ -198,14 +198,14 @@ export default function Contests() {
<div className="grid gap-4 sm:grid-cols-2"> <div className="grid gap-4 sm:grid-cols-2">
{contests.map((contest) => ( {contests.map((contest) => (
<div key={contest.id} className="card"> <div key={contest.id} className="card">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between gap-2">
<div> <div className="min-w-0">
<h3 className="text-lg font-semibold">{contest.name}</h3> <h3 className="break-words text-lg font-semibold">{contest.name}</h3>
{contest.description && ( {contest.description && (
<p className="mt-1 text-sm text-dark-400">{contest.description}</p> <p className="mt-1 text-sm text-dark-400">{contest.description}</p>
)} )}
</div> </div>
<div className="flex items-center gap-1 text-accent-400"> <div className="flex shrink-0 items-center gap-1 text-accent-400">
<TrophyIcon /> <TrophyIcon />
<span className="text-sm font-medium"> <span className="text-sm font-medium">
+{t('contests.days', { count: contest.prize_days })} +{t('contests.days', { count: contest.prize_days })}

View File

@@ -205,9 +205,9 @@ export default function Polls() {
<div className="grid gap-4 sm:grid-cols-2"> <div className="grid gap-4 sm:grid-cols-2">
{polls.map((poll) => ( {polls.map((poll) => (
<div key={poll.id} className="card"> <div key={poll.id} className="card">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between gap-2">
<div className="flex-1"> <div className="min-w-0 flex-1">
<h3 className="text-lg font-semibold">{poll.title}</h3> <h3 className="break-words text-lg font-semibold">{poll.title}</h3>
{poll.description && ( {poll.description && (
<p className="mt-1 text-sm text-dark-400">{poll.description}</p> <p className="mt-1 text-sm text-dark-400">{poll.description}</p>
)} )}
@@ -219,7 +219,7 @@ export default function Polls() {
</div> </div>
</div> </div>
{poll.reward_amount && ( {poll.reward_amount && (
<div className="flex items-center gap-1 text-accent-400"> <div className="flex shrink-0 items-center gap-1 text-accent-400">
<GiftIcon className="h-5 w-5" /> <GiftIcon className="h-5 w-5" />
<span className="text-sm font-medium">+{poll.reward_amount}</span> <span className="text-sm font-medium">+{poll.reward_amount}</span>
</div> </div>

View File

@@ -60,11 +60,13 @@ export function CampaignDetailPanel({ campaignId, className }: CampaignDetailPan
<div className="space-y-5"> <div className="space-y-5">
{/* Info */} {/* Info */}
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between gap-2 text-sm">
<span className="text-dark-500"> <span className="shrink-0 text-dark-500">
{t('admin.referralNetwork.campaign.startParam')} {t('admin.referralNetwork.campaign.startParam')}
</span> </span>
<span className="font-mono text-dark-200">{campaign.start_parameter}</span> <span className="min-w-0 truncate font-mono text-dark-200">
{campaign.start_parameter}
</span>
</div> </div>
<div className="flex justify-end"> <div className="flex justify-end">
<span <span
@@ -133,17 +135,19 @@ export function CampaignDetailPanel({ campaignId, className }: CampaignDetailPan
{campaign.top_referrers.map((referrer, index) => ( {campaign.top_referrers.map((referrer, index) => (
<div <div
key={referrer.user_id} key={referrer.user_id}
className="flex items-center justify-between text-sm" className="flex items-center justify-between gap-2 text-sm"
> >
<div className="flex items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<span className="flex h-5 w-5 items-center justify-center rounded-full bg-dark-700 text-[10px] font-medium text-dark-300"> <span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-dark-700 text-[10px] font-medium text-dark-300">
{index + 1} {index + 1}
</span> </span>
<span className="text-dark-200"> <span className="truncate text-dark-200">
{referrer.username ? `@${referrer.username}` : `#${referrer.user_id}`} {referrer.username ? `@${referrer.username}` : `#${referrer.user_id}`}
</span> </span>
</div> </div>
<span className="font-mono text-dark-300">{referrer.referral_count}</span> <span className="shrink-0 font-mono text-dark-300">
{referrer.referral_count}
</span>
</div> </div>
))} ))}
</div> </div>