fix: unify connection page design with global styles and add platform SVG icons

- Add svgIconKey to RemnawavePlatform and RemnawavePlatformData types
- Render platform OS icons from svgLibrary in AdminApps tabs and Connection page
- Replace platform dropdown with icon pill buttons on connection page
- Use .card class with colored left border for block cards
- Unify all back buttons with AdminBackButton pattern (border + bg)
- Fix app chips and platform tabs to not blend with background (btn-secondary style)
- Use accent outlined style for block action buttons matching original RemnaWave UI
- Use btn-secondary for tutorial button
This commit is contained in:
c0mrade
2026-02-05 13:21:32 +03:00
parent 91afbbf3c6
commit 4866003c23
4 changed files with 88 additions and 59 deletions

View File

@@ -118,6 +118,7 @@ export interface RemnawaveApp {
} }
export interface RemnawavePlatform { export interface RemnawavePlatform {
svgIconKey?: string;
apps: RemnawaveApp[]; apps: RemnawaveApp[];
} }

View File

@@ -440,19 +440,23 @@ export default function AdminApps() {
{/* Platform Tabs */} {/* Platform Tabs */}
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{PLATFORMS.map((platform) => ( {PLATFORMS.map((platform) => {
<button const platformSvgKey = remnaWaveConfig?.platforms?.[platform]?.svgIconKey;
key={platform} return (
onClick={() => setSelectedPlatform(platform)} <button
className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${ key={platform}
selectedPlatform === platform onClick={() => setSelectedPlatform(platform)}
? 'bg-accent-500 text-white' className={`flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors ${
: 'bg-dark-800 text-dark-300 hover:bg-dark-700' selectedPlatform === platform
}`} ? 'bg-accent-500 text-white'
> : 'bg-dark-800 text-dark-300 hover:bg-dark-700'
{PLATFORM_LABELS[platform]} }`}
</button> >
))} {renderSvgIcon(svgLibrary, platformSvgKey)}
{PLATFORM_LABELS[platform]}
</button>
);
})}
</div> </div>
{/* Apps List */} {/* Apps List */}

View File

@@ -335,21 +335,27 @@ export default function Connection() {
} }
const isMobilePlatform = detectedPlatform === 'ios' || detectedPlatform === 'android'; const isMobilePlatform = detectedPlatform === 'ios' || detectedPlatform === 'android';
if (isMobilePlatform) {
window.location.href = resolved; // Desktop: deep links need redirect page (browser can't handle custom schemes directly)
return; // Mobile: system browser handles custom schemes natively
const finalUrl = isMobilePlatform
? resolved
: `${window.location.origin}/miniapp/redirect.html?url=${encodeURIComponent(resolved)}&lang=${i18n.language || 'en'}`;
// In Telegram Mini App — sdkOpenLink opens URL in system browser,
// where custom URL schemes work (WebView itself can't handle them)
if (isTelegramWebApp) {
try {
sdkOpenLink(finalUrl, { tryInstantView: false });
return;
} catch {
// SDK not available, fallback
}
} }
const redirectUrl = `${window.location.origin}/miniapp/redirect.html?url=${encodeURIComponent(resolved)}&lang=${i18n.language || 'en'}`; window.location.href = finalUrl;
try {
sdkOpenLink(redirectUrl, { tryInstantView: false });
return;
} catch {
// SDK not available, fallback
}
window.location.href = redirectUrl;
}, },
[detectedPlatform, i18n.language, resolveUrl], [detectedPlatform, isTelegramWebApp, i18n.language, resolveUrl],
); );
const handleConnect = (app: AppInfo) => { const handleConnect = (app: AppInfo) => {
@@ -442,12 +448,12 @@ export default function Connection() {
// Main RemnaWave view — inline app chips + blocks // Main RemnaWave view — inline app chips + blocks
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* Header: title + platform dropdown */} {/* Header */}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{!isTelegramWebApp && ( {!isTelegramWebApp && (
<button <button
onClick={handleGoBack} onClick={handleGoBack}
className="-ml-2 rounded-xl p-2 text-dark-300 hover:bg-dark-800" className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600"
> >
<BackIcon /> <BackIcon />
</button> </button>
@@ -455,31 +461,47 @@ export default function Connection() {
<h2 className="flex-1 text-lg font-bold text-dark-100"> <h2 className="flex-1 text-lg font-bold text-dark-100">
{t('subscription.connection.title')} {t('subscription.connection.title')}
</h2> </h2>
{/* Platform dropdown */}
{availablePlatforms.length > 1 && (
<select
value={currentPlatformKey || ''}
onChange={(e) => {
const newPlatform = e.target.value;
setActivePlatformKey(newPlatform);
const platformData = appConfig.platforms[newPlatform];
if (platformData) {
const apps = getRemnawaveApps(platformData);
const app = apps.find((a) => a.featured) || apps[0];
if (app) setSelectedRemnawaveApp(app);
}
}}
className="rounded-lg border border-dark-600 bg-dark-800 px-3 py-1.5 text-sm text-dark-200 outline-none"
>
{availablePlatforms.map((p) => (
<option key={p} value={p}>
{getPlatformDisplayName(p)}
</option>
))}
</select>
)}
</div> </div>
{/* Platform tabs */}
{availablePlatforms.length > 1 && (
<div className="-mt-3 flex gap-2 overflow-x-auto pb-1">
{availablePlatforms.map((p) => {
const isActive = p === currentPlatformKey;
const pData = appConfig.platforms[p];
const svgIconKey = pData && isRemnawavePlatform(pData) ? pData.svgIconKey : undefined;
const platformSvg = getSvgHtml(svgIconKey);
return (
<button
key={p}
onClick={() => {
setActivePlatformKey(p);
const platformData = appConfig.platforms[p];
if (platformData) {
const apps = getRemnawaveApps(platformData);
const app = apps.find((a) => a.featured) || apps[0];
if (app) setSelectedRemnawaveApp(app);
}
}}
className={`flex items-center gap-1.5 whitespace-nowrap rounded-lg px-3 py-1.5 text-sm font-medium transition-all ${
isActive
? 'bg-accent-500/15 text-accent-400 ring-1 ring-accent-500/40'
: 'border border-dark-700/50 bg-dark-800/80 text-dark-200 hover:border-dark-600/50 hover:bg-dark-700/80'
}`}
>
{platformSvg && (
<div
className="h-4 w-4 flex-shrink-0 [&>svg]:h-full [&>svg]:w-full"
dangerouslySetInnerHTML={{ __html: platformSvg }}
/>
)}
{getPlatformDisplayName(p)}
</button>
);
})}
</div>
)}
{/* App cards row */} {/* App cards row */}
{currentPlatformApps.length > 0 && ( {currentPlatformApps.length > 0 && (
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
@@ -493,7 +515,7 @@ export default function Connection() {
className={`relative flex items-center gap-2 overflow-hidden rounded-xl px-3 py-2.5 text-sm font-medium transition-all active:scale-[0.97] ${ className={`relative flex items-center gap-2 overflow-hidden rounded-xl px-3 py-2.5 text-sm font-medium transition-all active:scale-[0.97] ${
isSelected isSelected
? 'bg-accent-500/15 text-accent-400 ring-1 ring-accent-500/40' ? 'bg-accent-500/15 text-accent-400 ring-1 ring-accent-500/40'
: 'bg-dark-800/60 text-dark-300 hover:bg-dark-800' : 'border border-dark-700/50 bg-dark-800/80 text-dark-200 hover:border-dark-600/50 hover:bg-dark-700/80'
}`} }`}
> >
{app.featured && <span className="h-2 w-2 shrink-0 rounded-full bg-amber-400" />} {app.featured && <span className="h-2 w-2 shrink-0 rounded-full bg-amber-400" />}
@@ -516,7 +538,7 @@ export default function Connection() {
href={appConfig.baseSettings.tutorialUrl} href={appConfig.baseSettings.tutorialUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center justify-center gap-2 rounded-xl border border-dark-600 px-4 py-2.5 text-sm font-medium text-dark-200 transition-all hover:bg-dark-800" className="btn-secondary w-full justify-center"
> >
<svg <svg
className="h-5 w-5" className="h-5 w-5"
@@ -575,7 +597,8 @@ export default function Connection() {
return ( return (
<div <div
key={blockIdx} key={blockIdx}
className="rounded-xl border border-dark-700/50 bg-dark-800/50 p-4" className="card border-l-2"
style={{ borderLeftColor: iconColor }}
> >
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
{/* SVG icon */} {/* SVG icon */}
@@ -615,7 +638,7 @@ export default function Connection() {
<button <button
key={btnIdx} key={btnIdx}
onClick={() => openDeepLink(deepLink)} onClick={() => openDeepLink(deepLink)}
className="btn-primary flex items-center gap-2 px-4 py-2 text-sm font-semibold" className="flex items-center gap-2 rounded-xl border border-accent-500/40 px-4 py-2 text-sm font-medium text-accent-400 transition-all hover:bg-accent-500/10"
> >
{subBtnSvg ? ( {subBtnSvg ? (
<div <div
@@ -639,7 +662,7 @@ export default function Connection() {
className={`flex items-center gap-2 rounded-xl border px-4 py-2 text-sm font-medium transition-all ${ className={`flex items-center gap-2 rounded-xl border px-4 py-2 text-sm font-medium transition-all ${
copied copied
? 'border-success-500 bg-success-500/10 text-success-400' ? 'border-success-500 bg-success-500/10 text-success-400'
: 'border-dark-600 text-dark-300 hover:bg-dark-800' : 'border-accent-500/40 text-accent-400 hover:bg-accent-500/10'
}`} }`}
> >
{copied ? <CheckIcon /> : <CopyIcon />} {copied ? <CheckIcon /> : <CopyIcon />}
@@ -666,7 +689,7 @@ export default function Connection() {
href={href} href={href}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 rounded-lg bg-dark-800 px-3 py-1.5 text-sm text-dark-200 hover:bg-dark-700" className="inline-flex items-center gap-1.5 rounded-xl border border-accent-500/40 px-4 py-2 text-sm font-medium text-accent-400 transition-all hover:bg-accent-500/10"
> >
{btnSvgHtml ? ( {btnSvgHtml ? (
<div <div
@@ -753,7 +776,7 @@ export default function Connection() {
{!isTelegramWebApp && ( {!isTelegramWebApp && (
<button <button
onClick={handleBack} onClick={handleBack}
className="-ml-2 rounded-xl p-2 text-dark-300 hover:bg-dark-800" className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600"
> >
<BackIcon /> <BackIcon />
</button> </button>
@@ -830,7 +853,7 @@ export default function Connection() {
{!isTelegramWebApp && ( {!isTelegramWebApp && (
<button <button
onClick={handleBack} onClick={handleBack}
className="-ml-2 rounded-xl p-2 text-dark-300 hover:bg-dark-800" className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600"
> >
<BackIcon /> <BackIcon />
</button> </button>
@@ -916,7 +939,7 @@ export default function Connection() {
{!isTelegramWebApp && ( {!isTelegramWebApp && (
<button <button
onClick={handleGoBack} onClick={handleGoBack}
className="-mr-2 rounded-xl p-2 text-dark-400 hover:bg-dark-800" className="flex h-10 w-10 items-center justify-center rounded-xl border border-dark-700 bg-dark-800 transition-colors hover:border-dark-600"
> >
<BackIcon /> <BackIcon />
</button> </button>

View File

@@ -529,6 +529,7 @@ export interface RemnawaveAppClient {
} }
export interface RemnawavePlatformData { export interface RemnawavePlatformData {
svgIconKey?: string;
apps: RemnawaveAppClient[]; apps: RemnawaveAppClient[];
} }