mirror of
https://github.com/unraid/api.git
synced 2026-01-05 16:09:49 -06:00
refactor: unraid ui cleanup and migration (#998)
Co-authored-by: Eli Bosley <ekbosley@gmail.com> Co-authored-by: Pujit Mehrotra <pujit@lime-technology.com> Co-authored-by: mdatelle <mike@datelle.net> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zack Spear <zackspear@users.noreply.github.com>
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
// eslint-disable vue/no-v-html
|
||||
import { BrandButton } from '@unraid/ui';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useServerStore } from '~/store/server';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const serverStore = useServerStore();
|
||||
@@ -33,7 +30,7 @@ const { authAction, stateData } = storeToRefs(serverStore);
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -15,15 +15,12 @@ else
|
||||
echo "Third party plugins found - PLEASE CHECK YOUR UNRAID NOTIFICATIONS AND WAIT FOR THE MESSAGE THAT IT IS SAFE TO REBOOT!"
|
||||
fi
|
||||
*/
|
||||
import { PageContainer } from '@unraid/ui';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onBeforeMount } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useServerStore } from '~/store/server';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
export interface Props {
|
||||
@@ -56,7 +53,7 @@ onBeforeMount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiPageContainer>
|
||||
<PageContainer>
|
||||
<UpdateOsStatus
|
||||
:title="t('Downgrade Unraid OS')"
|
||||
:subtitle="subtitle"
|
||||
@@ -70,15 +67,12 @@ onBeforeMount(() => {
|
||||
:version="restoreVersion"
|
||||
:t="t"
|
||||
/>
|
||||
<UpdateOsThirdPartyDrivers
|
||||
v-if="rebootType === 'thirdPartyDriversDownloading'"
|
||||
:t="t"
|
||||
/>
|
||||
</UiPageContainer>
|
||||
<UpdateOsThirdPartyDrivers v-if="rebootType === 'thirdPartyDriversDownloading'" :t="t" />
|
||||
</PageContainer>
|
||||
</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,12 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ArrowDownTrayIcon, ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { BrandButton } from '@unraid/ui';
|
||||
import { CONNECT_FORUMS, CONTACT, DISCORD, WEBGUI_GRAPHQL } from '~/helpers/urls';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -19,7 +17,11 @@ const downloadUrl = computed(() => new URL(`/graphql/api/logs?apiKey=${apiKey.va
|
||||
<div class="whitespace-normal flex flex-col gap-y-16px max-w-3xl">
|
||||
<span>
|
||||
{{ t('The primary method of support for Unraid Connect is through our forums and Discord.') }}
|
||||
{{ t('If you are asked to supply logs, please open a support request on our Contact Page and reply to the email message you receive with your logs attached.') }}
|
||||
{{
|
||||
t(
|
||||
'If you are asked to supply logs, please open a support request on our Contact Page and reply to the email message you receive with your logs attached.'
|
||||
)
|
||||
}}
|
||||
{{ t('The logs may contain sensitive information so do not post them publicly.') }}
|
||||
</span>
|
||||
<span class="flex flex-col gap-y-16px">
|
||||
@@ -36,15 +38,30 @@ const downloadUrl = computed(() => new URL(`/graphql/api/logs?apiKey=${apiKey.va
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-baseline gap-8px">
|
||||
<a :href="CONNECT_FORUMS.toString()" target="_blank" rel="noopener noreferrer" class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px">
|
||||
<a
|
||||
:href="CONNECT_FORUMS.toString()"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px"
|
||||
>
|
||||
{{ t('Unraid Connect Forums') }}
|
||||
<ArrowTopRightOnSquareIcon class="w-16px" />
|
||||
</a>
|
||||
<a :href="DISCORD.toString()" target="_blank" rel="noopener noreferrer" class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px">
|
||||
<a
|
||||
:href="DISCORD.toString()"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px"
|
||||
>
|
||||
{{ t('Unraid Discord') }}
|
||||
<ArrowTopRightOnSquareIcon class="w-16px" />
|
||||
</a>
|
||||
<a :href="CONTACT.toString()" target="_blank" rel="noopener noreferrer" class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px">
|
||||
<a
|
||||
:href="CONTACT.toString()"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-[#486dba] hover:text-[#3b5ea9] focus:text-[#3b5ea9] hover:underline focus:underline inline-flex flex-row items-center justify-start gap-8px"
|
||||
>
|
||||
{{ t('Unraid Contact Page') }}
|
||||
<ArrowTopRightOnSquareIcon class="w-16px" />
|
||||
</a>
|
||||
@@ -54,7 +71,7 @@ const downloadUrl = computed(() => new URL(`/graphql/api/logs?apiKey=${apiKey.va
|
||||
</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,21 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
BellAlertIcon,
|
||||
ExclamationTriangleIcon,
|
||||
InformationCircleIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
import { BellAlertIcon, ExclamationTriangleIcon, InformationCircleIcon } from '@heroicons/vue/24/solid';
|
||||
import { Badge } from '@unraid/ui';
|
||||
import { getReleaseNotesUrl, WEBGUI_TOOLS_DOWNGRADE, WEBGUI_TOOLS_UPDATE } from '~/helpers/urls';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUpdateOsStore } from '~/store/updateOs';
|
||||
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
import type { UiBadgeProps, UiBadgePropsColor } from '~/types/ui/badge';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -27,23 +18,22 @@ const { osVersion, rebootType, stateDataError } = storeToRefs(serverStore);
|
||||
const { available, availableWithRenewal } = storeToRefs(updateOsStore);
|
||||
const { rebootTypeText } = storeToRefs(updateOsActionsStore);
|
||||
|
||||
export interface UpdateOsStatus extends UserProfileLink {
|
||||
badge: UiBadgeProps;
|
||||
}
|
||||
const updateOsStatus = computed(() => {
|
||||
if (stateDataError.value) { // only allowed to update when server is does not have a state error
|
||||
if (stateDataError.value) {
|
||||
// only allowed to update when server is does not have a state error
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rebootTypeText.value) {
|
||||
return {
|
||||
badge: {
|
||||
color: 'yellow' as UiBadgePropsColor,
|
||||
color: 'yellow',
|
||||
icon: ExclamationTriangleIcon,
|
||||
},
|
||||
href: rebootType.value === 'downgrade'
|
||||
? WEBGUI_TOOLS_DOWNGRADE.toString()
|
||||
: WEBGUI_TOOLS_UPDATE.toString(),
|
||||
href:
|
||||
rebootType.value === 'downgrade'
|
||||
? WEBGUI_TOOLS_DOWNGRADE.toString()
|
||||
: WEBGUI_TOOLS_UPDATE.toString(),
|
||||
text: t(rebootTypeText.value),
|
||||
};
|
||||
}
|
||||
@@ -51,13 +41,13 @@ const updateOsStatus = computed(() => {
|
||||
if (availableWithRenewal.value || available.value) {
|
||||
return {
|
||||
badge: {
|
||||
color: 'orange' as UiBadgePropsColor,
|
||||
color: 'orange',
|
||||
icon: BellAlertIcon,
|
||||
},
|
||||
click: () => { updateOsStore.setModalOpen(true); },
|
||||
text: availableWithRenewal.value
|
||||
? t('Update Released')
|
||||
: t('Update Available'),
|
||||
click: () => {
|
||||
updateOsStore.setModalOpen(true);
|
||||
},
|
||||
text: availableWithRenewal.value ? t('Update Released') : t('Update Available'),
|
||||
title: availableWithRenewal.value
|
||||
? t('Unraid OS {0} Released', [availableWithRenewal.value])
|
||||
: t('Unraid OS {0} Update Available', [available.value]),
|
||||
@@ -77,15 +67,15 @@ const updateOsStatus = computed(() => {
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<UiBadge
|
||||
color="custom"
|
||||
<Badge
|
||||
variant="custom"
|
||||
:icon="InformationCircleIcon"
|
||||
icon-styles="text-header-text-secondary"
|
||||
size="14px"
|
||||
size="sm"
|
||||
class="text-header-text-secondary group-hover:text-orange-dark group-focus:text-orange-dark group-hover:underline group-focus:underline"
|
||||
>
|
||||
{{ osVersion }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
</a>
|
||||
<component
|
||||
:is="updateOsStatus.href ? 'a' : 'button'"
|
||||
@@ -95,14 +85,14 @@ const updateOsStatus = computed(() => {
|
||||
class="group"
|
||||
@click="updateOsStatus.click?.()"
|
||||
>
|
||||
<UiBadge
|
||||
<Badge
|
||||
v-if="updateOsStatus.badge"
|
||||
:color="updateOsStatus.badge.color"
|
||||
:icon="updateOsStatus.badge.icon"
|
||||
size="12px"
|
||||
size="xs"
|
||||
>
|
||||
{{ updateOsStatus.text }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
<template v-else>
|
||||
{{ updateOsStatus.text }}
|
||||
</template>
|
||||
@@ -111,7 +101,6 @@ const updateOsStatus = computed(() => {
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
</style>
|
||||
|
||||
@@ -15,15 +15,12 @@ else
|
||||
echo "Third party plugins found - PLEASE CHECK YOUR UNRAID NOTIFICATIONS AND WAIT FOR THE MESSAGE THAT IT IS SAFE TO REBOOT!"
|
||||
fi
|
||||
*/
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { BrandLoading, PageContainer } from '@unraid/ui';
|
||||
import { WEBGUI_TOOLS_UPDATE } from '~/helpers/urls';
|
||||
import { useAccountStore } from '~/store/account';
|
||||
import { useServerStore } from '~/store/server';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -46,7 +43,9 @@ const subtitle = computed(() => {
|
||||
});
|
||||
|
||||
/** when we're not prompting for reboot /Tools/Update will automatically send the user to account.unraid.net/server/update-os */
|
||||
const showLoader = computed(() => window.location.pathname === WEBGUI_TOOLS_UPDATE.pathname && rebootType.value === '');
|
||||
const showLoader = computed(
|
||||
() => window.location.pathname === WEBGUI_TOOLS_UPDATE.pathname && rebootType.value === ''
|
||||
);
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (showLoader.value) {
|
||||
@@ -57,7 +56,7 @@ onBeforeMount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UiPageContainer>
|
||||
<PageContainer>
|
||||
<BrandLoading v-if="showLoader" class="mx-auto my-12 max-w-160px" />
|
||||
<UpdateOsStatus
|
||||
v-else
|
||||
@@ -66,16 +65,14 @@ onBeforeMount(() => {
|
||||
:subtitle="subtitle"
|
||||
:t="t"
|
||||
/>
|
||||
<UpdateOsThirdPartyDrivers
|
||||
v-if="rebootType === 'thirdPartyDriversDownloading'"
|
||||
:t="t"
|
||||
/>
|
||||
</UiPageContainer>
|
||||
<UpdateOsThirdPartyDrivers v-if="rebootType === 'thirdPartyDriversDownloading'" :t="t" />
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../assets/main.css';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
@@ -124,6 +121,4 @@ onBeforeMount(() => {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind utilities;
|
||||
</style>
|
||||
|
||||
@@ -6,19 +6,16 @@ import {
|
||||
InformationCircleIcon,
|
||||
LifebuoyIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import dayjs from 'dayjs';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
import { BrandButton, CardWrapper } from '@unraid/ui';
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
import { FORUMS_BUG_REPORT } from '~/helpers/urls';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
import dayjs from 'dayjs';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
t: ComposerTranslation;
|
||||
@@ -30,9 +27,12 @@ const serverStore = useServerStore();
|
||||
const updateOsActionsStore = useUpdateOsActionsStore();
|
||||
|
||||
const { dateTimeFormat } = storeToRefs(serverStore);
|
||||
const {
|
||||
outputDateTimeFormatted: formattedReleaseDate,
|
||||
} = useDateTimeHelper(dateTimeFormat.value, props.t, true, dayjs(props.releaseDate, 'YYYY-MM-DD').valueOf());
|
||||
const { outputDateTimeFormatted: formattedReleaseDate } = useDateTimeHelper(
|
||||
dateTimeFormat.value,
|
||||
props.t,
|
||||
true,
|
||||
dayjs(props.releaseDate, 'YYYY-MM-DD').valueOf()
|
||||
);
|
||||
|
||||
const diagnosticsButton = ref<UserProfileLink | undefined>({
|
||||
click: () => {
|
||||
@@ -55,12 +55,10 @@ const downgradeButton = ref<UserProfileLink>({
|
||||
</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">
|
||||
<ArrowUturnDownIcon class="w-20px shrink-0" />
|
||||
<span class="leading-none inline-flex flex-wrap justify-start items-baseline gap-8px">
|
||||
<span class="text-20px">
|
||||
@@ -76,28 +74,45 @@ const downgradeButton = ref<UserProfileLink>({
|
||||
</h3>
|
||||
<div class="prose text-16px leading-relaxed opacity-75 whitespace-normal">
|
||||
<p>{{ t(`Downgrades are only recommended if you're unable to solve a critical issue.`) }}</p>
|
||||
<p>{{ t('In the rare event you need to downgrade we ask that you please provide us with Diagnostics so we can investigate your issue.') }}</p>
|
||||
<p>{{ t('Download the Diagnostics zip then please open a bug report on our forums with a description of the issue along with your diagnostics.') }} </p>
|
||||
<p>
|
||||
{{
|
||||
t(
|
||||
'In the rare event you need to downgrade we ask that you please provide us with Diagnostics so we can investigate your issue.'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<p>
|
||||
{{
|
||||
t(
|
||||
'Download the Diagnostics zip then please open a bug report on our forums with a description of the issue along with your diagnostics.'
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="downgradeButton" class="flex flex-col flex-shrink-0 gap-16px flex-grow items-stretch">
|
||||
<BrandButton
|
||||
:btn-style="'underline'"
|
||||
:variant="'underline'"
|
||||
:icon="InformationCircleIcon"
|
||||
:text="t('{0} Release Notes', [version])"
|
||||
@click="updateOsActionsStore.viewReleaseNotes(t('{0} Release Notes', [version]), '/boot/previous/changes.txt')"
|
||||
@click="
|
||||
updateOsActionsStore.viewReleaseNotes(
|
||||
t('{0} Release Notes', [version]),
|
||||
'/boot/previous/changes.txt'
|
||||
)
|
||||
"
|
||||
/>
|
||||
<BrandButton
|
||||
v-if="diagnosticsButton"
|
||||
:btn-style="'gray'"
|
||||
:variant="'gray'"
|
||||
:icon="diagnosticsButton.icon"
|
||||
:name="diagnosticsButton.name"
|
||||
:text="diagnosticsButton.text"
|
||||
@click="diagnosticsButton.click"
|
||||
/>
|
||||
<BrandButton
|
||||
:btn-style="'gray'"
|
||||
:variant="'gray'"
|
||||
:external="true"
|
||||
:href="FORUMS_BUG_REPORT.toString()"
|
||||
:icon="LifebuoyIcon"
|
||||
@@ -113,11 +128,11 @@ const downgradeButton = ref<UserProfileLink>({
|
||||
/>
|
||||
</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>
|
||||
|
||||
@@ -8,19 +8,18 @@ import {
|
||||
InformationCircleIcon,
|
||||
XCircleIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
import { WEBGUI_TOOLS_REGISTRATION } from '~/helpers/urls';
|
||||
import { Badge, BrandButton } from '@unraid/ui';
|
||||
import BrandLoadingWhite from '~/components/Brand/LoadingWhite.vue';
|
||||
import useDateTimeHelper from '~/composables/dateTime';
|
||||
import { WEBGUI_TOOLS_REGISTRATION } from '~/helpers/urls';
|
||||
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';
|
||||
|
||||
import BrandLoadingWhite from '~/components/Brand/LoadingWhite.vue';
|
||||
|
||||
export interface Props {
|
||||
downgradeNotAvailable?: boolean;
|
||||
restoreVersion?: string | undefined;
|
||||
@@ -42,16 +41,15 @@ const serverStore = useServerStore();
|
||||
const updateOsStore = useUpdateOsStore();
|
||||
const updateOsActionsStore = useUpdateOsActionsStore();
|
||||
|
||||
const { dateTimeFormat, osVersion, rebootType, rebootVersion, regExp, regUpdatesExpired } = storeToRefs(serverStore);
|
||||
const { dateTimeFormat, osVersion, rebootType, rebootVersion, regExp, regUpdatesExpired } =
|
||||
storeToRefs(serverStore);
|
||||
const { available, availableWithRenewal } = storeToRefs(updateOsStore);
|
||||
const { ineligibleText, rebootTypeText, status } = storeToRefs(updateOsActionsStore);
|
||||
|
||||
const updateAvailable = computed(() => available.value || availableWithRenewal.value);
|
||||
|
||||
const {
|
||||
outputDateTimeReadableDiff: readableDiffRegExp,
|
||||
outputDateTimeFormatted: formattedRegExp,
|
||||
} = useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
||||
const { outputDateTimeReadableDiff: readableDiffRegExp, outputDateTimeFormatted: formattedRegExp } =
|
||||
useDateTimeHelper(dateTimeFormat.value, props.t, true, regExp.value);
|
||||
|
||||
const regExpOutput = computed(() => {
|
||||
if (!regExp.value) {
|
||||
@@ -67,16 +65,16 @@ const regExpOutput = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const showRebootButton = computed(() => rebootType.value === 'downgrade' || rebootType.value === 'update');
|
||||
const showRebootButton = computed(
|
||||
() => rebootType.value === 'downgrade' || rebootType.value === 'update'
|
||||
);
|
||||
|
||||
const checkButton = computed((): ButtonProps => {
|
||||
if (showRebootButton.value || props.showExternalDowngrade) {
|
||||
return {
|
||||
btnStyle: 'outline',
|
||||
click: () => {
|
||||
props.showExternalDowngrade
|
||||
? accountStore.downgradeOs()
|
||||
: accountStore.updateOs();
|
||||
props.showExternalDowngrade ? accountStore.downgradeOs() : accountStore.updateOs();
|
||||
},
|
||||
icon: ArrowTopRightOnSquareIcon,
|
||||
text: props.t('More options'),
|
||||
@@ -124,9 +122,9 @@ const checkButton = computed((): ButtonProps => {
|
||||
:title="t('View release notes')"
|
||||
@click="updateOsActionsStore.viewReleaseNotes(t('{0} Release Notes', [osVersion]))"
|
||||
>
|
||||
<UiBadge :icon="InformationCircleIcon" class="underline">
|
||||
<Badge :icon="InformationCircleIcon" variant="gray" size="md">
|
||||
{{ t('Current Version {0}', [osVersion]) }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
</button>
|
||||
|
||||
<a
|
||||
@@ -135,75 +133,68 @@ const checkButton = computed((): ButtonProps => {
|
||||
class="group"
|
||||
:title="t('Learn more and fix')"
|
||||
>
|
||||
<UiBadge
|
||||
:color="'yellow'"
|
||||
<Badge
|
||||
variant="yellow"
|
||||
:icon="ExclamationTriangleIcon"
|
||||
:title="regExpOutput?.text"
|
||||
class="underline"
|
||||
>
|
||||
{{ t('Key ineligible for future releases') }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
</a>
|
||||
<UiBadge
|
||||
<Badge
|
||||
v-else-if="ineligibleText && availableWithRenewal"
|
||||
:color="'yellow'"
|
||||
variant="yellow"
|
||||
:icon="ExclamationTriangleIcon"
|
||||
:title="regExpOutput?.text"
|
||||
>
|
||||
{{ t('Key ineligible for {0}', [availableWithRenewal]) }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
|
||||
<UiBadge
|
||||
v-if="status === 'checking'"
|
||||
:color="'orange'"
|
||||
:icon="BrandLoadingWhite"
|
||||
>
|
||||
<Badge v-if="status === 'checking'" variant="orange" :icon="BrandLoadingWhite">
|
||||
{{ t('Checking...') }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
<template v-else>
|
||||
<UiBadge
|
||||
<Badge
|
||||
v-if="rebootType === ''"
|
||||
:color="updateAvailable ? 'orange' : 'green'"
|
||||
:variant="updateAvailable ? 'orange' : 'green'"
|
||||
:icon="updateAvailable ? BellAlertIcon : CheckCircleIcon"
|
||||
>
|
||||
{{ (available
|
||||
? t('Unraid {0} Available', [available])
|
||||
: (availableWithRenewal
|
||||
? t('Up-to-date with eligible releases')
|
||||
: t('Up-to-date')))
|
||||
{{
|
||||
available
|
||||
? t('Unraid {0} Available', [available])
|
||||
: availableWithRenewal
|
||||
? t('Up-to-date with eligible releases')
|
||||
: t('Up-to-date')
|
||||
}}
|
||||
</UiBadge>
|
||||
<UiBadge
|
||||
v-else
|
||||
:color="'yellow'"
|
||||
:icon="ExclamationTriangleIcon"
|
||||
>
|
||||
</Badge>
|
||||
<Badge v-else variant="yellow" :icon="ExclamationTriangleIcon">
|
||||
{{ t(rebootTypeText) }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
</template>
|
||||
|
||||
<UiBadge
|
||||
v-if="downgradeNotAvailable"
|
||||
:color="'gray'"
|
||||
:icon="XCircleIcon"
|
||||
>
|
||||
<Badge v-if="downgradeNotAvailable" variant="gray" :icon="XCircleIcon">
|
||||
{{ t('No downgrade available') }}
|
||||
</UiBadge>
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div class="inline-flex flex-col flex-shrink-0 gap-16px flex-grow items-center md:items-end">
|
||||
<span v-if="showRebootButton">
|
||||
<BrandButton
|
||||
btn-style="fill"
|
||||
variant="fill"
|
||||
:icon="ArrowPathIcon"
|
||||
:text="rebootType === 'downgrade' ? t('Reboot Now to Downgrade to {0}', [rebootVersion]) : t('Reboot Now to Update to {0}', [rebootVersion])"
|
||||
:text="
|
||||
rebootType === 'downgrade'
|
||||
? t('Reboot Now to Downgrade to {0}', [rebootVersion])
|
||||
: t('Reboot Now to Update to {0}', [rebootVersion])
|
||||
"
|
||||
@click="updateOsActionsStore.rebootServer()"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<BrandButton
|
||||
:btn-style="checkButton.btnStyle"
|
||||
:variant="checkButton.btnStyle"
|
||||
:icon="checkButton.icon"
|
||||
:text="checkButton.text"
|
||||
@click="checkButton.click"
|
||||
@@ -212,7 +203,7 @@ const checkButton = computed((): ButtonProps => {
|
||||
|
||||
<span v-if="rebootType !== ''">
|
||||
<BrandButton
|
||||
btn-style="outline"
|
||||
variant="outline"
|
||||
:icon="XCircleIcon"
|
||||
:text="t('Cancel {0}', [rebootType === 'downgrade' ? t('Downgrade') : t('Update')])"
|
||||
@click="updateOsStore.cancelUpdate()"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<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 { useClipboard } from '@vueuse/core';
|
||||
import {
|
||||
CheckIcon,
|
||||
ChevronDoubleDownIcon,
|
||||
@@ -10,10 +9,7 @@ import {
|
||||
WrenchScrewdriverIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { WEBGUI_CONNECT_SETTINGS, WEBGUI_TOOLS_REGISTRATION } from '~/helpers/urls';
|
||||
import { useAccountStore } from '~/store/account';
|
||||
import { useCallbackActionsStore } from '~/store/callbackActions';
|
||||
@@ -21,6 +17,8 @@ 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;
|
||||
@@ -38,21 +36,10 @@ const installKeyStore = useInstallKeyStore();
|
||||
const serverStore = useServerStore();
|
||||
const updateOsActionStore = useUpdateOsActionsStore();
|
||||
|
||||
const {
|
||||
accountAction,
|
||||
accountActionHide,
|
||||
accountActionStatus,
|
||||
accountActionType,
|
||||
} = storeToRefs(accountStore);
|
||||
const {
|
||||
callbackStatus,
|
||||
} = storeToRefs(callbackActionsStore);
|
||||
const {
|
||||
keyActionType,
|
||||
keyUrl,
|
||||
keyInstallStatus,
|
||||
keyType,
|
||||
} = storeToRefs(installKeyStore);
|
||||
const { accountAction, accountActionHide, accountActionStatus, accountActionType } =
|
||||
storeToRefs(accountStore);
|
||||
const { callbackStatus } = storeToRefs(callbackActionsStore);
|
||||
const { keyActionType, keyUrl, keyInstallStatus, keyType } = storeToRefs(installKeyStore);
|
||||
const {
|
||||
connectPluginInstalled,
|
||||
refreshServerStateStatus,
|
||||
@@ -80,7 +67,9 @@ const isSettingsPage = ref<boolean>(document.location.pathname === '/Settings/Ma
|
||||
|
||||
const heading = computed(() => {
|
||||
if (updateOsStatus.value === 'confirming') {
|
||||
return callbackTypeDowngrade.value ? props.t('Downgrade Unraid OS confirmation required') : props.t('Update Unraid OS confirmation required');
|
||||
return callbackTypeDowngrade.value
|
||||
? props.t('Downgrade Unraid OS confirmation required')
|
||||
: props.t('Update Unraid OS confirmation required');
|
||||
}
|
||||
switch (callbackStatus.value) {
|
||||
case 'error':
|
||||
@@ -94,19 +83,37 @@ const heading = computed(() => {
|
||||
});
|
||||
const subheading = computed(() => {
|
||||
if (updateOsStatus.value === 'confirming') {
|
||||
return callbackTypeDowngrade.value ? props.t('Please confirm the downgrade details below') : props.t('Please confirm the update details below');
|
||||
return callbackTypeDowngrade.value
|
||||
? props.t('Please confirm the downgrade details below')
|
||||
: props.t('Please confirm the update details below');
|
||||
}
|
||||
if (callbackStatus.value === 'error') {
|
||||
return props.t('Something went wrong'); /** @todo show actual error messages */
|
||||
}
|
||||
if (callbackStatus.value === 'loading') { return props.t('Please keep this window open while we perform some actions'); }
|
||||
if (callbackStatus.value === 'loading') {
|
||||
return props.t('Please keep this window open while we perform some actions');
|
||||
}
|
||||
if (callbackStatus.value === 'success') {
|
||||
if (accountActionType.value === 'signIn') { return props.t('You\'re one step closer to enhancing your Unraid experience'); }
|
||||
if (keyActionType.value === 'purchase') { return props.t('Thank you for purchasing an Unraid {0} Key!', [keyType.value]); }
|
||||
if (keyActionType.value === 'replace') { return props.t('Your {0} Key has been replaced!', [keyType.value]); }
|
||||
if (keyActionType.value === 'trialExtend') { return props.t('Your Trial key has been extended!'); }
|
||||
if (keyActionType.value === 'trialStart') { return props.t('Your free Trial key provides all the functionality of an Unleashed Registration key'); }
|
||||
if (keyActionType.value === 'upgrade') { return props.t('Thank you for upgrading to an Unraid {0} Key!', [keyType.value]); }
|
||||
if (accountActionType.value === 'signIn') {
|
||||
return props.t("You're one step closer to enhancing your Unraid experience");
|
||||
}
|
||||
if (keyActionType.value === 'purchase') {
|
||||
return props.t('Thank you for purchasing an Unraid {0} Key!', [keyType.value]);
|
||||
}
|
||||
if (keyActionType.value === 'replace') {
|
||||
return props.t('Your {0} Key has been replaced!', [keyType.value]);
|
||||
}
|
||||
if (keyActionType.value === 'trialExtend') {
|
||||
return props.t('Your Trial key has been extended!');
|
||||
}
|
||||
if (keyActionType.value === 'trialStart') {
|
||||
return props.t(
|
||||
'Your free Trial key provides all the functionality of an Unleashed Registration key'
|
||||
);
|
||||
}
|
||||
if (keyActionType.value === 'upgrade') {
|
||||
return props.t('Thank you for upgrading to an Unraid {0} Key!', [keyType.value]);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
@@ -137,30 +144,50 @@ const cancelUpdateOs = () => {
|
||||
// close();
|
||||
// };
|
||||
|
||||
const keyInstallStatusCopy = computed((): { text: string; } => {
|
||||
const keyInstallStatusCopy = computed((): { text: string } => {
|
||||
let txt1 = props.t('Installing');
|
||||
let txt2 = props.t('Installed');
|
||||
let txt3 = props.t('Install');
|
||||
switch (keyInstallStatus.value) {
|
||||
case 'installing':
|
||||
if (keyActionType.value === 'trialExtend') { txt1 = props.t('Installing Extended Trial'); }
|
||||
if (keyActionType.value === 'recover') { txt1 = props.t('Installing Recovered'); }
|
||||
if (keyActionType.value === 'renew') { txt1 = props.t('Installing Extended'); }
|
||||
if (keyActionType.value === 'replace') { txt1 = props.t('Installing Replaced'); }
|
||||
if (keyActionType.value === 'trialExtend') {
|
||||
txt1 = props.t('Installing Extended Trial');
|
||||
}
|
||||
if (keyActionType.value === 'recover') {
|
||||
txt1 = props.t('Installing Recovered');
|
||||
}
|
||||
if (keyActionType.value === 'renew') {
|
||||
txt1 = props.t('Installing Extended');
|
||||
}
|
||||
if (keyActionType.value === 'replace') {
|
||||
txt1 = props.t('Installing Replaced');
|
||||
}
|
||||
return {
|
||||
text: props.t('{0} {1} Key…', [txt1, keyType.value]),
|
||||
};
|
||||
case 'success':
|
||||
if (keyActionType.value === 'renew' || keyActionType.value === 'trialExtend') { txt2 = props.t('Extension Installed'); }
|
||||
if (keyActionType.value === 'recover') { txt2 = props.t('Recovered'); }
|
||||
if (keyActionType.value === 'replace') { txt2 = props.t('Replaced'); }
|
||||
if (keyActionType.value === 'renew' || keyActionType.value === 'trialExtend') {
|
||||
txt2 = props.t('Extension Installed');
|
||||
}
|
||||
if (keyActionType.value === 'recover') {
|
||||
txt2 = props.t('Recovered');
|
||||
}
|
||||
if (keyActionType.value === 'replace') {
|
||||
txt2 = props.t('Replaced');
|
||||
}
|
||||
return {
|
||||
text: props.t('{1} Key {0} Successfully', [txt2, keyType.value]),
|
||||
};
|
||||
case 'failed':
|
||||
if (keyActionType.value === 'trialExtend') { txt3 = props.t('Install Extended'); }
|
||||
if (keyActionType.value === 'recover') { txt3 = props.t('Install Recovered'); }
|
||||
if (keyActionType.value === 'replace') { txt3 = props.t('Install Replaced'); }
|
||||
if (keyActionType.value === 'trialExtend') {
|
||||
txt3 = props.t('Install Extended');
|
||||
}
|
||||
if (keyActionType.value === 'recover') {
|
||||
txt3 = props.t('Install Recovered');
|
||||
}
|
||||
if (keyActionType.value === 'replace') {
|
||||
txt3 = props.t('Install Replaced');
|
||||
}
|
||||
return {
|
||||
text: props.t('Failed to {0} {1} Key', [txt3, keyType.value]),
|
||||
};
|
||||
@@ -172,31 +199,32 @@ const keyInstallStatusCopy = computed((): { text: string; } => {
|
||||
}
|
||||
});
|
||||
|
||||
const accountActionStatusCopy = computed((): { text: string; } => {
|
||||
const accountActionStatusCopy = computed((): { text: string } => {
|
||||
switch (accountActionStatus.value) {
|
||||
case 'waiting':
|
||||
return {
|
||||
text: accountAction.value?.type === 'signIn'
|
||||
? props.t('Signing In')
|
||||
: props.t('Signing Out'),
|
||||
text: accountAction.value?.type === 'signIn' ? props.t('Signing In') : props.t('Signing Out'),
|
||||
};
|
||||
case 'updating':
|
||||
return {
|
||||
text: accountAction.value?.type === 'signIn'
|
||||
? props.t('Signing in {0}…', [accountAction.value.user?.preferred_username])
|
||||
: props.t('Signing out {0}…', [username.value]),
|
||||
text:
|
||||
accountAction.value?.type === 'signIn'
|
||||
? props.t('Signing in {0}…', [accountAction.value.user?.preferred_username])
|
||||
: props.t('Signing out {0}…', [username.value]),
|
||||
};
|
||||
case 'success':
|
||||
return {
|
||||
text: accountAction.value?.type === 'signIn'
|
||||
? props.t('{0} Signed In Successfully', [accountAction.value.user?.preferred_username])
|
||||
: props.t('{0} Signed Out Successfully', [username.value]),
|
||||
text:
|
||||
accountAction.value?.type === 'signIn'
|
||||
? props.t('{0} Signed In Successfully', [accountAction.value.user?.preferred_username])
|
||||
: props.t('{0} Signed Out Successfully', [username.value]),
|
||||
};
|
||||
case 'failed':
|
||||
return {
|
||||
text: accountAction.value?.type === 'signIn'
|
||||
? props.t('Sign In Failed')
|
||||
: props.t('Sign Out Failed'),
|
||||
text:
|
||||
accountAction.value?.type === 'signIn'
|
||||
? props.t('Sign In Failed')
|
||||
: props.t('Sign Out Failed'),
|
||||
};
|
||||
case 'ready':
|
||||
default:
|
||||
@@ -214,7 +242,9 @@ const { copy, copied, isSupported } = useClipboard({ source: keyUrl.value });
|
||||
*/
|
||||
const showUpdateEligibility = computed(() => {
|
||||
// rather than specifically targeting 'Starter' and 'Unleashed' we'll target all keys that are not 'Basic', 'Plus', 'Pro', 'Lifetime', or 'Trial'
|
||||
if (!keyType.value) { return false; }
|
||||
if (!keyType.value) {
|
||||
return false;
|
||||
}
|
||||
return !['Basic', 'Plus', 'Pro', 'Lifetime', 'Trial'].includes(keyType.value);
|
||||
});
|
||||
</script>
|
||||
@@ -245,20 +275,13 @@ const showUpdateEligibility = computed(() => {
|
||||
:text="keyInstallStatusCopy.text"
|
||||
>
|
||||
<div v-if="keyType === 'Trial'" class="opacity-75 italic mt-4px">
|
||||
<UpcUptimeExpire
|
||||
v-if="refreshServerStateStatus === 'done'"
|
||||
:for-expire="true"
|
||||
:t="t"
|
||||
/>
|
||||
<UpcUptimeExpire v-if="refreshServerStateStatus === 'done'" :for-expire="true" :t="t" />
|
||||
<p v-else>
|
||||
{{ t('Calculating trial expiration…') }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="showUpdateEligibility" class="opacity-75 italic mt-4px">
|
||||
<RegistrationUpdateExpiration
|
||||
v-if="refreshServerStateStatus === 'done'"
|
||||
:t="t"
|
||||
/>
|
||||
<RegistrationUpdateExpiration v-if="refreshServerStateStatus === 'done'" :t="t" />
|
||||
<p v-else>
|
||||
{{ t('Calculating OS Update Eligibility…') }}
|
||||
</p>
|
||||
@@ -276,7 +299,10 @@ const showUpdateEligibility = computed(() => {
|
||||
{{ t('Copy your Key URL: {0}', [keyUrl]) }}
|
||||
</p>
|
||||
<p>
|
||||
<a href="/Tools/Registration" class="opacity-75 hover:opacity-100 focus:opacity-100 underline transition">
|
||||
<a
|
||||
href="/Tools/Registration"
|
||||
class="opacity-75 hover:opacity-100 focus:opacity-100 underline transition"
|
||||
>
|
||||
{{ t('Then go to Tools > Registration to manually install it') }}
|
||||
</a>
|
||||
</p>
|
||||
@@ -284,7 +310,11 @@ const showUpdateEligibility = computed(() => {
|
||||
</UpcCallbackFeedbackStatus>
|
||||
|
||||
<UpcCallbackFeedbackStatus
|
||||
v-if="stateDataError && callbackStatus !== 'loading' && (keyInstallStatus === 'success' || keyInstallStatus === 'failed')"
|
||||
v-if="
|
||||
stateDataError &&
|
||||
callbackStatus !== 'loading' &&
|
||||
(keyInstallStatus === 'success' || keyInstallStatus === 'failed')
|
||||
"
|
||||
:error="true"
|
||||
:text="t('Post Install License Key Error')"
|
||||
>
|
||||
@@ -322,7 +352,11 @@ const showUpdateEligibility = computed(() => {
|
||||
</p>
|
||||
|
||||
<p class="text-14px italic opacity-75">
|
||||
{{ callbackTypeDowngrade ? t('This downgrade will require a reboot') : t('This update will require a reboot') }}
|
||||
{{
|
||||
callbackTypeDowngrade
|
||||
? t('This downgrade will require a reboot')
|
||||
: t('This update will require a reboot')
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -332,12 +366,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 btn-style="underline" :icon="XMarkIcon" :text="closeText" @click="close" />
|
||||
|
||||
<template v-if="connectPluginInstalled && accountActionType === 'signIn'">
|
||||
<BrandButton
|
||||
@@ -372,7 +401,9 @@ const showUpdateEligibility = computed(() => {
|
||||
/>
|
||||
<BrandButton
|
||||
:icon="CheckIcon"
|
||||
:text="callbackTypeDowngrade ? t('Confirm and start downgrade') : t('Confirm and start update')"
|
||||
:text="
|
||||
callbackTypeDowngrade ? t('Confirm and start downgrade') : t('Confirm and start update')
|
||||
"
|
||||
@click="confirmUpdateOs"
|
||||
/>
|
||||
</template>
|
||||
@@ -390,8 +421,8 @@ const showUpdateEligibility = computed(() => {
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
@@ -440,6 +471,4 @@ const showUpdateEligibility = computed(() => {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@tailwind utilities;
|
||||
</style>
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import { BrandButton, BrandLoadingWhite } from '@unraid/ui';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUnraidApiStore } from '~/store/unraidApi';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
import { useServerStore } from '~/store/server';
|
||||
import { useUnraidApiStore } from '~/store/unraidApi';
|
||||
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
import BrandLoadingWhite from '~/components/Brand/LoadingWhite.vue';
|
||||
|
||||
defineProps<{ t: ComposerTranslation; }>();
|
||||
defineProps<{ t: ComposerTranslation }>();
|
||||
|
||||
const { expireTime, connectPluginInstalled, state, stateData } = storeToRefs(useServerStore());
|
||||
const { unraidApiStatus, unraidApiRestartAction } = storeToRefs(useUnraidApiStore());
|
||||
|
||||
const showExpireTime = computed(() => (state.value === 'TRIAL' || state.value === 'EEXPIRED') && expireTime.value > 0);
|
||||
const showExpireTime = computed(
|
||||
() => (state.value === 'TRIAL' || state.value === 'EEXPIRED') && expireTime.value > 0
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -26,21 +23,24 @@ const showExpireTime = computed(() => (state.value === 'TRIAL' || state.value ==
|
||||
class="text-center prose text-16px leading-relaxed whitespace-normal opacity-75 gap-y-8px"
|
||||
v-html="t(stateData.message)"
|
||||
/>
|
||||
<UpcUptimeExpire
|
||||
v-if="showExpireTime"
|
||||
class="text-center opacity-75 mt-12px"
|
||||
:t="t"
|
||||
/>
|
||||
<UpcUptimeExpire v-if="showExpireTime" class="text-center opacity-75 mt-12px" :t="t" />
|
||||
</header>
|
||||
<template v-if="stateData.actions">
|
||||
<ul v-if="connectPluginInstalled && unraidApiStatus !== 'online'" class="list-reset flex flex-col gap-y-8px px-16px">
|
||||
<ul
|
||||
v-if="connectPluginInstalled && unraidApiStatus !== 'online'"
|
||||
class="list-reset flex flex-col gap-y-8px px-16px"
|
||||
>
|
||||
<li>
|
||||
<BrandButton
|
||||
class="w-full"
|
||||
:disabled="unraidApiStatus === 'connecting' || unraidApiStatus === 'restarting'"
|
||||
:icon="unraidApiStatus === 'restarting' ? BrandLoadingWhite : unraidApiRestartAction?.icon"
|
||||
:text="unraidApiStatus === 'restarting' ? t('Restarting unraid-api…') : t('Restart unraid-api')"
|
||||
:title="unraidApiStatus === 'restarting' ? t('Restarting unraid-api…') : t('Restart unraid-api')"
|
||||
:text="
|
||||
unraidApiStatus === 'restarting' ? t('Restarting unraid-api…') : t('Restart unraid-api')
|
||||
"
|
||||
:title="
|
||||
unraidApiStatus === 'restarting' ? t('Restarting unraid-api…') : t('Restart unraid-api')
|
||||
"
|
||||
@click="unraidApiRestartAction?.click?.()"
|
||||
/>
|
||||
</li>
|
||||
@@ -51,9 +51,9 @@ const showExpireTime = computed(() => (state.value === 'TRIAL' || state.value ==
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../../assets/main.css';
|
||||
|
||||
.DropdownWrapper_blip {
|
||||
box-shadow: var(--ring-offset-shadow), var(--ring-shadow), var(--shadow-foreground);
|
||||
|
||||
12489
web/package-lock.json
generated
12489
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,7 @@
|
||||
"@heroicons/vue": "^2.2.0",
|
||||
"@nuxtjs/color-mode": "^3.5.2",
|
||||
"@pinia/nuxt": "^0.9.0",
|
||||
"@unraid/ui": "file:../unraid-ui",
|
||||
"@vue/apollo-composable": "^4.2.1",
|
||||
"@vueuse/components": "^12.0.0",
|
||||
"@vueuse/integrations": "^12.0.0",
|
||||
@@ -87,6 +88,9 @@
|
||||
"vue-i18n": "^10.0.5",
|
||||
"wretch": "^2.11.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.30.1"
|
||||
},
|
||||
"overrides": {
|
||||
"vue": "latest",
|
||||
"radix-vue": {
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ExclamationTriangleIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { ExclamationTriangleIcon } from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, BrandLogo } from '@unraid/ui';
|
||||
import { serverState } from '~/_data/serverState';
|
||||
import type { SendPayloads } from '~/store/callback';
|
||||
import type { UiBadgePropsColor } from '~/types/ui/badge';
|
||||
import type { ButtonStyle } from '~/types/ui/button';
|
||||
import AES from 'crypto-js/aes';
|
||||
import BrandButton from '~/components/Brand/Button.vue';
|
||||
|
||||
const { registerEntry } = useCustomElements();
|
||||
onBeforeMount(() => {
|
||||
registerEntry('UnraidComponents');
|
||||
});
|
||||
|
||||
useHead({
|
||||
meta: [
|
||||
{ name: 'viewport',
|
||||
content: 'width=1300', }
|
||||
]
|
||||
})
|
||||
meta: [{ name: 'viewport', content: 'width=1300' }],
|
||||
});
|
||||
|
||||
const valueToMakeCallback = ref<SendPayloads | undefined>();
|
||||
const callbackDestination = ref<string>('');
|
||||
@@ -42,6 +36,19 @@ const createCallbackUrl = (payload: SendPayloads, sendType: string) => {
|
||||
callbackDestination.value = destinationUrl.toString(); // differs from callbackActions.send
|
||||
};
|
||||
|
||||
const variants = [
|
||||
'fill',
|
||||
'black',
|
||||
'gray',
|
||||
'outline',
|
||||
'outline-black',
|
||||
'outline-white',
|
||||
'underline',
|
||||
'underline-hover-red',
|
||||
'white',
|
||||
'none',
|
||||
] as const;
|
||||
|
||||
onMounted(() => {
|
||||
createCallbackUrl(
|
||||
[
|
||||
@@ -59,35 +66,6 @@ onMounted(() => {
|
||||
'forUpc'
|
||||
);
|
||||
});
|
||||
|
||||
const badgeColors = [
|
||||
'black',
|
||||
'white',
|
||||
'red',
|
||||
'yellow',
|
||||
'green',
|
||||
'blue',
|
||||
'indigo',
|
||||
'purple',
|
||||
'pink',
|
||||
'orange',
|
||||
'transparent',
|
||||
'current',
|
||||
'gray',
|
||||
'custom',
|
||||
] as UiBadgePropsColor[];
|
||||
|
||||
const buttonColors = [
|
||||
'black',
|
||||
'fill',
|
||||
'gray',
|
||||
'outline',
|
||||
'outline-black',
|
||||
'outline-white',
|
||||
'underline',
|
||||
'underline-hover-red',
|
||||
'white',
|
||||
] as ButtonStyle[];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -162,17 +140,16 @@ const buttonColors = [
|
||||
</code>
|
||||
</div>
|
||||
<div class="bg-background">
|
||||
<hr class="border-black dark:border-white" />
|
||||
<h2 class="text-xl font-semibold font-mono">Legacy Badge Components</h2>
|
||||
<template v-for="color in badgeColors" :key="color">
|
||||
<UiBadge size="14px" :icon="ExclamationTriangleIcon" :color="color">{{ color }}</UiBadge>
|
||||
</template>
|
||||
</div>
|
||||
<div class="bg-background">
|
||||
<hr class="border-black dark:border-white" />
|
||||
<h2 class="text-xl font-semibold font-mono">Legacy Button Components</h2>
|
||||
<template v-for="color in buttonColors" :key="color">
|
||||
<BrandButton type="button" size="14px" :icon="ExclamationTriangleIcon" :btn-style="color as ButtonStyle">{{ color }}</BrandButton>
|
||||
<hr class="border-black dark:border-white" />
|
||||
<h2 class="text-xl font-semibold font-mono">Brand Button Component</h2>
|
||||
<template v-for="variant in variants" :key="variant">
|
||||
<BrandButton
|
||||
:variant="variant"
|
||||
type="button"
|
||||
size="14px"
|
||||
:icon="ExclamationTriangleIcon"
|
||||
>{{ variant }}</BrandButton
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -182,6 +159,10 @@ const buttonColors = [
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../assets/main.css';
|
||||
|
||||
code {
|
||||
@apply rounded-lg bg-gray-200 p-1 text-black shadow;
|
||||
}
|
||||
|
||||
@@ -1,283 +1,34 @@
|
||||
import 'dotenv/config';
|
||||
import tailwindConfig from '@unraid/ui/tailwind.config';
|
||||
import type { Config } from 'tailwindcss';
|
||||
import type { PluginAPI } from 'tailwindcss/types/config';
|
||||
import remToRem from './utils/tailwind-rem-to-rem';
|
||||
|
||||
// @ts-expect-error - just trying to get this to build @fixme
|
||||
export default <Partial<Config>>{
|
||||
export default {
|
||||
presets: [tailwindConfig],
|
||||
content: [
|
||||
// Web components
|
||||
'./components/**/*.ce.{js,vue,ts}',
|
||||
// Regular Vue components
|
||||
'./components/**/*.{js,vue,ts}',
|
||||
'./layouts/**/*.vue',
|
||||
'./pages/**/*.vue',
|
||||
'../unraid-ui/src/**/*.{vue,ts}',
|
||||
],
|
||||
darkMode: ['selector'],
|
||||
safelist: [
|
||||
'dark',
|
||||
'DropdownWrapper_blip',
|
||||
'unraid_mark_1',
|
||||
'unraid_mark_2',
|
||||
'unraid_mark_3',
|
||||
'unraid_mark_4',
|
||||
'unraid_mark_6',
|
||||
'unraid_mark_7',
|
||||
'unraid_mark_8',
|
||||
'unraid_mark_9',
|
||||
],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
screens: {
|
||||
'2xl': '1400px',
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: 'clear-sans,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji',
|
||||
},
|
||||
colors: {
|
||||
inherit: 'inherit',
|
||||
transparent: 'transparent',
|
||||
|
||||
black: '#1c1b1b',
|
||||
'grey-darkest': '#222',
|
||||
'grey-darker': '#606f7b',
|
||||
'grey-dark': '#383735',
|
||||
'grey-mid': '#999999',
|
||||
grey: '#e0e0e0',
|
||||
'grey-light': '#dae1e7',
|
||||
'grey-lighter': '#f1f5f8',
|
||||
'grey-lightest': '#f2f2f2',
|
||||
white: '#ffffff',
|
||||
|
||||
// unraid colors
|
||||
'yellow-accent': '#E9BF41',
|
||||
'orange-dark': '#f15a2c',
|
||||
orange: '#ff8c2f',
|
||||
// palettes generated from https://uicolors.app/create
|
||||
'unraid-red': {
|
||||
DEFAULT: '#E22828',
|
||||
'50': '#fef2f2',
|
||||
'100': '#ffe1e1',
|
||||
'200': '#ffc9c9',
|
||||
'300': '#fea3a3',
|
||||
'400': '#fc6d6d',
|
||||
'500': '#f43f3f',
|
||||
'600': '#e22828',
|
||||
'700': '#bd1818',
|
||||
'800': '#9c1818',
|
||||
'900': '#821a1a',
|
||||
'950': '#470808',
|
||||
},
|
||||
|
||||
'unraid-green': {
|
||||
DEFAULT: '#63A659',
|
||||
'50': '#f5f9f4',
|
||||
'100': '#e7f3e5',
|
||||
'200': '#d0e6cc',
|
||||
'300': '#aad1a4',
|
||||
'400': '#7db474',
|
||||
'500': '#63a659',
|
||||
'600': '#457b3e',
|
||||
'700': '#396134',
|
||||
'800': '#314e2d',
|
||||
'900': '#284126',
|
||||
'950': '#122211',
|
||||
},
|
||||
'header-text-primary': 'var(--header-text-primary)',
|
||||
'header-text-secondary': 'var(--header-text-secondary)',
|
||||
'header-background-color': 'var(--header-background-color)',
|
||||
// ShadCN
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
},
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
},
|
||||
},
|
||||
// Unfortunately due to webGUI CSS setting base HTML font-size to .65% or something we must use pixel values for web components
|
||||
fontSize: {
|
||||
'10px': '10px',
|
||||
'12px': '12px',
|
||||
'14px': '14px',
|
||||
'16px': '16px',
|
||||
'18px': '18px',
|
||||
'20px': '20px',
|
||||
'24px': '24px',
|
||||
'30px': '30px',
|
||||
},
|
||||
spacing: {
|
||||
'4.5': '1.125rem',
|
||||
'-8px': '-8px',
|
||||
'2px': '2px',
|
||||
'4px': '4px',
|
||||
'6px': '6px',
|
||||
'8px': '8px',
|
||||
'10px': '10px',
|
||||
'12px': '12px',
|
||||
'14px': '14px',
|
||||
'16px': '16px',
|
||||
'20px': '20px',
|
||||
'24px': '24px',
|
||||
'28px': '28px',
|
||||
'32px': '32px',
|
||||
'36px': '36px',
|
||||
'40px': '40px',
|
||||
'64px': '64px',
|
||||
'80px': '80px',
|
||||
'90px': '90px',
|
||||
'150px': '150px',
|
||||
'160px': '160px',
|
||||
'200px': '200px',
|
||||
'260px': '260px',
|
||||
'300px': '300px',
|
||||
'310px': '310px',
|
||||
'350px': '350px',
|
||||
'448px': '448px',
|
||||
'512px': '512px',
|
||||
'640px': '640px',
|
||||
'800px': '800px',
|
||||
},
|
||||
minWidth: {
|
||||
'86px': '86px',
|
||||
'160px': '160px',
|
||||
'260px': '260px',
|
||||
'300px': '300px',
|
||||
'310px': '310px',
|
||||
'350px': '350px',
|
||||
'800px': '800px',
|
||||
},
|
||||
maxWidth: {
|
||||
'86px': '86px',
|
||||
'160px': '160px',
|
||||
'260px': '260px',
|
||||
'300px': '300px',
|
||||
'310px': '310px',
|
||||
'350px': '350px',
|
||||
'640px': '640px',
|
||||
'800px': '800px',
|
||||
'1024px': '1024px',
|
||||
},
|
||||
screens: {
|
||||
'2xs': '470px',
|
||||
xs: '530px',
|
||||
tall: { raw: '(min-height: 700px)' },
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: 0 },
|
||||
to: { height: 'var(--radix-accordion-content-height)' },
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--radix-accordion-content-height)' },
|
||||
to: { height: 0 },
|
||||
},
|
||||
'collapsible-down': {
|
||||
from: { height: 0 },
|
||||
to: { height: 'var(--radix-collapsible-content-height)' },
|
||||
},
|
||||
'collapsible-up': {
|
||||
from: { height: 'var(--radix-collapsible-content-height)' },
|
||||
to: { height: 0 },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
|
||||
'collapsible-up': 'collapsible-up 0.2s ease-in-out',
|
||||
},
|
||||
/**
|
||||
* @todo modify prose classes to use pixels for webgui…sadge https://tailwindcss.com/docs/typography-plugin#customizing-the-default-theme
|
||||
*/
|
||||
|
||||
typography: (theme: PluginAPI['theme']) => ({
|
||||
DEFAULT: {
|
||||
css: {
|
||||
color: theme('colors.foreground'),
|
||||
a: {
|
||||
color: theme('colors.primary'),
|
||||
textDecoration: 'underline',
|
||||
'&:hover': {
|
||||
color: theme('colors.primary-foreground'),
|
||||
},
|
||||
},
|
||||
'--tw-prose-body': theme('colors.foreground'),
|
||||
'--tw-prose-headings': theme('colors.foreground'),
|
||||
'--tw-prose-lead': theme('colors.foreground'),
|
||||
'--tw-prose-links': theme('colors.primary'),
|
||||
'--tw-prose-bold': theme('colors.foreground'),
|
||||
'--tw-prose-counters': theme('colors.foreground'),
|
||||
'--tw-prose-bullets': theme('colors.foreground'),
|
||||
'--tw-prose-hr': theme('colors.foreground'),
|
||||
'--tw-prose-quotes': theme('colors.foreground'),
|
||||
'--tw-prose-quote-borders': theme('colors.foreground'),
|
||||
'--tw-prose-captions': theme('colors.foreground'),
|
||||
'--tw-prose-code': theme('colors.foreground'),
|
||||
'--tw-prose-pre-code': theme('colors.foreground'),
|
||||
'--tw-prose-pre-bg': theme('colors.background'),
|
||||
'--tw-prose-th-borders': theme('colors.foreground'),
|
||||
'--tw-prose-td-borders': theme('colors.foreground'),
|
||||
'--tw-prose-invert-body': theme('colors.background'),
|
||||
'--tw-prose-invert-headings': theme('colors.background'),
|
||||
'--tw-prose-invert-lead': theme('colors.background'),
|
||||
'--tw-prose-invert-links': theme('colors.primary'),
|
||||
'--tw-prose-invert-bold': theme('colors.background'),
|
||||
'--tw-prose-invert-counters': theme('colors.background'),
|
||||
'--tw-prose-invert-bullets': theme('colors.background'),
|
||||
'--tw-prose-invert-hr': theme('colors.background'),
|
||||
'--tw-prose-invert-quotes': theme('colors.background'),
|
||||
'--tw-prose-invert-quote-borders': theme('colors.background'),
|
||||
'--tw-prose-invert-captions': theme('colors.background'),
|
||||
'--tw-prose-invert-code': theme('colors.background'),
|
||||
'--tw-prose-invert-pre-code': theme('colors.background'),
|
||||
'--tw-prose-invert-pre-bg': theme('colors.foreground'),
|
||||
'--tw-prose-invert-th-borders': theme('colors.background'),
|
||||
'--tw-prose-invert-td-borders': theme('colors.background'),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
mode: 'jit',
|
||||
safelist: [],
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
require('tailwindcss-animate'),
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('./utils/tailwind-rem-to-rem').default({
|
||||
remToRem({
|
||||
baseFontSize: 16,
|
||||
/**
|
||||
* The font size where the web components will be rendered in production.
|
||||
* Required due to the webgui using the 62.5% font-size "trick".
|
||||
* Set an env to 16 for local development and 10 for everything else.
|
||||
*/
|
||||
newFontSize: process.env.VITE_TAILWIND_BASE_FONT_SIZE ?? 10,
|
||||
newFontSize: Number(process.env.VITE_TAILWIND_BASE_FONT_SIZE ?? 10),
|
||||
}),
|
||||
],
|
||||
};
|
||||
theme: {
|
||||
extend: {
|
||||
// web-specific extensions only
|
||||
},
|
||||
},
|
||||
} satisfies Partial<Config>;
|
||||
|
||||
Reference in New Issue
Block a user