mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat: use app-level svgIconKey for app logos and improve tile contrast
- Add svgIconKey field to RemnawaveAppClient and RemnawaveApp types - Render app logos from svgLibrary via app.svgIconKey on connection page chips with fallback to getAppIcon() by name - Use app.svgIconKey for app icons on admin apps settings page - Show featured indicator as amber dot matching original RemnaWave UI - Increase inner tile background opacity from 30% to 50% on subscription page for better text readability
This commit is contained in:
@@ -113,6 +113,7 @@ export interface RemnawaveApp {
|
|||||||
featured?: boolean;
|
featured?: boolean;
|
||||||
urlScheme?: string;
|
urlScheme?: string;
|
||||||
isNeedBase64Encoding?: boolean;
|
isNeedBase64Encoding?: boolean;
|
||||||
|
svgIconKey?: string;
|
||||||
blocks: RemnawaveBlock[];
|
blocks: RemnawaveBlock[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,6 @@ function AppCard({
|
|||||||
lang: string;
|
lang: string;
|
||||||
}) {
|
}) {
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const firstBlock = app.blocks?.[0];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card overflow-hidden">
|
<div className="card overflow-hidden">
|
||||||
@@ -229,21 +228,9 @@ function AppCard({
|
|||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
className="flex w-full items-center gap-4 p-4 text-left"
|
className="flex w-full items-center gap-4 p-4 text-left"
|
||||||
>
|
>
|
||||||
{/* Icon from first block */}
|
{/* App icon from svgLibrary */}
|
||||||
<div
|
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-dark-700/50">
|
||||||
className="flex h-10 w-10 items-center justify-center rounded-full"
|
{renderSvgIcon(svgLibrary, app.svgIconKey) || <AppsIcon />}
|
||||||
style={
|
|
||||||
firstBlock?.svgIconColor
|
|
||||||
? {
|
|
||||||
backgroundColor: firstBlock.svgIconColor + '20',
|
|
||||||
color: firstBlock.svgIconColor,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{renderSvgIcon(svgLibrary, firstBlock?.svgIconKey, firstBlock?.svgIconColor) || (
|
|
||||||
<AppsIcon />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Name + badges */}
|
{/* Name + badges */}
|
||||||
|
|||||||
@@ -482,6 +482,7 @@ export default function Connection() {
|
|||||||
<div className="-mx-1 flex flex-wrap gap-2 px-1">
|
<div className="-mx-1 flex flex-wrap gap-2 px-1">
|
||||||
{currentPlatformApps.map((app, idx) => {
|
{currentPlatformApps.map((app, idx) => {
|
||||||
const isSelected = currentApp?.name === app.name;
|
const isSelected = currentApp?.name === app.name;
|
||||||
|
const appIconSvg = getSvgHtml(app.svgIconKey);
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={app.name + idx}
|
key={app.name + idx}
|
||||||
@@ -492,10 +493,16 @@ export default function Connection() {
|
|||||||
: 'bg-dark-800/60 text-dark-300 hover:bg-dark-800'
|
: 'bg-dark-800/60 text-dark-300 hover:bg-dark-800'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{app.featured && (
|
{app.featured && <span className="h-2 w-2 rounded-full bg-amber-400" />}
|
||||||
<svg className="h-3.5 w-3.5" fill="currentColor" viewBox="0 0 20 20">
|
{appIconSvg ? (
|
||||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
<div
|
||||||
</svg>
|
className="h-4 w-4 [&>svg]:h-full [&>svg]:w-full"
|
||||||
|
dangerouslySetInnerHTML={{ __html: appIconSvg }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="h-4 w-4 [&>svg]:h-full [&>svg]:w-full">
|
||||||
|
{getAppIcon(app.name)}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
{app.name}
|
{app.name}
|
||||||
</button>
|
</button>
|
||||||
@@ -553,7 +560,10 @@ export default function Connection() {
|
|||||||
: '');
|
: '');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={blockIdx} className="rounded-xl border border-dark-700 p-4">
|
<div
|
||||||
|
key={blockIdx}
|
||||||
|
className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-4"
|
||||||
|
>
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
{/* SVG icon */}
|
{/* SVG icon */}
|
||||||
{svgHtml && (
|
{svgHtml && (
|
||||||
|
|||||||
@@ -1117,7 +1117,7 @@ export default function Subscription() {
|
|||||||
{!showDeviceTopup ? (
|
{!showDeviceTopup ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDeviceTopup(true)}
|
onClick={() => setShowDeviceTopup(true)}
|
||||||
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/30 p-4 text-left transition-colors hover:border-dark-600"
|
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/50 p-4 text-left transition-colors hover:border-dark-600"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -1142,7 +1142,7 @@ export default function Subscription() {
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl border border-dark-700/50 bg-dark-800/30 p-5">
|
<div className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-5">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<h3 className="font-medium text-dark-100">{t('subscription.buyDevices')}</h3>
|
<h3 className="font-medium text-dark-100">{t('subscription.buyDevices')}</h3>
|
||||||
<button
|
<button
|
||||||
@@ -1275,7 +1275,7 @@ export default function Subscription() {
|
|||||||
{!showDeviceReduction ? (
|
{!showDeviceReduction ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDeviceReduction(true)}
|
onClick={() => setShowDeviceReduction(true)}
|
||||||
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/30 p-4 text-left transition-colors hover:border-dark-600"
|
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/50 p-4 text-left transition-colors hover:border-dark-600"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -1298,7 +1298,7 @@ export default function Subscription() {
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl border border-dark-700/50 bg-dark-800/30 p-5">
|
<div className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-5">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<h3 className="font-medium text-dark-100">
|
<h3 className="font-medium text-dark-100">
|
||||||
{t('subscription.additionalOptions.reduceDevicesTitle')}
|
{t('subscription.additionalOptions.reduceDevicesTitle')}
|
||||||
@@ -1444,7 +1444,7 @@ export default function Subscription() {
|
|||||||
{!showTrafficTopup ? (
|
{!showTrafficTopup ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowTrafficTopup(true)}
|
onClick={() => setShowTrafficTopup(true)}
|
||||||
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/30 p-4 text-left transition-colors hover:border-dark-600"
|
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/50 p-4 text-left transition-colors hover:border-dark-600"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -1470,7 +1470,7 @@ export default function Subscription() {
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl border border-dark-700/50 bg-dark-800/30 p-5">
|
<div className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-5">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<h3 className="font-medium text-dark-100">
|
<h3 className="font-medium text-dark-100">
|
||||||
{t('subscription.additionalOptions.buyTrafficTitle')}
|
{t('subscription.additionalOptions.buyTrafficTitle')}
|
||||||
@@ -1504,7 +1504,7 @@ export default function Subscription() {
|
|||||||
className={`rounded-xl border p-4 text-center transition-all ${
|
className={`rounded-xl border p-4 text-center transition-all ${
|
||||||
selectedTrafficPackage === pkg.gb
|
selectedTrafficPackage === pkg.gb
|
||||||
? 'border-accent-500 bg-accent-500/10'
|
? 'border-accent-500 bg-accent-500/10'
|
||||||
: 'border-dark-700/50 bg-dark-800/30 hover:border-dark-600'
|
: 'border-dark-700/50 bg-dark-800/50 hover:border-dark-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="text-lg font-semibold text-dark-100">
|
<div className="text-lg font-semibold text-dark-100">
|
||||||
@@ -1584,7 +1584,7 @@ export default function Subscription() {
|
|||||||
{!showServerManagement ? (
|
{!showServerManagement ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowServerManagement(true)}
|
onClick={() => setShowServerManagement(true)}
|
||||||
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/30 p-4 text-left transition-colors hover:border-dark-600"
|
className="w-full rounded-xl border border-dark-700/50 bg-dark-800/50 p-4 text-left transition-colors hover:border-dark-600"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -1607,7 +1607,7 @@ export default function Subscription() {
|
|||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl border border-dark-700/50 bg-dark-800/30 p-5">
|
<div className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-5">
|
||||||
<div className="mb-4 flex items-center justify-between">
|
<div className="mb-4 flex items-center justify-between">
|
||||||
<h3 className="font-medium text-dark-100">
|
<h3 className="font-medium text-dark-100">
|
||||||
{t('subscription.additionalOptions.manageServersTitle')}
|
{t('subscription.additionalOptions.manageServersTitle')}
|
||||||
@@ -1671,7 +1671,7 @@ export default function Subscription() {
|
|||||||
: 'border-accent-500 bg-accent-500/10'
|
: 'border-accent-500 bg-accent-500/10'
|
||||||
: willBeRemoved
|
: willBeRemoved
|
||||||
? 'border-error-500/50 bg-error-500/5'
|
? 'border-error-500/50 bg-error-500/5'
|
||||||
: 'border-dark-700/50 bg-dark-800/30 hover:border-dark-600'
|
: 'border-dark-700/50 bg-dark-800/50 hover:border-dark-600'
|
||||||
} ${!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 items-center gap-3">
|
||||||
@@ -1884,7 +1884,7 @@ export default function Subscription() {
|
|||||||
{devicesData.devices.map((device) => (
|
{devicesData.devices.map((device) => (
|
||||||
<div
|
<div
|
||||||
key={device.hwid}
|
key={device.hwid}
|
||||||
className="flex items-center justify-between rounded-xl border border-dark-700/50 bg-dark-800/30 p-4"
|
className="flex items-center justify-between rounded-xl border border-dark-700/50 bg-dark-800/50 p-4"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-dark-700">
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-dark-700">
|
||||||
@@ -2484,7 +2484,7 @@ export default function Subscription() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tariff Info */}
|
{/* Tariff Info */}
|
||||||
<div className="rounded-xl bg-dark-800/30 p-4">
|
<div className="rounded-xl bg-dark-800/50 p-4">
|
||||||
<div className="flex flex-wrap gap-4 text-sm">
|
<div className="flex flex-wrap gap-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<span className="text-dark-500">{t('subscription.traffic')}:</span>
|
<span className="text-dark-500">{t('subscription.traffic')}:</span>
|
||||||
@@ -2648,7 +2648,7 @@ export default function Subscription() {
|
|||||||
className={`relative rounded-xl border p-4 text-left transition-all ${
|
className={`relative rounded-xl border p-4 text-left transition-all ${
|
||||||
selectedTariffPeriod?.days === period.days && !useCustomDays
|
selectedTariffPeriod?.days === period.days && !useCustomDays
|
||||||
? 'border-accent-500 bg-accent-500/10'
|
? 'border-accent-500 bg-accent-500/10'
|
||||||
: 'border-dark-700/50 bg-dark-800/30 hover:border-dark-600'
|
: 'border-dark-700/50 bg-dark-800/50 hover:border-dark-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{displayDiscount && displayDiscount > 0 && (
|
{displayDiscount && displayDiscount > 0 && (
|
||||||
@@ -2685,7 +2685,7 @@ export default function Subscription() {
|
|||||||
{/* Custom days option */}
|
{/* Custom days option */}
|
||||||
{selectedTariff.custom_days_enabled &&
|
{selectedTariff.custom_days_enabled &&
|
||||||
(selectedTariff.price_per_day_kopeks ?? 0) > 0 && (
|
(selectedTariff.price_per_day_kopeks ?? 0) > 0 && (
|
||||||
<div className="rounded-xl border border-dark-700/50 bg-dark-800/30 p-4">
|
<div className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-4">
|
||||||
<div className="mb-3 flex items-center justify-between">
|
<div className="mb-3 flex items-center justify-between">
|
||||||
<span className="font-medium text-dark-200">
|
<span className="font-medium text-dark-200">
|
||||||
{t('subscription.customDays.title')}
|
{t('subscription.customDays.title')}
|
||||||
@@ -2785,7 +2785,7 @@ export default function Subscription() {
|
|||||||
<div className="mb-3 text-sm text-dark-400">
|
<div className="mb-3 text-sm text-dark-400">
|
||||||
{t('subscription.customTraffic.label')}
|
{t('subscription.customTraffic.label')}
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-dark-700/50 bg-dark-800/30 p-4">
|
<div className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-4">
|
||||||
<div className="mb-3 flex items-center justify-between">
|
<div className="mb-3 flex items-center justify-between">
|
||||||
<span className="font-medium text-dark-200">
|
<span className="font-medium text-dark-200">
|
||||||
{t('subscription.customTraffic.selectVolume')}
|
{t('subscription.customTraffic.selectVolume')}
|
||||||
@@ -3256,7 +3256,7 @@ export default function Subscription() {
|
|||||||
selectedServers.includes(server.uuid)
|
selectedServers.includes(server.uuid)
|
||||||
? 'border-accent-500 bg-accent-500/10'
|
? 'border-accent-500 bg-accent-500/10'
|
||||||
: server.is_available
|
: server.is_available
|
||||||
? 'border-dark-700/50 bg-dark-800/30 hover:border-dark-600'
|
? 'border-dark-700/50 bg-dark-800/50 hover:border-dark-600'
|
||||||
: 'cursor-not-allowed border-dark-800/30 bg-dark-900/30 opacity-50'
|
: 'cursor-not-allowed border-dark-800/30 bg-dark-900/30 opacity-50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ export interface RemnawaveAppClient {
|
|||||||
name: string;
|
name: string;
|
||||||
featured?: boolean;
|
featured?: boolean;
|
||||||
deepLink?: string | null;
|
deepLink?: string | null;
|
||||||
|
svgIconKey?: string;
|
||||||
blocks: RemnawaveBlockClient[];
|
blocks: RemnawaveBlockClient[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user