refactor: unraid-ui-web-migration (#1106)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced enhanced stepper components for smoother multi-step
interactions.
- Added new loading indicators and improved the loading experience with
customizable variants.
  
- **UI Improvements**
- Refreshed the global color palette and updated styling across buttons,
badges, and loading indicators for a more modern, consistent experience.
- Improved the organization and readability of templates and styles
across various components.

- **Code & Dependency Updates**
- Updated key dependencies and revised the theme and configuration
settings to improve performance and maintainability.
- Introduced new environment variables for better configuration
management.

- **Legacy Cleanup**
- Removed deprecated components and streamlined registrations to
simplify the codebase without affecting end-user functionality.
- Eliminated unused utility functions and legacy code to enhance overall
code quality.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: mdatelle <mike@datelle.net>
Co-authored-by: Eli Bosley <ekbosley@gmail.com>
This commit is contained in:
Michael Datelle
2025-02-12 18:00:06 -05:00
committed by GitHub
parent f76c0f05fb
commit 741e8532ab
159 changed files with 2044 additions and 3864 deletions

View File

@@ -1,6 +1,9 @@
<script lang="ts" setup>
// @todo ensure key installs and updateOs can be handled at the same time
// @todo with multiple actions of key install and update after successful key install, rather than showing default success message, show a message to have them confirm the update
import { storeToRefs } from 'pinia';
import { useClipboard } from '@vueuse/core';
import {
CheckIcon,
ChevronDoubleDownIcon,
@@ -9,16 +12,17 @@ import {
WrenchScrewdriverIcon,
XMarkIcon,
} from '@heroicons/vue/24/solid';
import { useClipboard } from '@vueuse/core';
import { BrandButton, BrandLoading } from '@unraid/ui';
import { WEBGUI_CONNECT_SETTINGS, WEBGUI_TOOLS_REGISTRATION } from '~/helpers/urls';
import type { ComposerTranslation } from 'vue-i18n';
import { useAccountStore } from '~/store/account';
import { useCallbackActionsStore } from '~/store/callbackActions';
import { useInstallKeyStore } from '~/store/installKey';
// import { usePromoStore } from '~/store/promo';
import { useServerStore } from '~/store/server';
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
import { storeToRefs } from 'pinia';
import type { ComposerTranslation } from 'vue-i18n';
export interface Props {
open?: boolean;
@@ -366,7 +370,7 @@ const showUpdateEligibility = computed(() => {
<template v-if="callbackStatus === 'success' || updateOsStatus === 'confirming'" #footer>
<div class="flex flex-row justify-center gap-16px">
<template v-if="callbackStatus === 'success'">
<BrandButton btn-style="underline" :icon="XMarkIcon" :text="closeText" @click="close" />
<BrandButton variant="underline" :icon="XMarkIcon" :text="closeText" @click="close" />
<template v-if="connectPluginInstalled && accountActionType === 'signIn'">
<BrandButton
@@ -394,7 +398,7 @@ const showUpdateEligibility = computed(() => {
<template v-if="updateOsStatus === 'confirming' && !stateDataError">
<BrandButton
btn-style="underline"
variant="underline"
:icon="XMarkIcon"
:text="t('Cancel')"
@click="cancelUpdateOs"
@@ -423,6 +427,7 @@ const showUpdateEligibility = computed(() => {
<style lang="postcss">
/* Import unraid-ui globals first */
@import '@unraid/ui/styles';
@import '../../assets/main.css';
.unraid_mark_2,
.unraid_mark_4 {

View File

@@ -1,13 +1,15 @@
<script setup lang="ts">
import { ExclamationTriangleIcon, CheckCircleIcon, UserCircleIcon } from '@heroicons/vue/24/solid';
import { storeToRefs } from 'pinia';
import { CheckCircleIcon, ExclamationTriangleIcon, UserCircleIcon } from '@heroicons/vue/24/solid';
import { BrandLoading } from '@unraid/ui';
import type { ComposerTranslation } from 'vue-i18n';
import BrandLoading from '~/components/Brand/Loading.vue';
import { useUnraidApiStore } from '~/store/unraidApi';
import { useServerStore } from '~/store/server';
import { useUnraidApiStore } from '~/store/unraidApi';
const props = defineProps<{ t: ComposerTranslation; }>();
const props = defineProps<{ t: ComposerTranslation }>();
const { username } = storeToRefs(useServerStore());

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import {
ArrowPathIcon,
ArrowTopRightOnSquareIcon,
@@ -9,22 +10,24 @@ import {
KeyIcon,
UserIcon,
} from '@heroicons/vue/24/solid';
import type { ComposerTranslation } from 'vue-i18n';
import { BrandLogoConnect } from '@unraid/ui';
import {
CONNECT_DASHBOARD,
WEBGUI_CONNECT_SETTINGS,
WEBGUI_TOOLS_REGISTRATION,
WEBGUI_TOOLS_DOWNGRADE,
WEBGUI_TOOLS_REGISTRATION,
WEBGUI_TOOLS_UPDATE,
} from '~/helpers/urls';
import type { UserProfileLink } from '~/types/userProfile';
import type { ComposerTranslation } from 'vue-i18n';
import { useAccountStore } from '~/store/account';
import { useErrorsStore } from '~/store/errors';
import { useServerStore } from '~/store/server';
import { useUpdateOsStore } from '~/store/updateOs';
import type { UserProfileLink } from '~/types/userProfile';
const props = defineProps<{ t: ComposerTranslation; }>();
const props = defineProps<{ t: ComposerTranslation }>();
const accountStore = useAccountStore();
const errorsStore = useErrorsStore();
@@ -40,23 +43,29 @@ const {
stateData,
stateDataError,
} = storeToRefs(useServerStore());
const {
available: osUpdateAvailable,
availableWithRenewal: osUpdateAvailableWithRenewal,
} = storeToRefs(updateOsStore);
const { available: osUpdateAvailable, availableWithRenewal: osUpdateAvailableWithRenewal } =
storeToRefs(updateOsStore);
const signInAction = computed(() => stateData.value.actions?.filter((act: { name: string; }) => act.name === 'signIn') ?? []);
const signOutAction = computed(() => stateData.value.actions?.filter((act: { name: string; }) => act.name === 'signOut') ?? []);
const signInAction = computed(
() => stateData.value.actions?.filter((act: { name: string }) => act.name === 'signIn') ?? []
);
const signOutAction = computed(
() => stateData.value.actions?.filter((act: { name: string }) => act.name === 'signOut') ?? []
);
/**
* Filter out the renew action from the key actions so we can display it separately and link to the Tools > Registration page
*/
const filteredKeyActions = computed(() => keyActions.value?.filter(action => !['renew'].includes(action.name)));
const filteredKeyActions = computed(() =>
keyActions.value?.filter((action) => !['renew'].includes(action.name))
);
const manageUnraidNetAccount = computed((): UserProfileLink => {
return {
external: true,
click: () => { accountStore.manage(); },
click: () => {
accountStore.manage();
},
icon: UserIcon,
text: props.t('Manage Unraid.net Account'),
title: props.t('Manage Unraid.net Account in new tab'),
@@ -86,13 +95,15 @@ const updateOsResponseModalOpenButton = computed((): UserProfileLink => {
});
const rebootDetectedButton = computed((): UserProfileLink => {
return {
href: rebootType.value === 'downgrade'
? WEBGUI_TOOLS_DOWNGRADE.toString()
: WEBGUI_TOOLS_UPDATE.toString(),
href:
rebootType.value === 'downgrade'
? WEBGUI_TOOLS_DOWNGRADE.toString()
: WEBGUI_TOOLS_UPDATE.toString(),
icon: ExclamationTriangleIcon,
text: rebootType.value === 'downgrade'
? props.t('Reboot Required for Downgrade')
: props.t('Reboot Required for Update'),
text:
rebootType.value === 'downgrade'
? props.t('Reboot Required for Downgrade')
: props.t('Reboot Required for Update'),
};
});
@@ -111,15 +122,17 @@ const updateOsButton = computed((): UserProfileLink[] => {
return btns;
});
const links = computed(():UserProfileLink[] => {
const links = computed((): UserProfileLink[] => {
return [
...(regUpdatesExpired.value
? [{
href: WEBGUI_TOOLS_REGISTRATION.toString(),
icon: KeyIcon,
text: props.t('OS Update Eligibility Expired'),
title: props.t('Go to Tools > Registration to Learn More'),
}]
? [
{
href: WEBGUI_TOOLS_REGISTRATION.toString(),
icon: KeyIcon,
text: props.t('OS Update Eligibility Expired'),
title: props.t('Go to Tools > Registration to Learn More'),
},
]
: []),
// ensure we only show the update button when we don't have an error
@@ -136,31 +149,36 @@ const links = computed(():UserProfileLink[] => {
text: props.t('Go to Connect'),
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]]),
];
});
const showErrors = computed(() => errors.value.length);
const showConnectStatus = computed(() => !showErrors.value && !stateData.value.error && registered.value && connectPluginInstalled.value);
const showKeyline = computed(() =>
(showConnectStatus.value && (keyActions.value?.length || links.value.length)) ||
unraidConnectWelcome.value
const showConnectStatus = computed(
() => !showErrors.value && !stateData.value.error && registered.value && connectPluginInstalled.value
);
const showKeyline = computed(
() =>
(showConnectStatus.value && (keyActions.value?.length || links.value.length)) ||
unraidConnectWelcome.value
);
const unraidConnectWelcome = computed(() => {
if (connectPluginInstalled.value && !registered.value && !errors.value.length && !stateDataError.value) {
if (
connectPluginInstalled.value &&
!registered.value &&
!errors.value.length &&
!stateDataError.value
) {
return {
heading: props.t('Thank you for installing Connect!'),
message: props.t('Sign In to your Unraid.net account to get started'),
@@ -172,9 +190,16 @@ const unraidConnectWelcome = computed(() => {
<template>
<div class="flex flex-col gap-y-8px min-w-300px max-w-350px">
<header v-if="connectPluginInstalled" class="flex flex-col items-start justify-between mt-8px mx-8px">
<header
v-if="connectPluginInstalled"
class="flex flex-col items-start justify-between mt-8px mx-8px"
>
<h2 class="text-18px leading-none flex flex-row gap-x-4px items-center justify-between">
<BrandLogoConnect gradient-start="currentcolor" gradient-stop="currentcolor" class="text-foreground w-[120px]" />
<BrandLogoConnect
gradient-start="currentcolor"
gradient-stop="currentcolor"
class="text-foreground w-[120px]"
/>
<UpcBeta />
</h2>
<template v-if="unraidConnectWelcome">

View File

@@ -1,13 +1,18 @@
<script lang="ts" setup>
import { BrandButton, BrandLoading } from '@unraid/ui';
import { useServerStore } from '~/store/server';
import { useUnraidApiStore } from '~/store/unraidApi';
import { storeToRefs } from 'pinia';
import { h } from 'vue';
import { storeToRefs } from 'pinia';
import { BrandButton, BrandLoading } from '@unraid/ui';
import type { ComposerTranslation } from 'vue-i18n';
import { useServerStore } from '~/store/server';
import { useUnraidApiStore } from '~/store/unraidApi';
defineProps<{ t: ComposerTranslation }>();
const BrandLoadingIcon = () => h(BrandLoading, { variant: 'white' });
const { expireTime, connectPluginInstalled, state, stateData } = storeToRefs(useServerStore());
const { unraidApiStatus, unraidApiRestartAction } = storeToRefs(useUnraidApiStore());
@@ -35,11 +40,7 @@ const showExpireTime = computed(
<BrandButton
class="w-full"
:disabled="unraidApiStatus === 'connecting' || unraidApiStatus === 'restarting'"
:icon="
unraidApiStatus === 'restarting'
? h(BrandLoading, { variant: 'white' })
: unraidApiRestartAction?.icon
"
:icon="unraidApiStatus === 'restarting' ? BrandLoadingIcon : unraidApiRestartAction?.icon"
:text="
unraidApiStatus === 'restarting' ? t('Restarting unraid-api…') : t('Restart unraid-api')
"

View File

@@ -2,16 +2,15 @@
/**
* @todo future idea turn this into a carousel. each feature could have a short video if we ever them
*/
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
import { CONNECT_DOCS } from '~/helpers/urls';
import type { UserProfilePromoFeature } from '~/types/userProfile';
import type { ComposerTranslation } from 'vue-i18n';
import useInstallPlugin from '~/composables/installPlugin';
import { CONNECT_DOCS } from '~/helpers/urls';
import { usePromoStore } from '~/store/promo';
import type { UserProfilePromoFeature } from '~/types/userProfile';
import 'tailwindcss/tailwind.css';
import '~/assets/main.css';
export interface Props {
open?: boolean;
@@ -46,11 +45,11 @@ const features = ref<UserProfilePromoFeature[]>([
},
{
title: 'Real-time Monitoring',
copy: 'Get an overview of your server\'s state, storage space, apps and VMs status, and more.',
copy: "Get an overview of your server's state, storage space, apps and VMs status, and more.",
},
{
title: 'Customizable Dashboard Tiles',
copy: 'Set custom server tiles how you like and automatically display your server\'s banner image on your Connect Dashboard.',
copy: "Set custom server tiles how you like and automatically display your server's banner image on your Connect Dashboard.",
},
{
title: 'License Management',
@@ -65,7 +64,9 @@ const features = ref<UserProfilePromoFeature[]>([
const staging = ref(false);
const connectPluginUrl = computed((): string => {
const url = new URL(`https://sfo2.digitaloceanspaces.com/unraid-dl/unraid-api/dynamix.unraid.net${staging.value ? '.staging.plg' : '.plg'}`);
const url = new URL(
`https://sfo2.digitaloceanspaces.com/unraid-dl/unraid-api/dynamix.unraid.net${staging.value ? '.staging.plg' : '.plg'}`
);
return url.toString();
});
@@ -102,8 +103,20 @@ const { install } = useInstallPlugin();
<template #footer>
<div class="w-full max-w-xs flex flex-col items-center gap-y-16px mx-auto">
<SwitchGroup v-if="import.meta.env.DEV" as="div" class="flex items-center justify-center">
<Switch v-model="staging" :class="[staging ? 'bg-indigo-600' : 'bg-gray-200', 'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2']">
<span aria-hidden="true" :class="[staging ? 't-x-5' : 't-x-0', 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out']" />
<Switch
v-model="staging"
:class="[
staging ? 'bg-indigo-600' : 'bg-gray-200',
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2',
]"
>
<span
aria-hidden="true"
:class="[
staging ? 't-x-5' : 't-x-0',
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
]"
/>
</Switch>
<SwitchLabel as="span" class="ml-3 text-12px">
<span class="font-semibold">Install Staging</span>
@@ -140,7 +153,7 @@ const { install } = useInstallPlugin();
</template>
<style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Import unraid-ui globals first */
@import '@unraid/ui/styles';
@import '../../assets/main.css';
</style>

View File

@@ -1,5 +1,8 @@
<script lang="ts" setup>
import { storeToRefs } from 'pinia';
import { BrandLoading } from '@unraid/ui';
import type { ComposerTranslation } from 'vue-i18n';
import { useTrialStore } from '~/store/trial';