feat: add empty state for connection page when no apps configured

Show different messages depending on user role:
- Regular users see a friendly "check back later" message
- Admins see a prompt with a link to /admin/apps settings
This commit is contained in:
c0mrade
2026-02-09 11:23:33 +03:00
parent 03a7db53fb
commit fb25df6f0f
5 changed files with 86 additions and 8 deletions

View File

@@ -355,7 +355,11 @@
"selectPlatform": "Select platform", "selectPlatform": "Select platform",
"app": "app", "app": "app",
"apps": "apps", "apps": "apps",
"tutorial": "Tutorial" "tutorial": "Tutorial",
"notConfigured": "Connection methods are not configured yet",
"notConfiguredUser": "Connection setup is in progress. Please check back later.",
"notConfiguredAdmin": "No connection apps have been configured. Set them up in the Apps settings.",
"goToApps": "Go to Apps settings"
}, },
"myDevices": "My Devices", "myDevices": "My Devices",
"noDevices": "No connected devices", "noDevices": "No connected devices",

View File

@@ -307,7 +307,11 @@
"selectPlatform": "انتخاب پلتفرم", "selectPlatform": "انتخاب پلتفرم",
"app": "برنامه", "app": "برنامه",
"apps": "برنامه", "apps": "برنامه",
"tutorial": "آموزش" "tutorial": "آموزش",
"notConfigured": "روش‌های اتصال هنوز پیکربندی نشده‌اند",
"notConfiguredUser": "تنظیمات اتصال در حال انجام است. لطفاً بعداً مراجعه کنید.",
"notConfiguredAdmin": "هیچ برنامه اتصالی پیکربندی نشده است. آن‌ها را در تنظیمات برنامه‌ها تنظیم کنید.",
"goToApps": "رفتن به تنظیمات برنامه‌ها"
}, },
"additionalOptions": { "additionalOptions": {
"title": "گزینه‌های اضافی", "title": "گزینه‌های اضافی",

View File

@@ -377,7 +377,11 @@
"selectPlatform": "Выберите платформу", "selectPlatform": "Выберите платформу",
"app": "приложение", "app": "приложение",
"apps": "приложений", "apps": "приложений",
"tutorial": "Инструкция" "tutorial": "Инструкция",
"notConfigured": "Способы подключения ещё не настроены",
"notConfiguredUser": "Настройка подключения в процессе. Пожалуйста, загляните позже.",
"notConfiguredAdmin": "Приложения для подключения не настроены. Настройте их в разделе Приложения.",
"goToApps": "Перейти к настройке приложений"
}, },
"myDevices": "Мои устройства", "myDevices": "Мои устройства",
"noDevices": "Нет подключенных устройств", "noDevices": "Нет подключенных устройств",

View File

@@ -307,7 +307,11 @@
"selectPlatform": "选择平台", "selectPlatform": "选择平台",
"app": "个应用", "app": "个应用",
"apps": "个应用", "apps": "个应用",
"tutorial": "教程" "tutorial": "教程",
"notConfigured": "连接方式尚未配置",
"notConfiguredUser": "连接设置正在进行中,请稍后再来查看。",
"notConfiguredAdmin": "尚未配置连接应用。请在应用设置中进行配置。",
"goToApps": "前往应用设置"
}, },
"additionalOptions": { "additionalOptions": {
"title": "附加选项", "title": "附加选项",

View File

@@ -1,5 +1,5 @@
import { useEffect, useCallback, useRef } from 'react'; import { useEffect, useCallback, useMemo, useRef } from 'react';
import { useNavigate } from 'react-router'; import { Link, useNavigate } from 'react-router';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { openLink as sdkOpenLink } from '@telegram-apps/sdk-react'; import { openLink as sdkOpenLink } from '@telegram-apps/sdk-react';
@@ -8,13 +8,13 @@ import { useTelegramSDK } from '../hooks/useTelegramSDK';
import { useHaptic } from '@/platform'; import { useHaptic } from '@/platform';
import { resolveTemplate, hasTemplates } from '../utils/templateEngine'; import { resolveTemplate, hasTemplates } from '../utils/templateEngine';
import { useAuthStore } from '../store/auth'; import { useAuthStore } from '../store/auth';
import type { AppConfig } from '../types'; import type { AppConfig, RemnawavePlatformData } from '../types';
import InstallationGuide from '../components/connection/InstallationGuide'; import InstallationGuide from '../components/connection/InstallationGuide';
export default function Connection() { export default function Connection() {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
const { user } = useAuthStore(); const { user, isAdmin } = useAuthStore();
const { isTelegramWebApp } = useTelegramSDK(); const { isTelegramWebApp } = useTelegramSDK();
const { impact: hapticImpact } = useHaptic(); const { impact: hapticImpact } = useHaptic();
@@ -79,6 +79,14 @@ export default function Connection() {
[isTelegramWebApp, i18n.language, resolveUrl], [isTelegramWebApp, i18n.language, resolveUrl],
); );
// Check if any platform has configured apps
const hasApps = useMemo(() => {
if (!appConfig?.platforms) return false;
return Object.values(appConfig.platforms).some(
(p: RemnawavePlatformData) => p.apps && p.apps.length > 0,
);
}, [appConfig?.platforms]);
// Loading // Loading
if (isLoading) { if (isLoading) {
return ( return (
@@ -115,6 +123,60 @@ export default function Connection() {
); );
} }
// No apps configured
if (!hasApps) {
return (
<div className="flex flex-1 flex-col items-center justify-center p-8 text-center">
<div className="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-dark-800">
<svg
className="h-8 w-8 text-dark-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={1.5}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 1.5H8.25A2.25 2.25 0 006 3.75v16.5a2.25 2.25 0 002.25 2.25h7.5A2.25 2.25 0 0018 20.25V3.75a2.25 2.25 0 00-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3"
/>
</svg>
</div>
<h3 className="mb-2 text-xl font-bold text-dark-100">
{t('subscription.connection.notConfigured')}
</h3>
<p className="mb-6 max-w-sm text-dark-400">
{isAdmin
? t('subscription.connection.notConfiguredAdmin')
: t('subscription.connection.notConfiguredUser')}
</p>
{isAdmin && (
<Link to="/admin/apps" className="btn-primary inline-flex items-center gap-2 px-6 py-2.5">
<svg
className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.108-1.204l-.526-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
{t('subscription.connection.goToApps')}
</Link>
)}
</div>
);
}
return ( return (
<InstallationGuide <InstallationGuide
appConfig={appConfig} appConfig={appConfig}