mirror of
https://github.com/unraid/api.git
synced 2026-05-02 05:01:34 -05:00
0c727c37f4
- Adjusted the positioning of the UserProfile component to be absolute, ensuring it aligns correctly within its parent container. - Modified the clean-unraid.sh script to remove the entire components directory instead of just the standalone apps directory, enhancing cleanup efficiency. - Added a cleanup step in deploy-dev.sh to clear the remote standalone directory before deployment, ensuring a fresh setup. These changes improve the layout of the UserProfile component and streamline the deployment process by ensuring no residual files remain.
141 lines
4.4 KiB
Vue
141 lines
4.4 KiB
Vue
<script lang="ts" setup>
|
|
import { onBeforeMount, onMounted, ref } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
import { DropdownMenu, Button } from '@unraid/ui';
|
|
import { useClipboardWithToast } from '~/composables/useClipboardWithToast';
|
|
import { devConfig } from '~/helpers/env';
|
|
|
|
import type { Server } from '~/types/server';
|
|
|
|
import NotificationsSidebar from '~/components/Notifications/Sidebar.vue';
|
|
import UpcDropdownContent from '~/components/UserProfile/DropdownContent.vue';
|
|
import UpcDropdownTrigger from '~/components/UserProfile/DropdownTrigger.vue';
|
|
import UpcServerStatus from '~/components/UserProfile/ServerStatus.vue';
|
|
import { useCallbackActionsStore } from '~/store/callbackActions';
|
|
import { useServerStore } from '~/store/server';
|
|
import { useThemeStore } from '~/store/theme';
|
|
|
|
export interface Props {
|
|
server?: Server | string;
|
|
}
|
|
const props = defineProps<Props>();
|
|
|
|
const { t } = useI18n();
|
|
|
|
const callbackStore = useCallbackActionsStore();
|
|
const serverStore = useServerStore();
|
|
|
|
const { callbackData } = storeToRefs(callbackStore);
|
|
const { name, description, guid, keyfile, lanIp } = storeToRefs(serverStore);
|
|
const { bannerGradient, theme } = storeToRefs(useThemeStore());
|
|
|
|
// Control dropdown open state
|
|
const dropdownOpen = ref(false);
|
|
|
|
/**
|
|
* Copy LAN IP on server name click
|
|
*/
|
|
const { copyWithNotification } = useClipboardWithToast();
|
|
const copyLanIp = async () => {
|
|
if (lanIp.value) {
|
|
await copyWithNotification(lanIp.value, t('LAN IP Copied'));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets the server store and locale messages then listen for callbacks
|
|
*/
|
|
onBeforeMount(() => {
|
|
if (!props.server) {
|
|
throw new Error('Server data not present');
|
|
}
|
|
|
|
if (typeof props.server === 'object') {
|
|
// Handles the testing dev Vue component
|
|
serverStore.setServer(props.server);
|
|
} else if (typeof props.server === 'string') {
|
|
// Handle web component
|
|
const parsedServerProp = JSON.parse(props.server);
|
|
serverStore.setServer(parsedServerProp);
|
|
}
|
|
|
|
// look for any callback params
|
|
callbackStore.watcher();
|
|
|
|
if (guid.value && keyfile.value) {
|
|
if (callbackData.value) {
|
|
return console.debug(
|
|
'Renew callback detected, skipping auto check for key replacement, renewal eligibility, and OS Update.'
|
|
);
|
|
}
|
|
} else {
|
|
console.warn(
|
|
'A valid keyfile and USB Flash boot device are required to check for key renewals, key replacement eligibiliy, and OS update availability.'
|
|
);
|
|
}
|
|
});
|
|
|
|
onMounted(() => {
|
|
if (devConfig.VITE_MOCK_USER_SESSION && devConfig.NODE_ENV === 'development') {
|
|
document.cookie = 'unraid_session_cookie=mockusersession';
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div id="UserProfile" class="text-foreground z-20 flex flex-col h-full gap-y-1 pt-2 pr-2 absolute top-0 right-0">
|
|
<div
|
|
v-if="bannerGradient"
|
|
class="absolute z-0 w-full top-0 bottom-0 right-0"
|
|
:style="bannerGradient"
|
|
/>
|
|
|
|
<UpcServerStatus class="relative z-10" />
|
|
|
|
<div class="relative z-10 flex flex-row items-center justify-end gap-x-2 h-full">
|
|
<div
|
|
class="text-base relative flex flex-col-reverse items-center md:items-center md:flex-row border-0 text-header-text-primary"
|
|
>
|
|
<template v-if="description && theme?.descriptionShow">
|
|
<span class="text-center md:text-right text-base hidden md:inline-flex md:items-center" v-html="description" />
|
|
<span class="text-header-text-secondary hidden md:inline-flex md:items-center px-2">•</span>
|
|
</template>
|
|
<Button
|
|
v-if="lanIp"
|
|
variant="ghost"
|
|
:title="t('Click to Copy LAN IP {0}', [lanIp])"
|
|
class="text-header-text-primary text-base p-0 h-auto opacity-100 hover:opacity-75 focus:opacity-75 transition-opacity flex items-center"
|
|
@click="copyLanIp()"
|
|
>
|
|
{{ name }}
|
|
</Button>
|
|
<span v-else class="text-header-text-primary text-sm xs:text-base flex items-center">
|
|
{{ name }}
|
|
</span>
|
|
</div>
|
|
|
|
<NotificationsSidebar />
|
|
|
|
<DropdownMenu
|
|
v-model:open="dropdownOpen"
|
|
align="end"
|
|
side="bottom"
|
|
:side-offset="4"
|
|
>
|
|
<template #trigger>
|
|
<UpcDropdownTrigger />
|
|
</template>
|
|
<template #content>
|
|
<div class="max-w-[350px] sm:min-w-[350px]">
|
|
<UpcDropdownContent
|
|
@close-dropdown="dropdownOpen = false"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</DropdownMenu>
|
|
</div>
|
|
</div>
|
|
</template>
|