diff --git a/lang/ar/sidebar.php b/lang/ar/sidebar.php index a9587e13..7203369a 100644 --- a/lang/ar/sidebar.php +++ b/lang/ar/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'إنشاء مساحة عمل', 'create_post' => 'إنشاء منشور', 'profile' => 'الملف الشخصي', + 'my_account' => 'حسابي', + 'account_settings' => 'الحساب والفوترة', + 'workspace_settings' => 'إعدادات مساحة العمل', 'log_out' => 'تسجيل الخروج', 'workspace' => 'مساحة العمل: :name', diff --git a/lang/de/sidebar.php b/lang/de/sidebar.php index c9a593f9..d6cb835c 100644 --- a/lang/de/sidebar.php +++ b/lang/de/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Workspace erstellen', 'create_post' => 'Beitrag erstellen', 'profile' => 'Profil', + 'my_account' => 'Mein Konto', + 'account_settings' => 'Konto & Abrechnung', + 'workspace_settings' => 'Workspace-Einstellungen', 'log_out' => 'Abmelden', 'workspace' => 'Workspace: :name', diff --git a/lang/el/sidebar.php b/lang/el/sidebar.php index a19adb71..5c011ccc 100644 --- a/lang/el/sidebar.php +++ b/lang/el/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Δημιουργία workspace', 'create_post' => 'Δημιουργία δημοσίευσης', 'profile' => 'Προφίλ', + 'my_account' => 'Ο λογαριασμός μου', + 'account_settings' => 'Λογαριασμός και χρέωση', + 'workspace_settings' => 'Ρυθμίσεις workspace', 'log_out' => 'Αποσύνδεση', 'workspace' => 'Workspace: :name', diff --git a/lang/en/sidebar.php b/lang/en/sidebar.php index f9cddbd6..eef5ebbc 100644 --- a/lang/en/sidebar.php +++ b/lang/en/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Create workspace', 'create_post' => 'Create post', 'profile' => 'Profile', + 'my_account' => 'My account', + 'account_settings' => 'Account & billing', + 'workspace_settings' => 'Workspace settings', 'log_out' => 'Log out', 'workspace' => 'Workspace: :name', diff --git a/lang/es/sidebar.php b/lang/es/sidebar.php index c6f768d4..82a4559a 100644 --- a/lang/es/sidebar.php +++ b/lang/es/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Crear workspace', 'create_post' => 'Crear post', 'profile' => 'Perfil', + 'my_account' => 'Mi cuenta', + 'account_settings' => 'Cuenta y facturación', + 'workspace_settings' => 'Configuración del workspace', 'log_out' => 'Cerrar sesión', 'workspace' => 'Workspace: :name', diff --git a/lang/fr/sidebar.php b/lang/fr/sidebar.php index a6325025..e3fc1fd0 100644 --- a/lang/fr/sidebar.php +++ b/lang/fr/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Créer un espace de travail', 'create_post' => 'Créer une publication', 'profile' => 'Profil', + 'my_account' => 'Mon compte', + 'account_settings' => 'Compte et facturation', + 'workspace_settings' => 'Paramètres de l\'espace de travail', 'log_out' => 'Se déconnecter', 'workspace' => 'Espace de travail : :name', diff --git a/lang/it/sidebar.php b/lang/it/sidebar.php index fa517924..2412acca 100644 --- a/lang/it/sidebar.php +++ b/lang/it/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Crea workspace', 'create_post' => 'Crea post', 'profile' => 'Profilo', + 'my_account' => 'Il mio account', + 'account_settings' => 'Account e fatturazione', + 'workspace_settings' => 'Impostazioni workspace', 'log_out' => 'Esci', 'workspace' => 'Workspace: :name', diff --git a/lang/ja/sidebar.php b/lang/ja/sidebar.php index 09ff7c59..5afd811c 100644 --- a/lang/ja/sidebar.php +++ b/lang/ja/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'ワークスペースを作成', 'create_post' => '投稿を作成', 'profile' => 'プロフィール', + 'my_account' => 'マイアカウント', + 'account_settings' => 'アカウントと請求', + 'workspace_settings' => 'ワークスペース設定', 'log_out' => 'ログアウト', 'workspace' => 'ワークスペース: :name', diff --git a/lang/ko/sidebar.php b/lang/ko/sidebar.php index 3d47a2bf..28c4995e 100644 --- a/lang/ko/sidebar.php +++ b/lang/ko/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => '워크스페이스 만들기', 'create_post' => '게시물 만들기', 'profile' => '프로필', + 'my_account' => '내 계정', + 'account_settings' => '계정 및 결제', + 'workspace_settings' => '워크스페이스 설정', 'log_out' => '로그아웃', 'workspace' => '워크스페이스: :name', diff --git a/lang/nl/sidebar.php b/lang/nl/sidebar.php index daca60f2..fd8efbeb 100644 --- a/lang/nl/sidebar.php +++ b/lang/nl/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Workspace aanmaken', 'create_post' => 'Post aanmaken', 'profile' => 'Profiel', + 'my_account' => 'Mijn account', + 'account_settings' => 'Account en facturatie', + 'workspace_settings' => 'Workspace-instellingen', 'log_out' => 'Uitloggen', 'workspace' => 'Workspace: :name', diff --git a/lang/pl/sidebar.php b/lang/pl/sidebar.php index 9e83547d..1018dd8e 100644 --- a/lang/pl/sidebar.php +++ b/lang/pl/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Utwórz przestrzeń roboczą', 'create_post' => 'Utwórz post', 'profile' => 'Profil', + 'my_account' => 'Moje konto', + 'account_settings' => 'Konto i płatności', + 'workspace_settings' => 'Ustawienia workspace', 'log_out' => 'Wyloguj się', 'workspace' => 'Przestrzeń robocza: :name', diff --git a/lang/pt-BR/sidebar.php b/lang/pt-BR/sidebar.php index 0717d621..b090575a 100644 --- a/lang/pt-BR/sidebar.php +++ b/lang/pt-BR/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Criar workspace', 'create_post' => 'Novo post', 'profile' => 'Perfil', + 'my_account' => 'Minha conta', + 'account_settings' => 'Conta e cobrança', + 'workspace_settings' => 'Configurações do workspace', 'log_out' => 'Sair', 'workspace' => 'Workspace: :name', diff --git a/lang/ru/sidebar.php b/lang/ru/sidebar.php index 4ebffe4e..8416f1c2 100644 --- a/lang/ru/sidebar.php +++ b/lang/ru/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Создать рабочее пространство', 'create_post' => 'Создать пост', 'profile' => 'Профиль', + 'my_account' => 'Мой аккаунт', + 'account_settings' => 'Аккаунт и оплата', + 'workspace_settings' => 'Настройки workspace', 'log_out' => 'Выйти', 'workspace' => 'Рабочее пространство: :name', diff --git a/lang/tr/sidebar.php b/lang/tr/sidebar.php index c7936fc9..8c8ba41c 100644 --- a/lang/tr/sidebar.php +++ b/lang/tr/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Çalışma alanı oluştur', 'create_post' => 'Gönderi oluştur', 'profile' => 'Profil', + 'my_account' => 'Hesabım', + 'account_settings' => 'Hesap ve faturalandırma', + 'workspace_settings' => 'Çalışma alanı ayarları', 'log_out' => 'Çıkış yap', 'workspace' => 'Çalışma alanı: :name', diff --git a/lang/uk/sidebar.php b/lang/uk/sidebar.php index 08ed5216..0c0f1fc8 100644 --- a/lang/uk/sidebar.php +++ b/lang/uk/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => 'Створити робочий простір', 'create_post' => 'Створити пост', 'profile' => 'Профіль', + 'my_account' => 'Мій акаунт', + 'account_settings' => 'Акаунт і оплата', + 'workspace_settings' => 'Налаштування робочого простору', 'log_out' => 'Вийти', 'workspace' => 'Робочий простір: :name', diff --git a/lang/zh/sidebar.php b/lang/zh/sidebar.php index 46a3eda6..18a0bb02 100644 --- a/lang/zh/sidebar.php +++ b/lang/zh/sidebar.php @@ -8,6 +8,9 @@ 'create_workspace' => '创建工作区', 'create_post' => '创建帖子', 'profile' => '个人资料', + 'my_account' => '我的账户', + 'account_settings' => '账户与账单', + 'workspace_settings' => '工作区设置', 'log_out' => '退出登录', 'workspace' => '工作区::name', diff --git a/resources/js/components/AppSidebar.vue b/resources/js/components/AppSidebar.vue index 7aea78e4..512a7e1f 100644 --- a/resources/js/components/AppSidebar.vue +++ b/resources/js/components/AppSidebar.vue @@ -1,5 +1,5 @@ \ No newline at end of file + diff --git a/resources/js/components/NavUser.vue b/resources/js/components/NavUser.vue deleted file mode 100644 index 8253dbf4..00000000 --- a/resources/js/components/NavUser.vue +++ /dev/null @@ -1,56 +0,0 @@ - - - diff --git a/resources/js/components/NotificationBell.vue b/resources/js/components/NotificationBell.vue index 7fcd5aab..c18f9e9b 100644 --- a/resources/js/components/NotificationBell.vue +++ b/resources/js/components/NotificationBell.vue @@ -5,6 +5,7 @@ import { IconArchive, IconBell, IconCheck, IconChecks, IconInbox, IconX } from ' import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import { Button } from '@/components/ui/button'; +import { useSidebar } from '@/components/ui/sidebar'; import { Tooltip, TooltipContent, @@ -12,7 +13,7 @@ import { } from '@/components/ui/tooltip'; import dayjs from '@/dayjs'; import { accounts } from '@/routes/app'; -import { index, read, readAll, archiveAll } from '@/routes/app/notifications'; +import { archiveAll, index, read, readAll } from '@/routes/app/notifications'; import { edit as editPost } from '@/routes/app/posts'; import type { SharedData } from '@/types'; @@ -165,6 +166,12 @@ const onEscape = (event: KeyboardEvent) => { } }; +// The panel is teleported to the body with an offset anchored to the open +// sidebar — closing the sidebar would leave it floating mid-screen. +const { state: sidebarState } = useSidebar(); + +watch(sidebarState, () => close()); + const formatTime = (date: string) => { return dayjs.utc(date).fromNow(); }; @@ -205,16 +212,16 @@ onBeforeUnmount(() => {
diff --git a/resources/js/components/UserMenuContent.vue b/resources/js/components/UserMenuContent.vue deleted file mode 100644 index 2fc9c54d..00000000 --- a/resources/js/components/UserMenuContent.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - diff --git a/resources/js/components/WorkspaceMenuContent.vue b/resources/js/components/WorkspaceMenuContent.vue new file mode 100644 index 00000000..9728c695 --- /dev/null +++ b/resources/js/components/WorkspaceMenuContent.vue @@ -0,0 +1,235 @@ + + + diff --git a/tests/Browser/SidebarMenuTest.php b/tests/Browser/SidebarMenuTest.php new file mode 100644 index 00000000..a190fd0b --- /dev/null +++ b/tests/Browser/SidebarMenuTest.php @@ -0,0 +1,190 @@ +script(<< { + const sel = '[data-testid="{$testId}"]'; + for (let i = 0; i < 100; i++) { + const el = document.querySelector(sel); + if (el && el.getBoundingClientRect().height > 0) return; + await new Promise((r) => setTimeout(r, 50)); + } + })(); + JS); +} + +test('account owners see account and workspace settings in the sidebar menu', function () { + config(['trypost.self_hosted' => false]); + + $user = User::factory()->create(); + $workspace = Workspace::factory()->create([ + 'user_id' => $user->id, + 'account_id' => $user->account_id, + ]); + $workspace->members()->attach($user->id, ['role' => Role::Admin->value]); + $user->update(['current_workspace_id' => $workspace->id]); + + subscribeAccount($user->account); + + $this->actingAs($user); + + $page = visit(route('app.calendar')); + + waitForSidebarTestId($page, 'sidebar-workspace-menu'); + + $page->assertVisible('@sidebar-workspace-menu') + ->click('@sidebar-workspace-menu'); + + waitForSidebarTestId($page, 'logout-button'); + + $page->assertVisible('@sidebar-menu-my-account') + ->assertVisible('@sidebar-menu-account-settings') + ->assertVisible('@sidebar-menu-workspace-settings') + ->assertVisible('@logout-button'); +}); + +test('account billing is hidden in the sidebar menu when self-hosted', function () { + config(['trypost.self_hosted' => true]); + + $user = User::factory()->create(); + $workspace = Workspace::factory()->create([ + 'user_id' => $user->id, + 'account_id' => $user->account_id, + ]); + $workspace->members()->attach($user->id, ['role' => Role::Admin->value]); + $user->update(['current_workspace_id' => $workspace->id]); + + $this->actingAs($user); + + $page = visit(route('app.calendar')); + + waitForSidebarTestId($page, 'sidebar-workspace-menu'); + + $page->assertVisible('@sidebar-workspace-menu') + ->click('@sidebar-workspace-menu'); + + waitForSidebarTestId($page, 'logout-button'); + + $page->assertVisible('@sidebar-menu-my-account') + ->assertMissing('@sidebar-menu-account-settings') + ->assertVisible('@sidebar-menu-workspace-settings'); +}); + +test('workspace admins see workspace settings but not account billing', function () { + config(['trypost.self_hosted' => false]); + + [ + 'owner' => $owner, + 'member' => $admin, + 'shared_workspaces' => [$workspace], + ] = strandedMemberOnSharedAccount( + sharedWorkspaces: 1, + attachMember: true, + setMemberCurrent: true, + ); + + $workspace->members()->updateExistingPivot($admin->id, [ + 'role' => Role::Admin->value, + ]); + + subscribeAccount($owner->account); + + $this->actingAs($admin->fresh()); + + $page = visit(route('app.calendar')); + + waitForSidebarTestId($page, 'sidebar-workspace-menu'); + + $page->assertVisible('@sidebar-workspace-menu') + ->click('@sidebar-workspace-menu'); + + waitForSidebarTestId($page, 'logout-button'); + + $page->assertVisible('@sidebar-menu-my-account') + ->assertVisible('@sidebar-menu-workspace-settings') + ->assertMissing('@sidebar-menu-account-settings') + ->assertVisible('@logout-button'); +}); + +test('workspace members do not see account or workspace settings in the sidebar menu', function () { + config(['trypost.self_hosted' => false]); + + [ + 'owner' => $owner, + 'member' => $member, + 'shared_workspaces' => [$workspace], + ] = strandedMemberOnSharedAccount( + sharedWorkspaces: 1, + attachMember: true, + setMemberCurrent: true, + ); + + $workspace->members()->updateExistingPivot($member->id, [ + 'role' => Role::Member->value, + ]); + + subscribeAccount($owner->account); + + $this->actingAs($member->fresh()); + + $page = visit(route('app.calendar')); + + waitForSidebarTestId($page, 'sidebar-workspace-menu'); + + $page->assertVisible('@sidebar-workspace-menu') + ->click('@sidebar-workspace-menu'); + + waitForSidebarTestId($page, 'logout-button'); + + $page->assertVisible('@sidebar-menu-my-account') + ->assertMissing('@sidebar-menu-account-settings') + ->assertMissing('@sidebar-menu-workspace-settings') + ->assertVisible('@logout-button'); +}); + +test('workspace viewers do not see account or workspace settings in the sidebar menu', function () { + config(['trypost.self_hosted' => false]); + + [ + 'owner' => $owner, + 'member' => $viewer, + 'shared_workspaces' => [$workspace], + ] = strandedMemberOnSharedAccount( + sharedWorkspaces: 1, + attachMember: true, + setMemberCurrent: true, + ); + + $workspace->members()->updateExistingPivot($viewer->id, [ + 'role' => Role::Viewer->value, + ]); + + subscribeAccount($owner->account); + + $this->actingAs($viewer->fresh()); + + $page = visit(route('app.calendar')); + + waitForSidebarTestId($page, 'sidebar-workspace-menu'); + + $page->assertVisible('@sidebar-workspace-menu') + ->click('@sidebar-workspace-menu'); + + waitForSidebarTestId($page, 'logout-button'); + + $page->assertVisible('@sidebar-menu-my-account') + ->assertMissing('@sidebar-menu-account-settings') + ->assertMissing('@sidebar-menu-workspace-settings') + ->assertVisible('@logout-button'); +});