mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
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:
@@ -118,6 +118,7 @@ export interface RemnawaveApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RemnawavePlatform {
|
export interface RemnawavePlatform {
|
||||||
|
svgIconKey?: string;
|
||||||
apps: RemnawaveApp[];
|
apps: RemnawaveApp[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 */}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -529,6 +529,7 @@ export interface RemnawaveAppClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RemnawavePlatformData {
|
export interface RemnawavePlatformData {
|
||||||
|
svgIconKey?: string;
|
||||||
apps: RemnawaveAppClient[];
|
apps: RemnawaveAppClient[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user