fix: omit Connect actions in UPC when plugin is not installed (#1417)

Removes Connect branding in dropdown when connect plugin is not installed.

Preview of dropdown:
<img width="453" alt="image"
src="https://github.com/user-attachments/assets/b3ba3954-f2d3-4760-a1e2-91556eb43903"
/>

## Summary by CodeRabbit

- **New Features**
- The Notifications Sidebar is now always visible in the user profile,
regardless of plugin installation status.

- **Refactor**
  - Improved detection logic for the connect plugin to enhance accuracy.
- Centralized and standardized the "Settings" link in the user profile
dropdown for consistent user experience.
- The account status section in Connect Settings now only appears when
the connect plugin is installed.
- Updated the Connect page title from "Unraid Connect" to "Unraid API"
for clarity.

- **Chores**
- Extended linting to include Vue files for better code quality
enforcement.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Pujit Mehrotra
2025-06-16 11:50:37 -04:00
committed by GitHub
parent 3dcbfbe489
commit 8c8a5276b4
7 changed files with 36 additions and 21 deletions

View File

@@ -52,7 +52,7 @@
"pre-commit": "pnpm lint-staged" "pre-commit": "pnpm lint-staged"
}, },
"lint-staged": { "lint-staged": {
"*.{js,jsx,ts,tsx}": [ "*.{js,jsx,ts,tsx,vue}": [
"pnpm lint:fix" "pnpm lint:fix"
] ]
}, },

View File

@@ -1,5 +1,5 @@
Menu="ManagementAccess:100" Menu="ManagementAccess:100"
Title="Unraid Connect" Title="Unraid API"
Icon="icon-u-globe" Icon="icon-u-globe"
Tag="globe" Tag="globe"
--- ---

View File

@@ -146,8 +146,21 @@ class ServerState
private function setConnectValues() private function setConnectValues()
{ {
if (file_exists('/usr/local/bin/unraid-api')) { $apiConfigPath = '/boot/config/plugins/dynamix.my.servers/configs/api.json';
$this->connectPluginInstalled = 'dynamix.unraid.net.plg'; if (!file_exists($apiConfigPath)) {
return; // plugin is not installed; exit early
}
$apiConfig = @json_decode(file_get_contents($apiConfigPath), true);
$pluginName = 'unraid-api-plugin-connect'; // name of connect plugin's npm package
if ($apiConfig && is_array($apiConfig['plugins'])) {
foreach ($apiConfig['plugins'] as $plugin) {
// recognize npm version identifiers inside $apiConfig['plugins']
if ($plugin === $pluginName || strpos($plugin, $pluginName . '@') === 0) {
$this->connectPluginInstalled = 'dynamix.unraid.net.plg';
break;
}
}
} }
// exit early if the plugin is not installed // exit early if the plugin is not installed

View File

@@ -294,13 +294,13 @@ describe('UserProfile.ce.vue', () => {
expect(heading.html()).toContain(initialServerData.description); expect(heading.html()).toContain(initialServerData.description);
}); });
it('conditionally renders notifications sidebar based on connectPluginInstalled', async () => { it('always renders notifications sidebar, regardless of connectPluginInstalled', async () => {
expect(wrapper.find('[data-testid="notifications-sidebar"]').exists()).toBe(true); expect(wrapper.find('[data-testid="notifications-sidebar"]').exists()).toBe(true);
serverStore.connectPluginInstalled = ''; serverStore.connectPluginInstalled = '';
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.find('[data-testid="notifications-sidebar"]').exists()).toBe(false); expect(wrapper.find('[data-testid="notifications-sidebar"]').exists()).toBe(true);
}); });
it('conditionally renders banner based on theme store', async () => { it('conditionally renders banner based on theme store', async () => {

View File

@@ -8,11 +8,14 @@ import { useMutation, useQuery } from '@vue/apollo-composable';
import { BrandButton, jsonFormsRenderers, Label } from '@unraid/ui'; import { BrandButton, jsonFormsRenderers, Label } from '@unraid/ui';
import { JsonForms } from '@jsonforms/vue'; import { JsonForms } from '@jsonforms/vue';
import { useServerStore } from '~/store/server';
// unified settings values are returned as JSON, so use a generic record type // unified settings values are returned as JSON, so use a generic record type
// import type { ConnectSettingsValues } from '~/composables/gql/graphql'; // import type { ConnectSettingsValues } from '~/composables/gql/graphql';
import { getConnectSettingsForm, updateConnectSettings } from './graphql/settings.query'; import { getConnectSettingsForm, updateConnectSettings } from './graphql/settings.query';
const { connectPluginInstalled } = storeToRefs(useServerStore());
/**-------------------------------------------- /**--------------------------------------------
* Settings State & Form definition * Settings State & Form definition
*---------------------------------------------**/ *---------------------------------------------**/
@@ -99,8 +102,10 @@ const onChange = ({ data }: { data: Record<string, unknown> }) => {
<div <div
class="grid grid-cols-settings items-baseline pl-3 gap-y-6 [&>*:nth-child(odd)]:text-end [&>*:nth-child(even)]:ml-10" class="grid grid-cols-settings items-baseline pl-3 gap-y-6 [&>*:nth-child(odd)]:text-end [&>*:nth-child(even)]:ml-10"
> >
<Label>Account Status:</Label> <template v-if="connectPluginInstalled">
<div v-html="'<unraid-i18n-host><unraid-auth></unraid-auth></unraid-i18n-host>'"></div> <Label>Account Status:</Label>
<div v-html="'<unraid-i18n-host><unraid-auth></unraid-auth></unraid-i18n-host>'"></div>
</template>
<Label>Download Unraid API Logs:</Label> <Label>Download Unraid API Logs:</Label>
<div <div
v-html=" v-html="

View File

@@ -23,7 +23,7 @@ const callbackStore = useCallbackActionsStore();
const serverStore = useServerStore(); const serverStore = useServerStore();
const { callbackData } = storeToRefs(callbackStore); const { callbackData } = storeToRefs(callbackStore);
const { name, description, guid, keyfile, lanIp, connectPluginInstalled } = storeToRefs(serverStore); const { name, description, guid, keyfile, lanIp } = storeToRefs(serverStore);
const { bannerGradient, theme } = storeToRefs(useThemeStore()); const { bannerGradient, theme } = storeToRefs(useThemeStore());
/** /**
@@ -134,10 +134,7 @@ onMounted(() => {
<div class="block w-2px h-24px bg-header-text-secondary" /> <div class="block w-2px h-24px bg-header-text-secondary" />
<template v-if="connectPluginInstalled"> <NotificationsSidebar />
<!-- Keep the sidebar out of staging/prod builds, but easily accessible for development -->
<NotificationsSidebar />
</template>
<UpcDropdownMenu :t="t"> <UpcDropdownMenu :t="t">
<template #trigger> <template #trigger>

View File

@@ -150,15 +150,15 @@ const links = computed((): UserProfileLink[] => {
title: props.t('Opens Connect in new tab'), title: props.t('Opens Connect in new tab'),
}, },
...[manageUnraidNetAccount.value], ...[manageUnraidNetAccount.value],
{
href: WEBGUI_CONNECT_SETTINGS.toString(),
icon: CogIcon,
text: props.t('Settings'),
title: props.t('Go to Connect plugin settings'),
},
...signOutAction.value, ...signOutAction.value,
] ]
: [...[manageUnraidNetAccount.value]]), : [...[manageUnraidNetAccount.value]]),
{
href: WEBGUI_CONNECT_SETTINGS.toString(),
icon: CogIcon,
text: props.t('Settings'),
title: props.t('Go to API Settings'),
},
]; ];
}); });