mirror of
https://github.com/unraid/api.git
synced 2026-01-07 09:10:05 -06:00
feat: KeyActions component & general progress
This commit is contained in:
@@ -29,7 +29,7 @@ const blacklistedGuid = '154B-00EE-0700-9B50CF819816';
|
||||
// EBLACKLISTED1
|
||||
// EBLACKLISTED2
|
||||
// ENOCONN
|
||||
const state: string = 'ENOKEYFILE2';
|
||||
const state: string = 'TRIAL';
|
||||
|
||||
const uptime = Date.now() - 60 * 60 * 1000; // 1 hour ago
|
||||
let expireTime = 0;
|
||||
@@ -46,8 +46,8 @@ const serverState = {
|
||||
expireTime,
|
||||
lanIp: '192.168.0.1',
|
||||
locale: 'en',
|
||||
pluginInstalled: true,
|
||||
registered: false,
|
||||
pluginInstalled: false,
|
||||
registered: true,
|
||||
site: 'http://localhost:4321',
|
||||
state,
|
||||
uptime,
|
||||
|
||||
22
app.vue
22
app.vue
@@ -12,32 +12,52 @@ onBeforeMount(() => {
|
||||
<client-only>
|
||||
<div class="flex flex-col gap-6 p-6">
|
||||
<h2>Vue Components</h2>
|
||||
<h3>UserProfileCe</h3>
|
||||
<UserProfileCe :server="serverState" />
|
||||
<h3>DownloadApiLogsCe</h3>
|
||||
<DownloadApiLogsCe />
|
||||
<h3>AuthCe</h3>
|
||||
<AuthCe />
|
||||
<h3>KeyActionsCe</h3>
|
||||
<KeyActionsCe />
|
||||
<h3>LaunchpadCe</h3>
|
||||
<LaunchpadCe />
|
||||
<h3>PluginPromoCe</h3>
|
||||
<PluginPromoCe />
|
||||
<h3>WanIpCheckCe</h3>
|
||||
<WanIpCheckCe />
|
||||
<h3>CallbackHandlerCe</h3>
|
||||
<CallbackHandlerCe />
|
||||
</div>
|
||||
<div class="flex flex-col gap-6 p-6">
|
||||
<h2>Web Components</h2>
|
||||
<h3>UserProfileCe</h3>
|
||||
<connect-user-profile :server="JSON.stringify(serverState)"></connect-user-profile>
|
||||
<h3>DownloadApiLogsCe</h3>
|
||||
<connect-download-api-logs></connect-download-api-logs>
|
||||
<h3>AuthCe</h3>
|
||||
<connect-auth></connect-auth>
|
||||
<h3>KeyActionsCe</h3>
|
||||
<connect-key-actions></connect-key-actions>
|
||||
<h3>LaunchpadCe</h3>
|
||||
<connect-launchpad></connect-launchpad>
|
||||
<h3>PluginPromoCe</h3>
|
||||
<connect-plugin-promo></connect-plugin-promo>
|
||||
<h3>WanIpCheckCe</h3>
|
||||
<connect-wan-ip-check></connect-wan-ip-check>
|
||||
<h3>CallbackHandlerCe</h3>
|
||||
<connect-callback-handler></connect-callback-handler>
|
||||
</div>
|
||||
</client-only>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
<style lang="postcss" scoped>
|
||||
h2 {
|
||||
@apply text-xl font-semibold font-mono;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-lg font-semibold font-mono;
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,18 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/*
|
||||
darkTheme
|
||||
alpha: '#1c1b1b',
|
||||
beta: '#f2f2f2',
|
||||
gamma: '#999999',
|
||||
|
||||
lightTheme
|
||||
alpha: '#f2f2f2',
|
||||
beta: '#1c1b1b',
|
||||
gamma: '#999999',
|
||||
*/
|
||||
|
||||
body {
|
||||
--color-alpha: #1c1b1b;
|
||||
--color-beta: #f2f2f2;
|
||||
@@ -23,7 +35,7 @@ body {
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: -10px;
|
||||
right: 37px;
|
||||
right: 40px;
|
||||
border-right: 11px solid transparent;
|
||||
border-bottom: 11px solid var(--color-alpha);
|
||||
border-left: 11px solid transparent;
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
const { keyActions } = storeToRefs(useServerStore());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-white font-semibold p-4 bg-orange-400 rounded-lg">
|
||||
KeyActions.ce
|
||||
</div>
|
||||
<template v-if="keyActions">
|
||||
<component
|
||||
v-for="action in keyActions" :key="action.name"
|
||||
:is="action.click ? 'button' : 'a'"
|
||||
@click="action.click()"
|
||||
rel="noopener noreferrer"
|
||||
class="text-white text-14px text-center w-full flex-none flex flex-row items-center justify-center gap-x-8px px-8px py-8px cursor-pointer rounded-md bg-gradient-to-r from-red to-orange hover:from-red/60 hover:to-orange/60 focus:from-red/60 focus:to-orange/60"
|
||||
target="_blank"
|
||||
download
|
||||
>
|
||||
<component v-if="action.icon" :is="action.icon" class="flex-shrink-0 w-14px" />
|
||||
{{ action.text }}
|
||||
</component>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
@@ -46,7 +46,7 @@ const installButtonClasses = 'text-white text-14px text-center w-full flex flex-
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="Promo TopBlip text-beta bg-alpha border-grey-darkest text-center relative z-10 w-full max-w-800px mr-8px p-24px sm:p-32px lg:px-40px shadow-md rounded-lg">
|
||||
<div class="text-beta bg-alpha border-grey-darkest text-center relative z-10 w-full max-w-800px mr-8px p-24px sm:p-32px lg:px-40px shadow-md rounded-lg">
|
||||
<h2 class="text-24px font-semibold my-0">
|
||||
Enhance your Unraid experience with these <span class="inline-flex flex-row items-end gap-x-4px"><br class="hidden sm:block"/>Connect <span><UpcBeta /></span></span> features
|
||||
</h2>
|
||||
|
||||
@@ -24,13 +24,7 @@ const toggleDropdown = useToggle(dropdownOpen);
|
||||
onClickOutside(dropdown, (_event) => dropdownOpen.value = false);
|
||||
|
||||
const serverStore = useServerStore();
|
||||
const { name, description, lanIp, uptime, expireTime, state } = storeToRefs(serverStore);
|
||||
|
||||
const uptimeOrExpiredTime = computed(() => {
|
||||
return (state.value === 'TRIAL' || state.value === 'EEXPIRED') && expireTime.value && expireTime.value > 0
|
||||
? expireTime.value
|
||||
: uptime.value;
|
||||
});
|
||||
const { name, description, lanIp } = storeToRefs(serverStore);
|
||||
|
||||
/**
|
||||
* Copy LAN IP on server name click
|
||||
@@ -72,9 +66,9 @@ onBeforeMount(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="UserProfile" class="text-alpha relative z-20 flex flex-col h-full pl-80px rounded">
|
||||
<div id="UserProfile" class="text-alpha relative z-20 flex flex-col h-full gap-y-4px pl-40px rounded">
|
||||
<div class="text-gamma text-12px text-right font-semibold leading-normal flex flex-row items-baseline justify-end gap-x-12px">
|
||||
<UpcUptimeExpire :time="uptimeOrExpiredTime" :state="state" />
|
||||
<UpcUptimeExpire />
|
||||
<span>•</span>
|
||||
<UpcServerState />
|
||||
</div>
|
||||
|
||||
@@ -1,115 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ArrowRightOnRectangleIcon, ArrowTopRightOnSquareIcon, CogIcon, InformationCircleIcon, UserIcon } from '@heroicons/vue/24/solid';
|
||||
|
||||
import { ACCOUNT, CONNECT_DASHBOARD, PLUGIN_SETTINGS } from '~/helpers/urls';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import type { ServerStateDataAction } from '~/types/server';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
const myServersEnv = ref<string>('Staging');
|
||||
const devEnv = ref<string>('development');
|
||||
const { pluginInstalled, registered } = storeToRefs(useServerStore());
|
||||
|
||||
const serverStore = useServerStore();
|
||||
const { pluginInstalled, registered, stateData } = storeToRefs(serverStore);
|
||||
|
||||
// Intended to hide sign in and sign out from actions v-for in UPC dropdown so we can display them separately
|
||||
const stateDataKeyActions = computed((): ServerStateDataAction[] | undefined => {
|
||||
const notAllowed = ['signIn', 'signOut'];
|
||||
if (!stateData.value.actions) return;
|
||||
return stateData.value.actions.filter(action => !notAllowed.includes(action.name));
|
||||
const showLaunchpad = computed(() => {
|
||||
return pluginInstalled.value && !registered.value;
|
||||
});
|
||||
|
||||
console.log('[registered]', registered.value);
|
||||
|
||||
const links = computed(():UserProfileLink[] => {
|
||||
return [
|
||||
...(registered.value && pluginInstalled.value
|
||||
? [
|
||||
{
|
||||
emphasize: true,
|
||||
external: true,
|
||||
href: CONNECT_DASHBOARD,
|
||||
icon: ArrowTopRightOnSquareIcon,
|
||||
text: 'Go to Connect',
|
||||
title: 'Opens Connect in new tab',
|
||||
},
|
||||
{
|
||||
external: true,
|
||||
href: ACCOUNT,
|
||||
icon: ArrowTopRightOnSquareIcon,
|
||||
text: 'Manage Unraid.net Account',
|
||||
title: 'Manage Unraid.net Account in new tab',
|
||||
},
|
||||
{
|
||||
href: PLUGIN_SETTINGS,
|
||||
icon: CogIcon,
|
||||
text: 'Settings',
|
||||
title: 'Go to Connect plugin settings',
|
||||
},
|
||||
{
|
||||
click: () => { console.debug('signOut') },
|
||||
external: true,
|
||||
icon: ArrowRightOnRectangleIcon,
|
||||
text: 'Sign Out',
|
||||
title: 'Sign Out to Unregister your server with Connect',
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(!registered.value && pluginInstalled.value
|
||||
? [
|
||||
{
|
||||
click: () => { console.debug('signIn') },
|
||||
external: true,
|
||||
icon: UserIcon,
|
||||
text: 'Sign In with Unraid.net Account',
|
||||
title: 'Sign In with Unraid.net Account',
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(!pluginInstalled.value
|
||||
? [
|
||||
{
|
||||
click: () => { console.debug('promo') },
|
||||
icon: InformationCircleIcon,
|
||||
text: 'Enhance your Unraid experience with Connect',
|
||||
title: 'Enhance your Unraid experience with Connect',
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
];
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UpcDropdownWrapper class="DropdownWrapper_blip text-beta absolute z-30 top-full right-0 min-w-300px max-w-350px">
|
||||
<header class="flex flex-row items-start justify-between mt-8px mx-8px">
|
||||
<h3 class="text-18px leading-none inline-flex flex-row gap-x-8px items-center">
|
||||
<span class="font-semibold">Connect</span>
|
||||
<UpcBeta />
|
||||
<span v-if="myServersEnv" :title="`API • ${myServersEnv}`">⚙️</span>
|
||||
<span v-if="devEnv" :title="`UPC • ${devEnv}`">⚠️</span>
|
||||
</h3>
|
||||
</header>
|
||||
<ul class="list-reset flex flex-col gap-y-4px p-0">
|
||||
<template v-if="stateDataKeyActions">
|
||||
<li v-for="action in stateDataKeyActions" :key="action.name">
|
||||
<UpcDropdownItem :item="action" />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<li class="m-8px">
|
||||
<UpcKeyline />
|
||||
</li>
|
||||
|
||||
<template v-if="links">
|
||||
<li v-for="(link, index) in links" :key="`link_${index}`">
|
||||
<UpcDropdownItem :item="link" />
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<UpcDropdownWrapper class="DropdownWrapper_blip text-beta absolute z-30 top-full right-0">
|
||||
<UpcDropdownContent v-if="!showLaunchpad" />
|
||||
<UpcDropdownLaunchpad v-else />
|
||||
</UpcDropdownWrapper>
|
||||
</template>
|
||||
|
||||
24
components/UserProfile/DropdownConnectStatus.vue
Normal file
24
components/UserProfile/DropdownConnectStatus.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ExclamationTriangleIcon } from '@heroicons/vue/24/solid';
|
||||
// import { storeToRefs } from 'pinia';
|
||||
// import { useServerStore } from '~/store/server';
|
||||
// const { stateData } = storeToRefs(useServerStore());
|
||||
|
||||
type ApiOnlineStatus = 'online'|'offline';
|
||||
const onlineStatus = ref<ApiOnlineStatus>('online');
|
||||
const apiLoading = ref(false);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="mb-8px px-8px flex flex-col items-center">
|
||||
<template v-if="apiLoading">
|
||||
<BrandLoading class="w-36px mx-auto" :height="21" />
|
||||
<span class="text-12px italic opacity-80">{{ 'Loading Connect status…' }}</span>
|
||||
</template>
|
||||
<span v-else class="w-full flex flex-row justify-start items-center gap-x-8px">
|
||||
<ExclamationTriangleIcon v-if="onlineStatus !== 'online'" class="text-red w-16px h-16px" />
|
||||
<span v-else class="block w-12px h-12px bg-green rounded-full"></span>
|
||||
<span>{{ onlineStatus !== 'online' ? 'Disconnected' : 'Connected' }}</span>
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
109
components/UserProfile/DropdownContent.vue
Normal file
109
components/UserProfile/DropdownContent.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ArrowRightOnRectangleIcon, ArrowTopRightOnSquareIcon, CogIcon, InformationCircleIcon, UserIcon } from '@heroicons/vue/24/solid';
|
||||
|
||||
import { ACCOUNT, CONNECT_DASHBOARD, PLUGIN_SETTINGS } from '~/helpers/urls';
|
||||
import { usePromoStore } from '~/store/promo';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
const myServersEnv = ref<string>('Staging');
|
||||
const devEnv = ref<string>('development');
|
||||
|
||||
const promoStore = usePromoStore();
|
||||
const { keyActions, pluginInstalled, registered, stateData } = storeToRefs(useServerStore());
|
||||
|
||||
const links = computed(():UserProfileLink[] => {
|
||||
return [
|
||||
...(registered.value && pluginInstalled.value
|
||||
? [
|
||||
{
|
||||
emphasize: true,
|
||||
external: true,
|
||||
href: CONNECT_DASHBOARD,
|
||||
icon: ArrowTopRightOnSquareIcon,
|
||||
text: 'Go to Connect',
|
||||
title: 'Opens Connect in new tab',
|
||||
},
|
||||
{
|
||||
external: true,
|
||||
href: ACCOUNT,
|
||||
icon: ArrowTopRightOnSquareIcon,
|
||||
text: 'Manage Unraid.net Account',
|
||||
title: 'Manage Unraid.net Account in new tab',
|
||||
},
|
||||
{
|
||||
href: PLUGIN_SETTINGS,
|
||||
icon: CogIcon,
|
||||
text: 'Settings',
|
||||
title: 'Go to Connect plugin settings',
|
||||
},
|
||||
{
|
||||
click: () => { console.debug('signOut') },
|
||||
external: true,
|
||||
icon: ArrowRightOnRectangleIcon,
|
||||
text: 'Sign Out',
|
||||
title: 'Sign Out to Unregister your server with Connect',
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(!registered.value && pluginInstalled.value
|
||||
? [
|
||||
{
|
||||
click: () => { console.debug('signIn') },
|
||||
external: true,
|
||||
icon: UserIcon,
|
||||
text: 'Sign In with Unraid.net Account',
|
||||
title: 'Sign In with Unraid.net Account',
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
...(!pluginInstalled.value
|
||||
? [
|
||||
{
|
||||
click: () => { promoStore.show() },
|
||||
icon: InformationCircleIcon,
|
||||
text: 'Enhance your Unraid experience with Connect',
|
||||
title: 'Enhance your Unraid experience with Connect',
|
||||
},
|
||||
]
|
||||
: []
|
||||
),
|
||||
];
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-y-8px min-w-300px max-w-350px">
|
||||
<header class="flex flex-row items-start justify-between mt-8px mx-8px">
|
||||
<h2 class="text-18px leading-none inline-flex flex-row gap-x-8px items-center">
|
||||
<span class="font-semibold">Connect</span>
|
||||
<UpcBeta />
|
||||
<span v-if="myServersEnv" :title="`API • ${myServersEnv}`">⚙️</span>
|
||||
<span v-if="devEnv" :title="`UPC • ${devEnv}`">⚠️</span>
|
||||
</h2>
|
||||
</header>
|
||||
<ul class="list-reset flex flex-col gap-y-4px p-0">
|
||||
<template v-if="keyActions">
|
||||
<li v-for="action in keyActions" :key="action.name">
|
||||
<UpcDropdownItem :item="action" />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<li class="m-8px">
|
||||
<UpcKeyline />
|
||||
</li>
|
||||
|
||||
<UpcDropdownError v-if="stateData.error" />
|
||||
<UpcDropdownConnectStatus v-else-if="registered && pluginInstalled" />
|
||||
|
||||
<template v-if="links">
|
||||
<li v-for="(link, index) in links" :key="`link_${index}`">
|
||||
<UpcDropdownItem :item="link" />
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
32
components/UserProfile/DropdownError.vue
Normal file
32
components/UserProfile/DropdownError.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { InformationCircleIcon } from '@heroicons/vue/24/solid';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import type { UserProfileLink } from '~/types/userProfile';
|
||||
|
||||
const { stateData } = storeToRefs(useServerStore());
|
||||
const links = ref<UserProfileLink[]>([
|
||||
{
|
||||
click: () => console.debug('Placeholder Button'),
|
||||
external: true,
|
||||
icon: InformationCircleIcon,
|
||||
text: 'Placeholder Button',
|
||||
},
|
||||
{
|
||||
click: () => console.debug('Support Button'),
|
||||
external: true,
|
||||
icon: InformationCircleIcon,
|
||||
text: 'Support Button',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul v-if="stateData.error" class="list-reset flex flex-col gap-y-4px p-12px -mx-4px bg-red/20">
|
||||
<h3>{{ stateData.error.heading }}</h3>
|
||||
<p>{{ stateData.error.message }}</p>
|
||||
<li v-for="(link, index) in links" :key="`link_${index}`" class="-mx-8px">
|
||||
<UpcDropdownItem :item="link" />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
@@ -28,7 +28,7 @@ const showExternalIconOnHover = computed(() => props.item?.external && props.ite
|
||||
}"
|
||||
>
|
||||
<span class="leading-snug inline-flex flex-row items-center gap-x-8px">
|
||||
<component :is="item?.icon" class="flex-shrink-0 fill-current w-16px h-16px" aria-hidden="true" />
|
||||
<component :is="item?.icon" class="flex-shrink-0 text-current w-16px h-16px" aria-hidden="true" />
|
||||
{{ item?.text }}
|
||||
</span>
|
||||
<ArrowTopRightOnSquareIcon
|
||||
|
||||
43
components/UserProfile/DropdownLaunchpad.vue
Normal file
43
components/UserProfile/DropdownLaunchpad.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script lang="ts" setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useServerStore } from '~/store/server';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
const { expireTime, state, stateData } = storeToRefs(useServerStore());
|
||||
|
||||
const showExpireTime = computed(() => {
|
||||
return (state.value === 'TRIAL' || state.value === 'EEXPIRED') && expireTime.value > 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-y-24px w-full min-w-300px md:min-w-[550px] max-w-4xl mr-8px p-16px md:py-24px md:px-32px lg:px-40px shadow-md rounded-lg">
|
||||
<header class="text-center">
|
||||
<h2 class="text-24px font-semibold">Thank you for installing Connect!</h2>
|
||||
<h3>Sign In to your Unraid.net account to register your server</h3>
|
||||
<UpcUptimeExpire v-if="showExpireTime" class="opacity-75 mt-12px" />
|
||||
</header>
|
||||
<ul class="list-reset flex flex-col gap-y-8px" v-if="stateData.actions">
|
||||
<li v-for="action in stateData.actions" :key="action.name">
|
||||
<component
|
||||
:is="action.click ? 'button' : 'a'"
|
||||
@click="action.click()"
|
||||
rel="noopener noreferrer"
|
||||
class="text-white text-14px text-center w-full flex-none flex flex-row items-center justify-center gap-x-8px px-8px py-8px cursor-pointer rounded-md bg-gradient-to-r from-red to-orange hover:from-red/60 hover:to-orange/60 focus:from-red/60 focus:to-orange/60"
|
||||
target="_blank"
|
||||
download
|
||||
>
|
||||
<component v-if="action.icon" :is="action.icon" class="flex-shrink-0 w-14px" />
|
||||
{{ action.text }}
|
||||
</component>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
</style>
|
||||
91
components/UserProfile/DropdownPromo.vue
Normal file
91
components/UserProfile/DropdownPromo.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<script lang="ts" setup>
|
||||
import type { UserProfilePromoFeature } from '~/types/userProfile';
|
||||
import 'tailwindcss/tailwind.css';
|
||||
import '~/assets/main.css';
|
||||
|
||||
const features = ref<UserProfilePromoFeature[]>([
|
||||
{
|
||||
title: 'Dynamic Remote Access',
|
||||
copy: 'Toggle on/off server accessibility with dynamic remote access. Automatically turn on UPnP and open a random WAN port on your router at the click of a button and close off access in seconds.',
|
||||
},
|
||||
{
|
||||
title: 'Manage Your Server Within Connect',
|
||||
copy: 'Servers equipped with a myunraid.net certificate can be managed directly from within the Connect web UI. Manage multiple servers from your phone, tablet, laptop, or PC in the same browser window.',
|
||||
},
|
||||
{
|
||||
title: 'Deep Linking',
|
||||
copy: 'The Connect dashboard links to relevant sections of the webgui, allowing quick access to those settings and server sections.',
|
||||
},
|
||||
{
|
||||
title: 'Online Flash Backup',
|
||||
copy: 'Never ever be left without a backup of your config. If you need to change flash drives, generate a backup from Connect and be up and running in minutes.',
|
||||
},
|
||||
{
|
||||
title: 'Real-time Monitoring',
|
||||
copy: 'Get an overview of your server\'s state, storage space, apps and VMs status, and more.',
|
||||
},
|
||||
{
|
||||
title: 'Customizable Dashboard Tiles',
|
||||
copy: 'Set custom server tiles how you like and automatically display your server\'s banner image on your Connect Dashboard.',
|
||||
},
|
||||
{
|
||||
title: 'License Management',
|
||||
copy: 'Manage your license keys at any time via the My Keys section.',
|
||||
},
|
||||
{
|
||||
title: 'Plus more on the way',
|
||||
copy: 'All you need is an active internet connection, an Unraid.net account, and the Connect plugin. Get started by installing the plugin.',
|
||||
},
|
||||
]);
|
||||
|
||||
const installPlugin = (staging = false) => {
|
||||
return console.debug(staging ? 'dynamix.unraid.net.staging.plg' : 'dynamix.unraid.net.plg');
|
||||
};
|
||||
|
||||
const installButtonClasses = 'text-white text-14px text-center w-full flex flex-row items-center justify-center gap-x-8px px-8px py-8px cursor-pointer rounded-md bg-gradient-to-r from-red to-orange hover:from-red/60 hover:to-orange/60 focus:from-red/60 focus:to-orange/60';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-center relative z-10 w-full max-w-800px p-24px sm:p-32px lg:px-40px">
|
||||
<h2 class="text-24px font-semibold my-0">
|
||||
Enhance your Unraid experience with these <span class="inline-flex flex-row items-end gap-x-4px"><br class="hidden sm:block"/>Connect <span><UpcBeta /></span></span> features
|
||||
</h2>
|
||||
|
||||
<div class="text-12px flex flex-wrap justify-center my-16px">
|
||||
<UpcPromoFeature
|
||||
v-for="(feature, index) in features"
|
||||
:key="index"
|
||||
:title="feature.title"
|
||||
:copy="feature.copy"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full max-w-xs flex flex-col gap-y-8px mx-auto">
|
||||
<!-- v-if="devEnv" -->
|
||||
<button @click="installPlugin(true)" :class="installButtonClasses">Install Staging</button>
|
||||
<button @click="installPlugin()" :class="installButtonClasses">{{ 'Install Connect' }}</button>
|
||||
<div>
|
||||
<a
|
||||
href="https://docs.unraid.net/category/unraid-connect"
|
||||
class="text-12px tracking-wide inline-block mx-8px opacity-60 hover:opacity-100 focus:opacity-100 underline transition"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
:title="'Checkout the Connect Documentation'"
|
||||
>{{ 'Learn More' }}</a>
|
||||
<button
|
||||
@click="console.debug('Close Promo')"
|
||||
class="text-12px tracking-wide inline-block mx-8px opacity-60 hover:opacity-100 focus:opacity-100 underline transition"
|
||||
:title="'Close Promo'"
|
||||
>
|
||||
{{ 'No thanks' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="postcss">
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
</style>
|
||||
@@ -1,10 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<span class="block w-12px h-12px mr-8px bg-green rounded-full"></span>
|
||||
Connected
|
||||
</div>
|
||||
</template>
|
||||
@@ -51,18 +51,15 @@ const title = computed((): string => {
|
||||
class="group text-18px hover:text-alpha focus:text-alpha border border-transparent flex flex-row justify-end items-center h-full gap-x-8px outline-none focus:outline-none"
|
||||
:title="title"
|
||||
>
|
||||
<!-- show info icon for non-error of myServersOutOfDate b/c it still allows the API to connect -->
|
||||
<InformationCircleIcon v-if="pluginOutdated" class="text-red fill-current relative w-24px h-24px" />
|
||||
<!-- v-else-if="showWarning" -->
|
||||
<ExclamationTriangleIcon class="text-red fill-current relative w-24px h-24px" />
|
||||
|
||||
<span class="flex flex-row items-center gap-x-8px">
|
||||
{{ text }}
|
||||
<UpcTriangleDown v-if="registeredAndPluginInstalled" :open="open" />
|
||||
<span class="leading-none">{{ text }}</span>
|
||||
<UpcTriangleDown :open="open" />
|
||||
</span>
|
||||
|
||||
<BrandAvatar />
|
||||
<UpcTriangleDown v-if="!registeredAndPluginInstalled" :open="open" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Bars3Icon, XMarkIcon } from "@heroicons/vue/24/solid";
|
||||
|
||||
export interface Props {
|
||||
open?: boolean;
|
||||
}
|
||||
@@ -7,11 +9,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<span
|
||||
class="w-0 h-0 border-t-[4px] border-l-[4px] border-r-[4px] border-t-solid border-t-black border-l-solid border-l-transparent border-r-solid border-r-transparent transition-transform"
|
||||
:class="{
|
||||
'-rotate-180': open,
|
||||
'rotate-0': !open,
|
||||
}"
|
||||
/>
|
||||
<Bars3Icon v-if="!open" class="w-16px" />
|
||||
<XMarkIcon v-else class="w-16px" />
|
||||
</template>
|
||||
@@ -1,31 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import dateDiff from '~/helpers/time/dateDiff';
|
||||
import dateFormat from '~/helpers/time/dateFormat';
|
||||
import buildStringFromValues from '~/helpers/time/buildTimeString';
|
||||
import { useServerStore } from '~/store/server';
|
||||
|
||||
export interface Props {
|
||||
time: string;
|
||||
state: string;
|
||||
}
|
||||
const serverStore = useServerStore();
|
||||
const { uptime, expireTime, state } = storeToRefs(serverStore);
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const uptimeOrExpiredTime = computed(() => {
|
||||
return (state.value === 'TRIAL' || state.value === 'EEXPIRED') && expireTime.value && expireTime.value > 0
|
||||
? expireTime.value
|
||||
: uptime.value;
|
||||
});
|
||||
|
||||
const parsedTime = ref<string>('');
|
||||
const formattedTime = computed<string>(() => {
|
||||
return dateFormat(props.time);
|
||||
return dateFormat((uptimeOrExpiredTime.value).toString());
|
||||
});
|
||||
|
||||
const countUp = computed<boolean>(() => {
|
||||
return props.state !== 'TRIAL' && props.state === 'EEXPIRED';
|
||||
return state.value !== 'TRIAL' && state.value === 'EEXPIRED';
|
||||
})
|
||||
|
||||
const output = computed(() => {
|
||||
if (!countUp.value) {
|
||||
return {
|
||||
title: props.state === 'EEXPIRED'
|
||||
title: state.value === 'EEXPIRED'
|
||||
? `Trial Key Expired at ${formattedTime.value}`
|
||||
: `Trial Key Expires at ${formattedTime.value}`,
|
||||
text: props.state === 'EEXPIRED'
|
||||
text: state.value === 'EEXPIRED'
|
||||
? `Trial Key Expired ${parsedTime.value}`
|
||||
: `Trial Key Expires in ${parsedTime.value}`,
|
||||
};
|
||||
@@ -36,7 +40,7 @@ const output = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const runDiff = () => parsedTime.value = buildStringFromValues(dateDiff(props.time, countUp.value));
|
||||
const runDiff = () => parsedTime.value = buildStringFromValues(dateDiff((uptimeOrExpiredTime.value).toString(), countUp.value));
|
||||
|
||||
let interval: string | number | NodeJS.Timeout | undefined = undefined;
|
||||
onBeforeMount(() => {
|
||||
|
||||
@@ -49,6 +49,10 @@ export default defineNuxtConfig({
|
||||
name: 'ConnectPluginPromo',
|
||||
path: '@/components/PluginPromo.ce',
|
||||
},
|
||||
{
|
||||
name: 'ConnectUserProfile',
|
||||
path: '@/components/UserProfile.ce',
|
||||
},
|
||||
{
|
||||
name: 'ConnectWanIpCheck',
|
||||
path: '@/components/WanIpCheck.ce',
|
||||
|
||||
26
store/promo.ts
Normal file
26
store/promo.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { useToggle } from '@vueuse/core';
|
||||
import { defineStore, createPinia, setActivePinia } from "pinia";
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
* @see https://github.com/vuejs/pinia/discussions/1085
|
||||
*/
|
||||
setActivePinia(createPinia());
|
||||
|
||||
export const usePromoStore = defineStore('promo', () => {
|
||||
const visible = ref<boolean>(false);
|
||||
|
||||
const hide = () => visible.value = false;
|
||||
const show = () => visible.value = true;
|
||||
const toggle = useToggle(visible);
|
||||
|
||||
watch(visible, () => {
|
||||
console.debug('[visible]', visible.value);
|
||||
});
|
||||
|
||||
return {
|
||||
visible,
|
||||
hide,
|
||||
show,
|
||||
toggle,
|
||||
};
|
||||
});
|
||||
@@ -234,6 +234,17 @@ export const useServerStore = defineStore('server', () => {
|
||||
};
|
||||
}
|
||||
});
|
||||
const authActionsNames = ['signIn', 'signOut'];
|
||||
// Extract sign in / out from actions so we can display seperately as needed
|
||||
const authActions = computed((): ServerStateDataAction | undefined => {
|
||||
if (!stateData.value.actions) return;
|
||||
return stateData.value.actions.find(action => authActionsNames.includes(action.name));
|
||||
});
|
||||
// Remove sign in / out from actions so we can display them separately
|
||||
const keyActions = computed((): ServerStateDataAction[] | undefined => {
|
||||
if (!stateData.value.actions) return;
|
||||
return stateData.value.actions.filter(action => !authActionsNames.includes(action.name));
|
||||
});
|
||||
|
||||
/**
|
||||
* Actions
|
||||
@@ -284,7 +295,9 @@ export const useServerStore = defineStore('server', () => {
|
||||
uptime,
|
||||
username,
|
||||
// getters
|
||||
authActions,
|
||||
isRemoteAccess,
|
||||
keyActions,
|
||||
pluginOutdated,
|
||||
server,
|
||||
stateData,
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { defineStore, createPinia, setActivePinia } from "pinia";
|
||||
import { useServerStore } from './server';
|
||||
/**
|
||||
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
|
||||
* @see https://github.com/vuejs/pinia/discussions/1085
|
||||
*/
|
||||
setActivePinia(createPinia());
|
||||
|
||||
export const useWanIpCheckStore = defineStore('wanIpCheck', () => {
|
||||
const serverStore = useServerStore();
|
||||
/**
|
||||
* State
|
||||
*/
|
||||
const wanIp = ref<string | null>(sessionStorage.getItem('unraidConnect_wanIp'));
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
/**
|
||||
* Actions
|
||||
*/
|
||||
return {
|
||||
// state
|
||||
// getters
|
||||
// actions
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user