mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(sidebar): label the app-rail add button for new users (#4052)
This commit is contained in:
@@ -7,23 +7,36 @@ import SidebarAppRail from './SidebarAppRail';
|
||||
const mockNavigate = vi.fn();
|
||||
const mockDispatch = vi.fn();
|
||||
|
||||
const mockState = {
|
||||
const whatsappAccount = {
|
||||
id: 'acct-whatsapp',
|
||||
provider: 'whatsapp',
|
||||
label: 'WhatsApp',
|
||||
createdAt: '2026-01-01T00:00:00.000Z',
|
||||
status: 'open',
|
||||
};
|
||||
|
||||
const accountsWith: Record<string, typeof whatsappAccount> = { 'acct-whatsapp': whatsappAccount };
|
||||
|
||||
let mockState = {
|
||||
accounts: {
|
||||
accounts: {
|
||||
'acct-whatsapp': {
|
||||
id: 'acct-whatsapp',
|
||||
provider: 'whatsapp',
|
||||
label: 'WhatsApp',
|
||||
createdAt: '2026-01-01T00:00:00.000Z',
|
||||
status: 'open',
|
||||
},
|
||||
},
|
||||
accounts: accountsWith,
|
||||
order: ['acct-whatsapp'],
|
||||
activeAccountId: null,
|
||||
unread: {},
|
||||
activeAccountId: null as string | null,
|
||||
unread: {} as Record<string, number>,
|
||||
},
|
||||
};
|
||||
|
||||
function setAccounts(order: string[]) {
|
||||
mockState = {
|
||||
accounts: {
|
||||
accounts: order.length ? accountsWith : {},
|
||||
order,
|
||||
activeAccountId: null,
|
||||
unread: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
vi.mock('react-router-dom', async importOriginal => {
|
||||
const actual = await importOriginal<typeof import('react-router-dom')>();
|
||||
return { ...actual, useNavigate: () => mockNavigate };
|
||||
@@ -40,6 +53,7 @@ vi.mock('../../../store/hooks', () => ({
|
||||
describe('SidebarAppRail', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
setAccounts(['acct-whatsapp']);
|
||||
});
|
||||
|
||||
it('selects a provider webview without mutating the current route', () => {
|
||||
@@ -65,6 +79,23 @@ describe('SidebarAppRail', () => {
|
||||
payload: '__agent__',
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the "Add apps" label when no provider apps are connected', () => {
|
||||
setAccounts([]);
|
||||
renderRail('/chat');
|
||||
|
||||
const addButton = screen.getByTestId('accounts-add-button');
|
||||
expect(addButton).toHaveTextContent('accounts.addApps');
|
||||
});
|
||||
|
||||
it('collapses the add button to an icon once an app is connected', () => {
|
||||
setAccounts(['acct-whatsapp']);
|
||||
renderRail('/chat');
|
||||
|
||||
const addButton = screen.getByTestId('accounts-add-button');
|
||||
expect(addButton).not.toHaveTextContent('accounts.addApps');
|
||||
expect(addButton).toHaveAttribute('aria-label', 'accounts.addApps');
|
||||
});
|
||||
});
|
||||
|
||||
function renderRail(route: string) {
|
||||
|
||||
@@ -113,6 +113,13 @@ export default function SidebarAppRail() {
|
||||
const selectedId = activeAccountId ?? AGENT_ID;
|
||||
const isAgentSelected = selectedId === AGENT_ID;
|
||||
|
||||
// New-user affordance: when no provider apps are connected yet, the lone
|
||||
// dashed "+" tile reads as ambiguous. Expand it into a labelled "Add apps"
|
||||
// pill so first-run users understand they can connect more apps; once at
|
||||
// least one app is connected, collapse back to the compact icon to reclaim
|
||||
// horizontal space in the rail.
|
||||
const showAddLabel = accounts.length === 0;
|
||||
|
||||
// The chat page hides its active provider webview while a rail overlay is
|
||||
// open (the native CEF view composites above HTML, so React overlays would be
|
||||
// painted over). Mirror local overlay state into Redux for it to read.
|
||||
@@ -253,12 +260,17 @@ export default function SidebarAppRail() {
|
||||
}}
|
||||
data-analytics-id="sidebar-app-rail-add-account"
|
||||
data-testid="accounts-add-button"
|
||||
className="group relative flex h-9 w-9 flex-none items-center justify-center rounded-xl border border-dashed border-stone-300 text-stone-400 hover:bg-stone-50 hover:text-stone-600 dark:border-neutral-700 dark:text-neutral-500 dark:hover:bg-neutral-800/60 dark:hover:text-neutral-300"
|
||||
aria-label={t('accounts.addAccount')}
|
||||
title={t('accounts.addAccount')}>
|
||||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
className={`group relative flex h-9 flex-none items-center justify-center gap-1.5 rounded-xl border border-dashed border-stone-300 text-stone-400 transition-colors hover:bg-stone-50 hover:text-stone-600 dark:border-neutral-700 dark:text-neutral-500 dark:hover:bg-neutral-800/60 dark:hover:text-neutral-300 ${
|
||||
showAddLabel ? 'w-auto px-2.5' : 'w-9'
|
||||
}`}
|
||||
aria-label={t('accounts.addApps')}
|
||||
title={t('accounts.addApps')}>
|
||||
<svg className="h-4 w-4 flex-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
{showAddLabel && (
|
||||
<span className="whitespace-nowrap text-xs font-medium">{t('accounts.addApps')}</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -845,6 +845,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'افحص الذاكرة أو صدّرها أو امسحها بنفسك. اضبطها من الإعدادات › الذاكرة.',
|
||||
'accounts.addAccount': 'إضافة حساب',
|
||||
'accounts.addApps': 'إضافة تطبيقات',
|
||||
'accounts.manageAccounts': 'إدارة الحسابات',
|
||||
'accounts.noAccounts': 'لا توجد حسابات متصلة',
|
||||
'accounts.connectAccount': 'اربط حسابًا للبدء',
|
||||
|
||||
@@ -867,6 +867,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'মেমোরি নিজে পরীক্ষা, এক্সপোর্ট বা মুছুন। Settings › Memory-এ কনফিগার করুন।',
|
||||
'accounts.addAccount': 'অ্যাকাউন্ট যোগ করুন',
|
||||
'accounts.addApps': 'অ্যাপ যোগ করুন',
|
||||
'accounts.manageAccounts': 'অ্যাকাউন্ট পরিচালনা',
|
||||
'accounts.noAccounts': 'কোনো অ্যাকাউন্ট সংযুক্ত নেই',
|
||||
'accounts.connectAccount': 'শুরু করতে একটি অ্যাকাউন্ট সংযুক্ত করুন',
|
||||
|
||||
@@ -889,6 +889,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Überprüfe, exportiere oder lösche den Speicher selbst. Konfiguriere in den Einstellungen › Speicher.',
|
||||
'accounts.addAccount': 'Konto hinzufügen',
|
||||
'accounts.addApps': 'Apps hinzufügen',
|
||||
'accounts.manageAccounts': 'Konten verwalten',
|
||||
'accounts.noAccounts': 'Keine Konten verbunden',
|
||||
'accounts.connectAccount': 'Verbinde ein Konto, um loszulegen',
|
||||
|
||||
@@ -1192,6 +1192,7 @@ const en: TranslationMap = {
|
||||
|
||||
// Accounts
|
||||
'accounts.addAccount': 'Add Account',
|
||||
'accounts.addApps': 'Add apps',
|
||||
'accounts.manageAccounts': 'Manage Accounts',
|
||||
'accounts.noAccounts': 'No accounts connected',
|
||||
'accounts.connectAccount': 'Connect an account to get started',
|
||||
|
||||
@@ -884,6 +884,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Inspecciona, exporta o borra la memoria tú mismo. Configura en Configuración › Memoria.',
|
||||
'accounts.addAccount': 'Agregar cuenta',
|
||||
'accounts.addApps': 'Agregar apps',
|
||||
'accounts.manageAccounts': 'Administrar cuentas',
|
||||
'accounts.noAccounts': 'Sin cuentas conectadas',
|
||||
'accounts.connectAccount': 'Conecta una cuenta para empezar',
|
||||
|
||||
@@ -887,6 +887,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Inspecte, exporte ou efface la mémoire toi-même. À configurer dans Paramètres › Mémoire.',
|
||||
'accounts.addAccount': 'Ajouter un compte',
|
||||
'accounts.addApps': 'Ajouter des apps',
|
||||
'accounts.manageAccounts': 'Gérer les comptes',
|
||||
'accounts.noAccounts': 'Aucun compte connecté',
|
||||
'accounts.connectAccount': 'Connecte un compte pour démarrer',
|
||||
|
||||
@@ -863,6 +863,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'मेमोरी खुद देखें, एक्सपोर्ट करें या मिटाएं। Settings › Memory में कॉन्फिगर करें।',
|
||||
'accounts.addAccount': 'अकाउंट जोड़ें',
|
||||
'accounts.addApps': 'ऐप्स जोड़ें',
|
||||
'accounts.manageAccounts': 'अकाउंट मैनेज करें',
|
||||
'accounts.noAccounts': 'कोई अकाउंट कनेक्ट नहीं है',
|
||||
'accounts.connectAccount': 'शुरू करने के लिए एक अकाउंट कनेक्ट करें',
|
||||
|
||||
@@ -870,6 +870,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Periksa, ekspor, atau hapus memori sendiri. Konfigurasi di Pengaturan › Memori.',
|
||||
'accounts.addAccount': 'Tambah Akun',
|
||||
'accounts.addApps': 'Tambah aplikasi',
|
||||
'accounts.manageAccounts': 'Kelola Akun',
|
||||
'accounts.noAccounts': 'Belum ada akun terhubung',
|
||||
'accounts.connectAccount': 'Hubungkan akun untuk memulai',
|
||||
|
||||
@@ -881,6 +881,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Ispeziona, esporta o cancella la memoria da solo. Configura in Impostazioni › Memoria.',
|
||||
'accounts.addAccount': 'Aggiungi account',
|
||||
'accounts.addApps': 'Aggiungi app',
|
||||
'accounts.manageAccounts': 'Gestisci account',
|
||||
'accounts.noAccounts': 'Nessun account connesso',
|
||||
'accounts.connectAccount': 'Connetti un account per iniziare',
|
||||
|
||||
@@ -864,6 +864,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'메모리를 직접 검사, 내보내기 또는 삭제할 수 있습니다. 설정 › 메모리에서 구성할 수 있습니다.',
|
||||
'accounts.addAccount': '계정 추가',
|
||||
'accounts.addApps': '앱 추가',
|
||||
'accounts.manageAccounts': '계정 관리',
|
||||
'accounts.noAccounts': '연결된 계정이 없습니다',
|
||||
'accounts.connectAccount': '시작하려면 계정을 연결하세요',
|
||||
|
||||
@@ -876,6 +876,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Przeglądaj, eksportuj lub czyść pamięć samodzielnie. Skonfiguruj w Ustawieniach › Pamięć.',
|
||||
'accounts.addAccount': 'Dodaj konto',
|
||||
'accounts.addApps': 'Dodaj aplikacje',
|
||||
'accounts.manageAccounts': 'Zarządzaj kontami',
|
||||
'accounts.noAccounts': 'Brak podłączonych kont',
|
||||
'accounts.connectAccount': 'Podłącz konto, aby zacząć',
|
||||
|
||||
@@ -886,6 +886,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Inspecione, exporte ou limpe a memória você mesmo. Configure em Configurações › Memória.',
|
||||
'accounts.addAccount': 'Adicionar Conta',
|
||||
'accounts.addApps': 'Adicionar apps',
|
||||
'accounts.manageAccounts': 'Gerenciar Contas',
|
||||
'accounts.noAccounts': 'Nenhuma conta conectada',
|
||||
'accounts.connectAccount': 'Conecte uma conta para começar',
|
||||
|
||||
@@ -874,6 +874,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.configureDesc':
|
||||
'Просматривай, экспортируй или очищай память самостоятельно. Настрой в Настройки › Память.',
|
||||
'accounts.addAccount': 'Добавить аккаунт',
|
||||
'accounts.addApps': 'Добавить приложения',
|
||||
'accounts.manageAccounts': 'Управление аккаунтами',
|
||||
'accounts.noAccounts': 'Аккаунты не подключены',
|
||||
'accounts.connectAccount': 'Подключи аккаунт для начала работы',
|
||||
|
||||
@@ -825,6 +825,7 @@ const messages: TranslationMap = {
|
||||
'onboarding.custom.memory.defaultDesc': 'OpenHuman 会自动管理记忆存储和检索。无需设置。',
|
||||
'onboarding.custom.memory.configureDesc': '你可以自行查看、导出或清除记忆。在设置 › 记忆中配置。',
|
||||
'accounts.addAccount': '添加账户',
|
||||
'accounts.addApps': '添加应用',
|
||||
'accounts.manageAccounts': '管理账户',
|
||||
'accounts.noAccounts': '未连接任何账户',
|
||||
'accounts.connectAccount': '连接一个账户以开始使用',
|
||||
|
||||
Reference in New Issue
Block a user