chore(web): add pinia store and select dropdown for dummy server state (#1143)

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

- **New Features**
- Introduced an interactive server selector that lets users toggle
between different server modes (e.g., Default and OEM Activation) via a
dropdown.
- Integrated reactive state management across key pages, ensuring
dynamic UI updates.
  - Added new popover components for enhanced UI interactions.
- Introduced a settings interface for developers, allowing access to
server selection within a popover.

- **Bug Fixes**
- Restored functionality for the downgrade feature that was previously
removed.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1209127325997642
This commit is contained in:
Pujit Mehrotra
2025-02-19 08:53:54 -05:00
committed by GitHub
parent 9bc8060a83
commit 0b8df2a43e
10 changed files with 207 additions and 66 deletions
@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { PopoverRootEmits, PopoverRootProps } from 'radix-vue'
import { PopoverRoot, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<PopoverRootProps>()
const emits = defineEmits<PopoverRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<PopoverRoot v-bind="forwarded">
<slot />
</PopoverRoot>
</template>
@@ -0,0 +1,49 @@
<script setup lang="ts">
import type { PopoverContentEmits, PopoverContentProps } from 'radix-vue'
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
import {
PopoverContent,
PopoverPortal,
useForwardPropsEmits,
} from 'radix-vue'
import { computed } from 'vue'
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(
defineProps<PopoverContentProps & { class?: HTMLAttributes['class'] }>(),
{
align: 'center',
sideOffset: 4,
},
)
const emits = defineEmits<PopoverContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<PopoverPortal>
<PopoverContent
v-bind="{ ...forwarded, ...$attrs }"
:class="
cn(
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
)
"
>
<slot />
</PopoverContent>
</PopoverPortal>
</template>
@@ -0,0 +1,12 @@
<script setup lang="ts">
import type { PopoverTriggerProps } from 'radix-vue'
import { PopoverTrigger } from 'radix-vue'
const props = defineProps<PopoverTriggerProps>()
</script>
<template>
<PopoverTrigger v-bind="props">
<slot />
</PopoverTrigger>
</template>
@@ -0,0 +1,3 @@
export { default as Popover } from './Popover.vue'
export { default as PopoverContent } from './PopoverContent.vue'
export { default as PopoverTrigger } from './PopoverTrigger.vue'
+1
View File
@@ -155,3 +155,4 @@ export {
type ButtonProps,
};
export { Toaster } from '@/components/common/toast';
export * from '@/components/common/popover';
+32 -14
View File
@@ -11,6 +11,7 @@ import type {
ServerState,
// ServerUpdateOsResponse,
} from '~/types/server';
import { defineStore } from 'pinia'
// dayjs plugins
// extend(customParseFormat);
@@ -132,20 +133,7 @@ const osVersionBranch = 'stable';
// }
// };
export const serverState: Server = {
// activationCodeData: {
// code: 'CC2KP3TDRF',
// partnerName: 'OEM Partner',
// partnerUrl: 'https://unraid.net/OEM+Partner',
// sysModel: 'OEM Partner v1',
// comment: 'OEM Partner NAS',
// caseIcon: 'case-model.png',
// header: '#ffffff',
// headermetacolor: '#eeeeee',
// background: '#000000',
// showBannerGradient: 'yes',
// partnerLogo: true,
// },
const baseServerState: Server = {
avatar: 'https://source.unsplash.com/300x300/?portrait',
config: {
id: 'config-id',
@@ -204,3 +192,33 @@ export const serverState: Server = {
username: 'zspearmint',
wanFQDN: '',
};
export type ServerSelector = 'default' | 'oemActivation';
const defaultServer: ServerSelector = 'default';
const servers: Record<ServerSelector, Server> = {
default: baseServerState,
/** shows oem activation flow */
oemActivation: {
...baseServerState,
activationCodeData: {
code: 'CC2KP3TDRF',
partnerName: 'OEM Partner',
partnerUrl: 'https://unraid.net/OEM+Partner',
sysModel: 'OEM Partner v1',
comment: 'OEM Partner NAS',
caseIcon: 'case-model.png',
header: '#ffffff',
headermetacolor: '#eeeeee',
background: '#000000',
showBannerGradient: 'yes',
partnerLogo: true,
},
},
};
export const useDummyServerStore = defineStore('_dummyServer',() => {
const selector = ref<ServerSelector>(defaultServer);
const serverState = computed(() => servers[selector.value] ?? servers.default);
return { selector, serverState };
})
+29
View File
@@ -0,0 +1,29 @@
<script lang="ts" setup>
import {
Popover,
PopoverContent,
PopoverTrigger,
Button,
} from '@unraid/ui';
import { CogIcon } from '@heroicons/vue/24/solid';
</script>
<template>
<Popover>
<PopoverTrigger as-child>
<Button type="button" size="icon" class="fixed bottom-4 right-4 rounded-full bg-teal-500 z-50"><CogIcon class="size-6" /></Button>
</PopoverTrigger>
<PopoverContent>
<DummyServerSwitcher />
</PopoverContent>
</Popover>
</template>
<style lang="postcss">
/* Import unraid-ui globals first */
@import '@unraid/ui/styles';
@import '../assets/main.css';
</style>
+36
View File
@@ -0,0 +1,36 @@
<script lang="ts" setup>
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@unraid/ui';
import { useDummyServerStore, type ServerSelector } from '~/_data/serverState';
const store = useDummyServerStore();
const { selector, serverState } = storeToRefs(store);
const updateSelector = (val: string) => {
selector.value = val as ServerSelector;
};
</script>
<template>
<div class="flex flex-col gap-2 border-solid border-2 p-2 border-r-2">
<h1 class="text-lg">Server State Selection</h1>
<details>
<summary>Initial Server State: {{ selector }}</summary>
<pre>{{ JSON.stringify(serverState, null, 4) }}</pre>
</details>
<Select v-model="selector" @update:model-value="updateSelector">
<SelectTrigger>
<SelectValue placeholder="Select an initial state" />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default</SelectItem>
<SelectItem value="oemActiviation">OEM Activation</SelectItem>
</SelectContent>
</Select>
</div>
</template>
<style lang="postcss">
/* Import unraid-ui globals first */
@import '@unraid/ui/styles';
@import '../assets/main.css';
</style>
+4 -1
View File
@@ -1,13 +1,15 @@
<script lang="ts" setup>
import { ExclamationTriangleIcon } from '@heroicons/vue/24/solid';
import { BrandButton, BrandLogo } from '@unraid/ui';
import { serverState } from '~/_data/serverState';
import { useDummyServerStore } from '~/_data/serverState';
import AES from 'crypto-js/aes';
import type { SendPayloads } from '~/store/callback';
import SsoButtonCe from '~/components/SsoButton.ce.vue';
const serverStore = useDummyServerStore();
const { serverState } = storeToRefs(serverStore);
const { registerEntry } = useCustomElements();
onBeforeMount(() => {
registerEntry('UnraidComponents');
@@ -80,6 +82,7 @@ onMounted(() => {
<div class="pb-12 mx-auto">
<client-only>
<div class="flex flex-col gap-6 p-6">
<DummyServerSwitcher />
<ColorSwitcherCe />
<h2 class="text-xl font-semibold font-mono">Vue Components</h2>
<h3 class="text-lg font-semibold font-mono">UserProfileCe</h3>
+26 -51
View File
@@ -1,6 +1,8 @@
<script lang="ts" setup>
import { serverState } from '~/_data/serverState';
import { useDummyServerStore } from '~/_data/serverState';
const serverStore = useDummyServerStore();
const { serverState } = storeToRefs(serverStore);
const { registerEntry } = useCustomElements();
onBeforeMount(() => {
registerEntry('UnraidComponents');
@@ -12,12 +14,8 @@ onBeforeMount(() => {
<unraid-i18n-host
class="flex flex-col gap-6 p-6 mx-auto text-black bg-white dark:text-white dark:bg-black"
>
<h2 class="text-xl font-semibold font-mono">
Web Components
</h2>
<h3 class="text-lg font-semibold font-mono">
UserProfileCe
</h3>
<h2 class="text-xl font-semibold font-mono">Web Components</h2>
<h3 class="text-lg font-semibold font-mono">UserProfileCe</h3>
<header class="bg-header-background-color py-4 flex flex-row justify-between items-center">
<div class="inline-flex flex-col gap-4 items-start px-4">
<a href="https://unraid.net" target="_blank">
@@ -27,59 +25,36 @@ onBeforeMount(() => {
</div>
<unraid-user-profile :server="JSON.stringify(serverState)" />
</header>
<hr class="border-black dark:border-white" >
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">
ConnectSettingsCe
</h3>
<h3 class="text-lg font-semibold font-mono">ConnectSettingsCe</h3>
<ConnectSettingsCe />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
DownloadApiLogsCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">DownloadApiLogsCe</h3>
<unraid-download-api-logs />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
AuthCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">AuthCe</h3>
<unraid-auth />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
WanIpCheckCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">WanIpCheckCe</h3>
<unraid-wan-ip-check php-wan-ip="47.184.85.45" />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
HeaderOsVersion
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">HeaderOsVersion</h3>
<unraid-header-os-version />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
UpdateOsCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">UpdateOsCe</h3>
<unraid-update-os />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
DowngradeOsCe
</h3>
<unraid-downgrade-os
restore-release-date="2022-10-10"
restore-version="6.11.2"
/>
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
RegistrationCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">DowngradeOsCe</h3>
<unraid-downgrade-os restore-release-date="2022-10-10" restore-version="6.11.2" />
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">RegistrationCe</h3>
<unraid-registration />
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
ModalsCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">ModalsCe</h3>
<!-- uncomment to test modals <unraid-modals />-->
<hr class="border-black dark:border-white">
<h3 class="text-lg font-semibold font-mono">
SSOSignInButtonCe
</h3>
<hr class="border-black dark:border-white" />
<h3 class="text-lg font-semibold font-mono">SSOSignInButtonCe</h3>
<unraid-sso-button :ssoenabled="serverState.ssoEnabled" />
</unraid-i18n-host>
</client-only>