Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 1x | import React from 'react';
import { AlertTriangle, X, ExternalLink } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { QuotaOverage } from '../api/auth';
interface QuotaWarningBannerProps {
overages: QuotaOverage[];
onDismiss?: () => void;
}
const QuotaWarningBanner: React.FC<QuotaWarningBannerProps> = ({ overages, onDismiss }) => {
const { t } = useTranslation();
if (!overages || overages.length === 0) {
return null;
}
// Find the most urgent overage (least days remaining)
const mostUrgent = overages.reduce((prev, curr) =>
curr.days_remaining < prev.days_remaining ? curr : prev
);
const isUrgent = mostUrgent.days_remaining <= 7;
const isCritical = mostUrgent.days_remaining <= 1;
const getBannerStyles = () => {
if (isCritical) {
return 'bg-red-600 text-white border-red-700';
}
if (isUrgent) {
return 'bg-amber-500 text-white border-amber-600';
}
return 'bg-amber-100 text-amber-900 border-amber-300';
};
const getIconColor = () => {
if (isCritical || isUrgent) {
return 'text-white';
}
return 'text-amber-600';
};
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
};
return (
<div className={`border-b ${getBannerStyles()}`}>
<div className="max-w-7xl mx-auto px-4 py-3 sm:px-6 lg:px-8">
<div className="flex items-center justify-between flex-wrap gap-2">
<div className="flex items-center gap-3">
<AlertTriangle className={`h-5 w-5 flex-shrink-0 ${getIconColor()}`} />
<div className="flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<span className="font-medium">
{isCritical
? t('quota.banner.critical', 'URGENT: Automatic archiving tomorrow!')
: isUrgent
? t('quota.banner.urgent', 'Action Required: {{days}} days left', { days: mostUrgent.days_remaining })
: t('quota.banner.warning', 'Quota exceeded for {{count}} item(s)', { count: overages.length })
}
</span>
<span className="text-sm opacity-90">
{t('quota.banner.details',
'You have {{overage}} {{type}} over your plan limit. Grace period ends {{date}}.',
{
overage: mostUrgent.overage_amount,
type: mostUrgent.display_name,
date: formatDate(mostUrgent.grace_period_ends_at)
}
)}
</span>
</div>
</div>
<div className="flex items-center gap-2">
<Link
to="/settings/quota"
className={`inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${
isCritical || isUrgent
? 'bg-white/20 hover:bg-white/30 text-white'
: 'bg-amber-600 hover:bg-amber-700 text-white'
}`}
>
{t('quota.banner.manage', 'Manage Quota')}
<ExternalLink className="h-4 w-4" />
</Link>
{onDismiss && (
<button
onClick={onDismiss}
className={`p-1 rounded-md transition-colors ${
isCritical || isUrgent
? 'hover:bg-white/20'
: 'hover:bg-amber-200'
}`}
aria-label={t('common.dismiss', 'Dismiss')}
>
<X className="h-5 w-5" />
</button>
)}
</div>
</div>
{/* Show additional overages if there are more than one */}
{overages.length > 1 && (
<div className="mt-2 text-sm opacity-90">
<span className="font-medium">{t('quota.banner.allOverages', 'All overages:')}</span>
<ul className="ml-4 mt-1 space-y-0.5">
{overages.map((overage) => (
<li key={overage.id}>
{overage.display_name}: {overage.current_usage}/{overage.allowed_limit}
({t('quota.banner.overBy', 'over by {{amount}}', { amount: overage.overage_amount })})
{' - '}
{overage.days_remaining <= 0
? t('quota.banner.expiredToday', 'expires today!')
: t('quota.banner.daysLeft', '{{days}} days left', { days: overage.days_remaining })
}
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
};
export default QuotaWarningBanner;
|