mirror of
https://github.com/unraid/api.git
synced 2026-01-06 08:39:54 -06:00
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:
@@ -1,12 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { ArrowPathIcon, ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
|
||||
import { BrandButton } from '@unraid/ui';
|
||||
|
||||
import type { BrandButtonProps } from '@unraid/ui';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import { useAccountStore } from '~/store/account';
|
||||
import type { ButtonStyle } from '~/types/ui/button';
|
||||
|
||||
defineProps<{
|
||||
btnStyle?: ButtonStyle;
|
||||
variant?: BrandButtonProps['variant'];
|
||||
t: ComposerTranslation;
|
||||
}>();
|
||||
|
||||
@@ -16,7 +18,7 @@ const accountStore = useAccountStore();
|
||||
<template>
|
||||
<div class="flex flex-col sm:flex-shrink-0 sm:flex-grow-0 items-center">
|
||||
<BrandButton
|
||||
:btn-style="btnStyle"
|
||||
:variant="variant"
|
||||
:icon="ArrowPathIcon"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
:text="t('Check for OS Updates')"
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import {
|
||||
ArrowTopRightOnSquareIcon,
|
||||
ArrowSmallRightIcon,
|
||||
ArrowTopRightOnSquareIcon,
|
||||
EyeIcon,
|
||||
KeyIcon,
|
||||
ServerStackIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed } from 'vue';
|
||||
import { BrandButton, BrandLoading } from '@unraid/ui';
|
||||
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import { usePurchaseStore } from '~/store/purchase';
|
||||
@@ -31,12 +34,8 @@ const updateOsStore = useUpdateOsStore();
|
||||
const updateOsChangelogStore = useUpdateOsChangelogStore();
|
||||
|
||||
const { availableWithRenewal } = storeToRefs(updateOsStore);
|
||||
const {
|
||||
releaseForUpdate,
|
||||
mutatedParsedChangelog,
|
||||
parseChangelogFailed,
|
||||
parsedChangelogTitle,
|
||||
} = storeToRefs(updateOsChangelogStore);
|
||||
const { releaseForUpdate, mutatedParsedChangelog, parseChangelogFailed, parsedChangelogTitle } =
|
||||
storeToRefs(updateOsChangelogStore);
|
||||
|
||||
const showExtendKeyButton = computed(() => {
|
||||
return availableWithRenewal.value;
|
||||
@@ -62,27 +61,21 @@ const showExtendKeyButton = computed(() => {
|
||||
v-html="mutatedParsedChangelog"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-else-if="parseChangelogFailed"
|
||||
class="text-center flex flex-col gap-4 prose"
|
||||
>
|
||||
<div v-else-if="parseChangelogFailed" class="text-center flex flex-col gap-4 prose">
|
||||
<h2 class="text-lg text-unraid-red italic font-semibold">
|
||||
{{ props.t(`Error Parsing Changelog • {0}`, [parseChangelogFailed]) }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ props.t(`It's highly recommended to review the changelog before continuing your update`) }}
|
||||
</p>
|
||||
<div
|
||||
v-if="releaseForUpdate?.changelogPretty"
|
||||
class="flex self-center"
|
||||
>
|
||||
<div v-if="releaseForUpdate?.changelogPretty" class="flex self-center">
|
||||
<BrandButton
|
||||
:href="releaseForUpdate?.changelogPretty"
|
||||
btn-style="underline"
|
||||
variant="underline"
|
||||
:external="true"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
>
|
||||
{{ props.t("View Changelog on Docs") }}
|
||||
{{ props.t('View Changelog on Docs') }}
|
||||
</BrandButton>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,7 +85,7 @@ const showExtendKeyButton = computed(() => {
|
||||
class="text-center flex flex-col justify-center w-full min-h-[250px] min-w-[280px] sm:min-w-[400px]"
|
||||
>
|
||||
<BrandLoading class="w-[150px] mx-auto mt-24px" />
|
||||
<p>{{ props.t("Fetching & parsing changelog…") }}</p>
|
||||
<p>{{ props.t('Fetching & parsing changelog…') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -100,31 +93,31 @@ const showExtendKeyButton = computed(() => {
|
||||
<div class="flex flex-col-reverse xs:flex-row justify-between gap-12px md:gap-16px">
|
||||
<div class="flex flex-col-reverse xs:flex-row xs:justify-start gap-12px md:gap-16px">
|
||||
<BrandButton
|
||||
btn-style="underline-hover-red"
|
||||
variant="underline"
|
||||
:icon="XMarkIcon"
|
||||
@click="updateOsChangelogStore.setReleaseForUpdate(null)"
|
||||
>
|
||||
{{ props.t("Close") }}
|
||||
{{ props.t('Close') }}
|
||||
</BrandButton>
|
||||
<BrandButton
|
||||
v-if="releaseForUpdate?.changelogPretty"
|
||||
btn-style="underline"
|
||||
variant="underline"
|
||||
:external="true"
|
||||
:href="releaseForUpdate?.changelogPretty"
|
||||
:icon="EyeIcon"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
>
|
||||
{{ props.t("View on Docs") }}
|
||||
{{ props.t('View on Docs') }}
|
||||
</BrandButton>
|
||||
</div>
|
||||
<BrandButton
|
||||
v-if="showExtendKeyButton"
|
||||
btn-style="fill"
|
||||
variant="fill"
|
||||
:icon="KeyIcon"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
@click="purchaseStore.renew()"
|
||||
>
|
||||
{{ props.t("Extend License to Update") }}
|
||||
{{ props.t('Extend License to Update') }}
|
||||
</BrandButton>
|
||||
<BrandButton
|
||||
v-else-if="releaseForUpdate?.sha256"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import {
|
||||
ArrowTopRightOnSquareIcon,
|
||||
CogIcon,
|
||||
@@ -7,8 +9,10 @@ import {
|
||||
KeyIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, BrandLoading } from '@unraid/ui';
|
||||
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import type { BrandButtonProps } from '@unraid/ui';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
@@ -17,7 +21,6 @@ import { usePurchaseStore } from '~/store/purchase';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUpdateOsStore } from '~/store/updateOs';
|
||||
import { useUpdateOsChangelogStore } from '~/store/updateOsChangelog';
|
||||
import type { ButtonProps } from '~/types/ui/button';
|
||||
|
||||
export interface Props {
|
||||
open?: boolean;
|
||||
@@ -55,10 +58,18 @@ const {
|
||||
* So we need to watch for this value to be able to format it based on the user's date time preferences.
|
||||
*/
|
||||
const formattedRegExp = ref<string>();
|
||||
const setFormattedRegExp = () => { // ran in watch on regExp and onBeforeMount
|
||||
if (!regExp.value) { return; }
|
||||
const setFormattedRegExp = () => {
|
||||
// ran in watch on regExp and onBeforeMount
|
||||
if (!regExp.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { outputDateTimeFormatted } = useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
||||
const { outputDateTimeFormatted } = useDateTimeHelper(
|
||||
dateTimeFormat.value,
|
||||
props.t,
|
||||
true,
|
||||
regExp.value
|
||||
);
|
||||
formattedRegExp.value = outputDateTimeFormatted.value;
|
||||
};
|
||||
watch(regExp, (_newV) => {
|
||||
@@ -75,7 +86,9 @@ watch(updateOsIgnoredReleases, (newVal, oldVal) => {
|
||||
|
||||
const notificationsSettings = computed(() => {
|
||||
return !updateOsNotificationsEnabled.value
|
||||
? props.t('Go to Settings > Notifications to enable automatic OS update notifications for future releases.')
|
||||
? props.t(
|
||||
'Go to Settings > Notifications to enable automatic OS update notifications for future releases.'
|
||||
)
|
||||
: undefined;
|
||||
});
|
||||
|
||||
@@ -111,7 +124,9 @@ const modalCopy = computed((): ModalCopy | null => {
|
||||
: undefined;
|
||||
return {
|
||||
title: props.t('Unraid OS {0} Update Available', [available.value]),
|
||||
description: description ? `<p>${formattedReleaseDate}</p><p>${description}</p>` : formattedReleaseDate,
|
||||
description: description
|
||||
? `<p>${formattedReleaseDate}</p><p>${description}</p>`
|
||||
: formattedReleaseDate,
|
||||
};
|
||||
} else if (!available.value && !availableWithRenewal.value) {
|
||||
return {
|
||||
@@ -126,12 +141,12 @@ const showNotificationsSettingsLink = computed(() => {
|
||||
return !updateOsNotificationsEnabled.value && !available.value && !availableWithRenewal.value;
|
||||
});
|
||||
|
||||
const extraLinks = computed((): ButtonProps[] => {
|
||||
const buttons: ButtonProps[] = [];
|
||||
const extraLinks = computed((): BrandButtonProps[] => {
|
||||
const buttons: BrandButtonProps[] = [];
|
||||
|
||||
if (showNotificationsSettingsLink.value) {
|
||||
buttons.push({
|
||||
btnStyle: 'outline',
|
||||
variant: 'outline',
|
||||
href: '/Settings/Notifications',
|
||||
icon: CogIcon,
|
||||
text: props.t('Enable update notifications'),
|
||||
@@ -141,11 +156,13 @@ const extraLinks = computed((): ButtonProps[] => {
|
||||
return buttons;
|
||||
});
|
||||
|
||||
const actionButtons = computed((): ButtonProps[] | null => {
|
||||
const actionButtons = computed((): BrandButtonProps[] | null => {
|
||||
// update not available or no action buttons default closing
|
||||
if (!available.value || ignoreThisRelease.value) { return null; }
|
||||
if (!available.value || ignoreThisRelease.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const buttons: ButtonProps[] = [];
|
||||
const buttons: BrandButtonProps[] = [];
|
||||
|
||||
// update available but not stable branch - should link out to account update callback
|
||||
// if availableWithRenewal.value is true, then we need to renew the license before we can update so don't show the verify button
|
||||
@@ -162,10 +179,9 @@ const actionButtons = computed((): ButtonProps[] | null => {
|
||||
// update available - open changelog to commence update
|
||||
if (available.value && updateOsResponse.value?.changelog) {
|
||||
buttons.push({
|
||||
btnStyle: availableWithRenewal.value
|
||||
? 'outline'
|
||||
: undefined,
|
||||
click: async () => await updateOsChangelogStore.setReleaseForUpdate(updateOsResponse.value ?? null),
|
||||
variant: availableWithRenewal.value ? 'outline' : undefined,
|
||||
click: async () =>
|
||||
await updateOsChangelogStore.setReleaseForUpdate(updateOsResponse.value ?? null),
|
||||
icon: EyeIcon,
|
||||
text: availableWithRenewal.value
|
||||
? props.t('View Changelog')
|
||||
@@ -200,7 +216,13 @@ const close = () => {
|
||||
};
|
||||
|
||||
const renderMainSlot = computed(() => {
|
||||
return !!(checkForUpdatesLoading.value || available.value || availableWithRenewal.value || extraLinks.value?.length > 0 || updateOsIgnoredReleases.value.length > 0);
|
||||
return !!(
|
||||
checkForUpdatesLoading.value ||
|
||||
available.value ||
|
||||
availableWithRenewal.value ||
|
||||
extraLinks.value?.length > 0 ||
|
||||
updateOsIgnoredReleases.value.length > 0
|
||||
);
|
||||
});
|
||||
|
||||
const userFormattedReleaseDate = ref<string>();
|
||||
@@ -209,9 +231,16 @@ const userFormattedReleaseDate = ref<string>();
|
||||
* So we need to watch for this value to be able to format it based on the user's date time preferences.
|
||||
*/
|
||||
const setUserFormattedReleaseDate = () => {
|
||||
if (!availableReleaseDate.value) { return; }
|
||||
if (!availableReleaseDate.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { outputDateTimeFormatted } = useDateTimeHelper(dateTimeFormat.value, props.t, true, availableReleaseDate.value.valueOf());
|
||||
const { outputDateTimeFormatted } = useDateTimeHelper(
|
||||
dateTimeFormat.value,
|
||||
props.t,
|
||||
true,
|
||||
availableReleaseDate.value.valueOf()
|
||||
);
|
||||
userFormattedReleaseDate.value = outputDateTimeFormatted.value;
|
||||
};
|
||||
watch(availableReleaseDate, (_newV) => {
|
||||
@@ -225,7 +254,8 @@ onBeforeMount(() => {
|
||||
});
|
||||
|
||||
const modalWidth = computed(() => {
|
||||
if (availableWithRenewal.value) { // wider since we'll have four buttons
|
||||
if (availableWithRenewal.value) {
|
||||
// wider since we'll have four buttons
|
||||
return 'max-w-800px';
|
||||
}
|
||||
return 'max-w-640px';
|
||||
@@ -249,7 +279,7 @@ const modalWidth = computed(() => {
|
||||
<BrandButton
|
||||
v-for="item in extraLinks"
|
||||
:key="item.text"
|
||||
:btn-style="item.btnStyle ?? undefined"
|
||||
:btn-style="item.variant ?? undefined"
|
||||
:href="item.href ?? undefined"
|
||||
:icon="item.icon"
|
||||
:icon-right="item.iconRight"
|
||||
@@ -265,10 +295,15 @@ const modalWidth = computed(() => {
|
||||
<div class="flex justify-center items-center gap-8px p-8px rounded">
|
||||
<Switch
|
||||
v-model="ignoreThisRelease"
|
||||
:class="ignoreThisRelease ? 'bg-gradient-to-r from-unraid-red to-orange' : 'bg-transparent'"
|
||||
:class="
|
||||
ignoreThisRelease ? 'bg-gradient-to-r from-unraid-red to-orange' : 'bg-transparent'
|
||||
"
|
||||
class="relative inline-flex h-24px w-[48px] items-center rounded-full overflow-hidden"
|
||||
>
|
||||
<span v-show="!ignoreThisRelease" class="absolute z-0 inset-0 opacity-10 bg-foreground" />
|
||||
<span
|
||||
v-show="!ignoreThisRelease"
|
||||
class="absolute z-0 inset-0 opacity-10 bg-foreground"
|
||||
/>
|
||||
<span
|
||||
:class="ignoreThisRelease ? 'translate-x-[26px]' : 'translate-x-[2px]'"
|
||||
class="inline-block h-20px w-20px transform rounded-full bg-white transition"
|
||||
@@ -280,7 +315,10 @@ const modalWidth = computed(() => {
|
||||
</div>
|
||||
</SwitchGroup>
|
||||
</div>
|
||||
<div v-else-if="updateOsIgnoredReleases.length > 0" class="w-full max-w-640px mx-auto flex flex-col gap-8px">
|
||||
<div
|
||||
v-else-if="updateOsIgnoredReleases.length > 0"
|
||||
class="w-full max-w-640px mx-auto flex flex-col gap-8px"
|
||||
>
|
||||
<h3 class="text-left text-16px font-semibold italic">
|
||||
{{ t('Ignored Releases') }}
|
||||
</h3>
|
||||
@@ -304,13 +342,13 @@ const modalWidth = computed(() => {
|
||||
>
|
||||
<div class="flex flex-col-reverse xs:flex-row justify-start gap-8px">
|
||||
<BrandButton
|
||||
btn-style="underline-hover-red"
|
||||
variant="underline-hover-red"
|
||||
:icon="XMarkIcon"
|
||||
:text="t('Close')"
|
||||
@click="close"
|
||||
/>
|
||||
<BrandButton
|
||||
btn-style="underline"
|
||||
variant="underline"
|
||||
:icon="ArrowTopRightOnSquareIcon"
|
||||
:text="t('More options')"
|
||||
@click="accountStore.updateOs()"
|
||||
@@ -320,7 +358,7 @@ const modalWidth = computed(() => {
|
||||
<BrandButton
|
||||
v-for="item in actionButtons"
|
||||
:key="item.text"
|
||||
:btn-style="item.btnStyle ?? undefined"
|
||||
:btn-style="item.variant ?? undefined"
|
||||
:icon="item.icon"
|
||||
:icon-right="item.iconRight"
|
||||
:icon-right-hover-display="item.iconRightHoverDisplay"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { XMarkIcon } from '@heroicons/vue/24/solid';
|
||||
import { BrandButton } from '@unraid/ui';
|
||||
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import { useServerStore } from '~/store/server';
|
||||
@@ -29,7 +31,7 @@ const evenBgColor = computed(() => {
|
||||
>
|
||||
<span class="font-semibold">{{ label }}</span>
|
||||
<BrandButton
|
||||
:btn-style="'underline'"
|
||||
variant="underline"
|
||||
:icon-right="XMarkIcon"
|
||||
:text="t('Remove')"
|
||||
:title="t('Remove from ignore list')"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { h } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
ArrowTopRightOnSquareIcon,
|
||||
@@ -8,17 +11,17 @@ import {
|
||||
InformationCircleIcon,
|
||||
XCircleIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { Badge, BrandButton } from '@unraid/ui';
|
||||
import BrandLoadingWhite from '~/components/Brand/LoadingWhite.vue';
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
import { Badge, BrandButton, BrandLoading } from '@unraid/ui';
|
||||
import { WEBGUI_TOOLS_REGISTRATION } from '~/helpers/urls';
|
||||
|
||||
import type { BrandButtonProps } from '@unraid/ui';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
import { useAccountStore } from '~/store/account';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUpdateOsStore } from '~/store/updateOs';
|
||||
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
|
||||
import type { ButtonProps } from '~/types/ui/button';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
export interface Props {
|
||||
downgradeNotAvailable?: boolean;
|
||||
@@ -41,6 +44,8 @@ const serverStore = useServerStore();
|
||||
const updateOsStore = useUpdateOsStore();
|
||||
const updateOsActionsStore = useUpdateOsActionsStore();
|
||||
|
||||
const LoadingIcon = () => h(BrandLoading, { variant: 'white' });
|
||||
|
||||
const { dateTimeFormat, osVersion, rebootType, rebootVersion, regExp, regUpdatesExpired } =
|
||||
storeToRefs(serverStore);
|
||||
const { available, availableWithRenewal } = storeToRefs(updateOsStore);
|
||||
@@ -69,10 +74,10 @@ const showRebootButton = computed(
|
||||
() => rebootType.value === 'downgrade' || rebootType.value === 'update'
|
||||
);
|
||||
|
||||
const checkButton = computed((): ButtonProps => {
|
||||
const checkButton = computed((): BrandButtonProps => {
|
||||
if (showRebootButton.value || props.showExternalDowngrade) {
|
||||
return {
|
||||
btnStyle: 'outline',
|
||||
variant: 'outline',
|
||||
click: () => {
|
||||
props.showExternalDowngrade ? accountStore.downgradeOs() : accountStore.updateOs();
|
||||
},
|
||||
@@ -83,7 +88,7 @@ const checkButton = computed((): ButtonProps => {
|
||||
|
||||
if (!updateAvailable.value) {
|
||||
return {
|
||||
btnStyle: 'outline',
|
||||
variant: 'outline',
|
||||
click: () => {
|
||||
updateOsStore.localCheckForUpdate();
|
||||
},
|
||||
@@ -93,7 +98,7 @@ const checkButton = computed((): ButtonProps => {
|
||||
}
|
||||
|
||||
return {
|
||||
btnStyle: 'fill',
|
||||
variant: 'fill',
|
||||
click: () => {
|
||||
updateOsStore.setModalOpen(true);
|
||||
},
|
||||
@@ -151,7 +156,7 @@ const checkButton = computed((): ButtonProps => {
|
||||
{{ t('Key ineligible for {0}', [availableWithRenewal]) }}
|
||||
</Badge>
|
||||
|
||||
<Badge v-if="status === 'checking'" variant="orange" :icon="BrandLoadingWhite">
|
||||
<Badge v-if="status === 'checking'" variant="orange" :icon="LoadingIcon">
|
||||
{{ t('Checking...') }}
|
||||
</Badge>
|
||||
<template v-else>
|
||||
@@ -194,7 +199,7 @@ const checkButton = computed((): ButtonProps => {
|
||||
|
||||
<span>
|
||||
<BrandButton
|
||||
:variant="checkButton.btnStyle"
|
||||
:variant="checkButton.variant"
|
||||
:icon="checkButton.icon"
|
||||
:text="checkButton.text"
|
||||
@click="checkButton.click"
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { ExclamationTriangleIcon } from '@heroicons/vue/24/solid';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import { ExclamationTriangleIcon } from '@heroicons/vue/24/solid';
|
||||
import { CardWrapper } from '@unraid/ui';
|
||||
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
|
||||
@@ -13,7 +16,7 @@ const { rebootTypeText } = storeToRefs(useUpdateOsActionsStore());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiCardWrapper :increased-padding="true">
|
||||
<CardWrapper :increased-padding="true">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-20px sm:gap-24px">
|
||||
<div class="grid gap-y-16px">
|
||||
<h3 class="text-20px font-semibold leading-normal flex flex-row items-center gap-8px">
|
||||
@@ -21,9 +24,15 @@ const { rebootTypeText } = storeToRefs(useUpdateOsActionsStore());
|
||||
{{ t(rebootTypeText) }}
|
||||
</h3>
|
||||
<div class="text-16px leading-relaxed opacity-75 whitespace-normal">
|
||||
<p>{{ t('During the Unraid OS update process third-party drivers were detected and are currently being updated in the background. Please wait for those to finish downloading before rebooting your server to complete the update process. You should receive a system notification when complete. You may also refresh this page to check for an updated status.') }}</p>
|
||||
<p>
|
||||
{{
|
||||
t(
|
||||
'During the Unraid OS update process third-party drivers were detected and are currently being updated in the background. Please wait for those to finish downloading before rebooting your server to complete the update process. You should receive a system notification when complete. You may also refresh this page to check for an updated status.'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</UiCardWrapper>
|
||||
</CardWrapper>
|
||||
</template>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
* @todo require keyfile to update
|
||||
* @todo require valid guid / server state to update
|
||||
*/
|
||||
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
|
||||
import { ref, watchEffect } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import {
|
||||
ArchiveBoxArrowDownIcon,
|
||||
ArrowPathIcon,
|
||||
@@ -12,19 +14,17 @@ import {
|
||||
BellAlertIcon,
|
||||
EyeIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, CardWrapper } from '@unraid/ui';
|
||||
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref, watchEffect } from 'vue';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUpdateOsStore } from '~/store/updateOs';
|
||||
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
const props = defineProps<{
|
||||
t: ComposerTranslation;
|
||||
@@ -39,9 +39,12 @@ const updateOsActionsStore = useUpdateOsActionsStore();
|
||||
const { connectPluginInstalled, flashBackupActivated } = storeToRefs(useServerStore());
|
||||
const { available } = storeToRefs(updateOsStore);
|
||||
|
||||
const {
|
||||
outputDateTimeFormatted: formattedReleaseDate,
|
||||
} = useDateTimeHelper(dateTimeFormat.value, props.t, true, dayjs(updateOsResponse.value?.date ?? '', 'YYYY-MM-DD').valueOf());
|
||||
const { outputDateTimeFormatted: formattedReleaseDate } = useDateTimeHelper(
|
||||
dateTimeFormat.value,
|
||||
props.t,
|
||||
true,
|
||||
dayjs(updateOsResponse.value?.date ?? '', 'YYYY-MM-DD').valueOf()
|
||||
);
|
||||
|
||||
const updateButton = ref<UserProfileLink | undefined>();
|
||||
|
||||
@@ -90,7 +93,12 @@ const startFlashBackup = () => {
|
||||
flashBackup();
|
||||
checkFlashBackupStatus();
|
||||
} else {
|
||||
alert(props.t('Flash Backup is not available. Navigate to {0}/Main/Settings/Flash to try again then come back to this page.', [window.location.origin]));
|
||||
alert(
|
||||
props.t(
|
||||
'Flash Backup is not available. Navigate to {0}/Main/Settings/Flash to try again then come back to this page.',
|
||||
[window.location.origin]
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
/**
|
||||
@@ -114,7 +122,9 @@ const checkFlashBackupStatus = () => {
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const disableCallbackButton = computed(() => !acknowledgeBackup.value || flashBackupBasicStatus.value === 'started');
|
||||
const disableCallbackButton = computed(
|
||||
() => !acknowledgeBackup.value || flashBackupBasicStatus.value === 'started'
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
if (available.value) {
|
||||
@@ -129,28 +139,29 @@ watchEffect(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiCardWrapper :increased-padding="true">
|
||||
<CardWrapper :increased-padding="true">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-20px sm:gap-24px">
|
||||
<div class="grid gap-y-16px">
|
||||
<h3
|
||||
class="font-semibold leading-normal flex flex-row items-start justify-start gap-8px"
|
||||
>
|
||||
<h3 class="font-semibold leading-normal flex flex-row items-start justify-start gap-8px">
|
||||
<component :is="headingIcon" class="w-20px shrink-0" />
|
||||
<span class="leading-none inline-flex flex-wrap justify-start items-baseline gap-8px">
|
||||
<span class="text-20px">
|
||||
{{ heading }}
|
||||
</span>
|
||||
<span
|
||||
v-if="updateOsResponse && formattedReleaseDate"
|
||||
class="text-16px opacity-75 shrink"
|
||||
>
|
||||
<span v-if="updateOsResponse && formattedReleaseDate" class="text-16px opacity-75 shrink">
|
||||
{{ formattedReleaseDate }}
|
||||
</span>
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<div class="prose opacity-75 text-16px leading-relaxed whitespace-normal">
|
||||
<p>{{ t('Receive the latest and greatest for Unraid OS. Whether it new features, security patches, or bug fixes – keeping your server up-to-date ensures the best experience that Unraid has to offer.') }}</p>
|
||||
<p>
|
||||
{{
|
||||
t(
|
||||
'Receive the latest and greatest for Unraid OS. Whether it new features, security patches, or bug fixes – keeping your server up-to-date ensures the best experience that Unraid has to offer.'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<p v-if="available">
|
||||
{{ flashBackupCopy }}
|
||||
</p>
|
||||
@@ -160,7 +171,7 @@ watchEffect(() => {
|
||||
<div class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
|
||||
<template v-if="available && updateButton">
|
||||
<BrandButton
|
||||
btn-style="outline"
|
||||
variant="outline"
|
||||
:disabled="flashBackupBasicStatus === 'started'"
|
||||
:icon="ArchiveBoxArrowDownIcon"
|
||||
:name="'flashBackup'"
|
||||
@@ -191,24 +202,36 @@ watchEffect(() => {
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
acknowledgeBackup ? 'opacity-0 duration-100 ease-out' : 'opacity-100 duration-200 ease-in',
|
||||
acknowledgeBackup
|
||||
? 'opacity-0 duration-100 ease-out'
|
||||
: 'opacity-100 duration-200 ease-in',
|
||||
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity',
|
||||
]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-12px w-12px text-gray-400" fill="none" viewBox="0 0 12 12">
|
||||
<path d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M4 8l2-2m0 0l2-2M6 6L4 4m2 2l2 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
:class="[
|
||||
acknowledgeBackup ? 'opacity-100 duration-200 ease-in' : 'opacity-0 duration-100 ease-out',
|
||||
acknowledgeBackup
|
||||
? 'opacity-100 duration-200 ease-in'
|
||||
: 'opacity-0 duration-100 ease-out',
|
||||
'absolute inset-0 flex h-full w-full items-center justify-center transition-opacity',
|
||||
]"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg class="h-12px w-12px text-green-500" fill="currentColor" viewBox="0 0 12 12">
|
||||
<path d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z" />
|
||||
<path
|
||||
d="M3.707 5.293a1 1 0 00-1.414 1.414l1.414-1.414zM5 8l-.707.707a1 1 0 001.414 0L5 8zm4.707-3.293a1 1 0 00-1.414-1.414l1.414 1.414zm-7.414 2l2 2 1.414-1.414-2-2-1.414 1.414zm3.414 2l4-4-1.414-1.414-4 4 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
@@ -221,24 +244,28 @@ watchEffect(() => {
|
||||
</template>
|
||||
|
||||
<BrandButton
|
||||
btn-style="fill"
|
||||
variant="fill"
|
||||
:disabled="disableCallbackButton"
|
||||
:external="updateButton?.external"
|
||||
:icon="EyeIcon"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
:name="updateButton?.name"
|
||||
:text="t('View Available Updates')"
|
||||
:title="!acknowledgeBackup ? t('Acklowledge that you have made a Flash Backup to enable this action') : ''"
|
||||
:title="
|
||||
!acknowledgeBackup
|
||||
? t('Acklowledge that you have made a Flash Backup to enable this action')
|
||||
: ''
|
||||
"
|
||||
class="flex-none"
|
||||
@click="updateButton?.click"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</UiCardWrapper>
|
||||
</CardWrapper>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../../assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ArrowTopRightOnSquareIcon,
|
||||
ExclamationTriangleIcon,
|
||||
EyeIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import dayjs from 'dayjs';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref, watchEffect } from 'vue';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
import { ArrowTopRightOnSquareIcon, ExclamationTriangleIcon, EyeIcon } from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, CardWrapper } from '@unraid/ui';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUpdateOsStore } from '~/store/updateOs';
|
||||
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
const props = defineProps<{
|
||||
t: ComposerTranslation;
|
||||
@@ -30,8 +26,15 @@ const { dateTimeFormat, regTy, renewAction, updateOsResponse } = storeToRefs(ser
|
||||
const { availableWithRenewal } = storeToRefs(updateOsStore);
|
||||
const { ineligibleText } = storeToRefs(updateOsActionsStore);
|
||||
|
||||
const availableWithRenewalRelease = computed(() => availableWithRenewal.value ? updateOsResponse.value : undefined);
|
||||
const { outputDateTimeFormatted: formattedReleaseDate } = useDateTimeHelper(dateTimeFormat.value, props.t, true, dayjs(availableWithRenewalRelease.value?.date, 'YYYY-MM-DD').valueOf());
|
||||
const availableWithRenewalRelease = computed(() =>
|
||||
availableWithRenewal.value ? updateOsResponse.value : undefined
|
||||
);
|
||||
const { outputDateTimeFormatted: formattedReleaseDate } = useDateTimeHelper(
|
||||
dateTimeFormat.value,
|
||||
props.t,
|
||||
true,
|
||||
dayjs(availableWithRenewalRelease.value?.date, 'YYYY-MM-DD').valueOf()
|
||||
);
|
||||
|
||||
const heading = computed((): string => {
|
||||
if (availableWithRenewal.value) {
|
||||
@@ -56,19 +59,19 @@ watchEffect(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiCardWrapper :increased-padding="true" :warning="true">
|
||||
<CardWrapper :increased-padding="true" :warning="true">
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-20px sm:gap-24px">
|
||||
<div class="grid gap-y-16px">
|
||||
<h3
|
||||
class="font-semibold leading-normal flex flex-row items-start justify-start gap-8px"
|
||||
>
|
||||
<h3 class="font-semibold leading-normal flex flex-row items-start justify-start gap-8px">
|
||||
<ExclamationTriangleIcon class="w-20px shrink-0" />
|
||||
<span class="leading-none inline-flex flex-wrap justify-start items-baseline gap-8px">
|
||||
<span class="text-20px">
|
||||
{{ heading }}
|
||||
</span>
|
||||
<span
|
||||
v-if="availableWithRenewalRelease && availableWithRenewalRelease.date && formattedReleaseDate"
|
||||
v-if="
|
||||
availableWithRenewalRelease && availableWithRenewalRelease.date && formattedReleaseDate
|
||||
"
|
||||
class="text-16px opacity-75 shrink"
|
||||
>
|
||||
{{ formattedReleaseDate }}
|
||||
@@ -80,10 +83,7 @@ watchEffect(() => {
|
||||
<RegistrationUpdateExpiration :t="t" />
|
||||
</h4>
|
||||
|
||||
<div
|
||||
class="prose text-black text-16px leading-relaxed whitespace-normal"
|
||||
v-html="text"
|
||||
/>
|
||||
<div class="prose text-black text-16px leading-relaxed whitespace-normal" v-html="text" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-shrink-0 items-center gap-16px">
|
||||
@@ -98,7 +98,7 @@ watchEffect(() => {
|
||||
@click="renewAction.click?.()"
|
||||
/>
|
||||
<!-- <BrandButton
|
||||
btn-style="black"
|
||||
variant="black"
|
||||
href="/Tools/Registration"
|
||||
:icon="WrenchScrewdriverIcon"
|
||||
:icon-right="ArrowSmallRightIcon"
|
||||
@@ -107,7 +107,7 @@ watchEffect(() => {
|
||||
|
||||
<BrandButton
|
||||
v-if="availableWithRenewal && updateButton"
|
||||
btn-style="outline-black"
|
||||
variant="outline-black"
|
||||
:external="updateButton?.external"
|
||||
:icon="EyeIcon"
|
||||
:icon-right="ArrowTopRightOnSquareIcon"
|
||||
@@ -118,11 +118,11 @@ watchEffect(() => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</UiCardWrapper>
|
||||
</CardWrapper>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../../assets/main.css';
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user