Files
api/web/components/UserProfile/DropdownTrigger.vue
Eli Bosley 88087d5201 feat: mount vue apps, not web components (#1639)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Standalone web bundle with auto-mount utilities and a self-contained
test page.
* New responsive modal components for consistent mobile/desktop dialogs.
  * Header actions to copy OS/API versions.

* **Improvements**
* Refreshed UI styles (muted borders), accessibility and animation
refinements.
  * Theming updates and Tailwind v4–aligned, component-scoped styles.
  * Runtime GraphQL endpoint override and CSRF header support.

* **Bug Fixes**
* Safer network fetching and improved manifest/asset loading with
duplicate protection.

* **Tests/Chores**
* Parallel plugin tests, new extractor test suite, and updated
build/test scripts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-03 15:42:21 -04:00

79 lines
2.2 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { Button } from '@unraid/ui';
import {
Bars3Icon,
ExclamationTriangleIcon,
InformationCircleIcon,
ShieldExclamationIcon,
} from '@heroicons/vue/24/solid';
import BrandAvatar from '~/components/Brand/Avatar.vue';
import { useErrorsStore } from '~/store/errors';
import { useServerStore } from '~/store/server';
const { t } = useI18n();
const { errors } = storeToRefs(useErrorsStore());
const { connectPluginInstalled, state, stateData } = storeToRefs(useServerStore());
const showErrorIcon = computed(() => errors.value.length || stateData.value.error);
const text = computed((): string => {
if (stateData.value.error && state.value !== 'EEXPIRED') {
return t('Fix Error');
}
return '';
});
const title = computed((): string => {
if (state.value === 'ENOKEYFILE') {
return t('Get Started');
}
if (state.value === 'EEXPIRED') {
return t('Trial Expired, see options below');
}
if (showErrorIcon.value) {
return t('Learn more about the error');
}
return t('Open Dropdown');
});
</script>
<template>
<Button
variant="header"
size="header"
class="justify-center gap-x-1.5 pl-0"
:title="title"
>
<template v-if="errors.length && errors[0].level">
<InformationCircleIcon
v-if="errors[0].level === 'info'"
class="text-unraid-red fill-current relative w-6 h-6"
/>
<ExclamationTriangleIcon
v-if="errors[0].level === 'warning'"
class="text-unraid-red fill-current relative w-6 h-6"
/>
<ShieldExclamationIcon
v-if="errors[0].level === 'error'"
class="text-unraid-red fill-current relative w-6 h-6"
/>
</template>
<span v-if="text" class="relative leading-none">
<span>{{ text }}</span>
<span
class="absolute bottom-[-3px] inset-x-0 h-0.5 w-full bg-linear-to-r from-unraid-red to-orange rounded opacity-0 group-hover:opacity-100 group-focus:opacity-100 transition-opacity"
/>
</span>
<Bars3Icon class="w-5" />
<BrandAvatar v-if="connectPluginInstalled" />
</Button>
</template>