mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-15 18:35:00 +03:00
6f1dd0d757
The sidebar no longer carries a sponsor card. The backend stops serving the "sidebar" slot, so a sponsors.json entry that still lists it keeps only its other slots and one listing nothing else is dropped. Its rotation, three-sponsor cap and collapsed-rail logo rendering go with it. The Sponsors page now lists only real sponsors: the "Your brand here" card shows only as the empty state instead of sitting in the grid and repeating the header's Become a sponsor button, and the Available placements section and its slot-occupancy helper are removed. Cards are larger, two per row on wide screens. Unused i18n keys are dropped from every locale.
90 lines
2.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
import { useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { CloseOutlined, ExportOutlined } from '@ant-design/icons';
|
|
|
|
import { withBasePath } from '@/api/http-init';
|
|
import type { Sponsor } from '@/generated/types';
|
|
import { pickLocale } from '@/lib/sponsors';
|
|
import './SponsorCard.css';
|
|
|
|
export type SponsorCardVariant = 'banner' | 'compact' | 'card';
|
|
|
|
export interface SponsorCardProps {
|
|
sponsor: Sponsor;
|
|
variant?: SponsorCardVariant;
|
|
onClose?: () => void;
|
|
}
|
|
|
|
function SponsorLogo({ sponsor }: { sponsor: Sponsor }) {
|
|
const [failedSrc, setFailedSrc] = useState('');
|
|
if (sponsor.logo && failedSrc !== sponsor.logo) {
|
|
return (
|
|
<img
|
|
className="sponsor-logo"
|
|
src={withBasePath(sponsor.logo)}
|
|
alt=""
|
|
loading="lazy"
|
|
onError={() => setFailedSrc(sponsor.logo ?? '')}
|
|
/>
|
|
);
|
|
}
|
|
return (
|
|
<span className="sponsor-logo sponsor-logo-fallback" aria-hidden="true">
|
|
{sponsor.name.slice(0, 1).toUpperCase()}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export default function SponsorCard({ sponsor, variant = 'banner', onClose }: SponsorCardProps) {
|
|
const { t, i18n } = useTranslation();
|
|
const lang = i18n.resolvedLanguage || i18n.language || 'en';
|
|
const title = pickLocale(sponsor.title, lang) || sponsor.name;
|
|
const text = pickLocale(sponsor.text, lang);
|
|
const tag = t('pages.sponsors.tag');
|
|
|
|
return (
|
|
<div className={`sponsor-card sponsor-card-${variant}`}>
|
|
<a
|
|
className="sponsor-main"
|
|
href={sponsor.link}
|
|
target="_blank"
|
|
rel="noopener noreferrer sponsored"
|
|
title={variant === 'compact' ? [title, text].filter(Boolean).join('\n') : undefined}
|
|
>
|
|
<SponsorLogo sponsor={sponsor} />
|
|
<span className="sponsor-body" dir="auto">
|
|
<span className="sponsor-head">
|
|
{variant !== 'banner' && <span className="sponsor-tag">{tag}</span>}
|
|
<span className="sponsor-title">{title}</span>
|
|
</span>
|
|
{text && <span className="sponsor-text">{text}</span>}
|
|
</span>
|
|
{variant === 'banner' && (
|
|
<span className="sponsor-aside">
|
|
<span className="sponsor-tag">{tag}</span>
|
|
<span className="sponsor-visit">
|
|
{t('pages.sponsors.visit')} <ExportOutlined />
|
|
</span>
|
|
</span>
|
|
)}
|
|
{variant === 'card' && (
|
|
<span className="sponsor-visit">
|
|
{t('pages.sponsors.visit')} <ExportOutlined />
|
|
</span>
|
|
)}
|
|
</a>
|
|
{onClose && (
|
|
<button
|
|
type="button"
|
|
className="sponsor-close"
|
|
aria-label={t('close')}
|
|
title={t('close')}
|
|
onClick={onClose}
|
|
>
|
|
<CloseOutlined />
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|