mirror of
https://github.com/unraid/api.git
synced 2026-01-06 08:39:54 -06:00
refactor(web): use tabs instead of buttons in NotificationsSidebar header
This commit is contained in:
@@ -34,7 +34,7 @@ const icon = computed<{ component: Component, color: string } | null>(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="group/item relative w-full py-4 flex flex-col gap-2">
|
||||
<div class="group/item relative w-full py-4 pl-1 flex flex-col gap-2">
|
||||
<header class="w-full flex flex-row items-start justify-between gap-2">
|
||||
<h3 class="text-16px font-semibold leading-2 flex flex-row items-start gap-2">
|
||||
<component :is="icon.component" v-if="icon" class="size-6 shrink-0" :class="icon.color" />
|
||||
|
||||
@@ -14,33 +14,33 @@ import { useUnraidApiStore } from "~/store/unraidApi";
|
||||
import gql from "graphql-tag";
|
||||
|
||||
const getNotifications = gql`
|
||||
query Notifications($filter: NotificationFilter!) {
|
||||
notifications {
|
||||
list(filter: $filter) {
|
||||
id
|
||||
title
|
||||
subject
|
||||
description
|
||||
importance
|
||||
link
|
||||
type
|
||||
timestamp
|
||||
}
|
||||
query Notifications($filter: NotificationFilter!) {
|
||||
notifications {
|
||||
list(filter: $filter) {
|
||||
id
|
||||
title
|
||||
subject
|
||||
description
|
||||
importance
|
||||
link
|
||||
type
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const notifications = ref<NotificationItemProps[]>([]);
|
||||
watch(notifications, (newVal) => {
|
||||
console.log('[notifications]', newVal);
|
||||
console.log("[notifications]", newVal);
|
||||
});
|
||||
|
||||
const fetchType = ref<'UNREAD' | 'ARCHIVED'>('UNREAD');
|
||||
const setFetchType = (type: 'UNREAD' | 'ARCHIVED') => fetchType.value = type;
|
||||
const fetchType = ref<"UNREAD" | "ARCHIVED">("UNREAD");
|
||||
const setFetchType = (type: "UNREAD" | "ARCHIVED") => (fetchType.value = type);
|
||||
|
||||
const { unraidApiClient } = storeToRefs(useUnraidApiStore());
|
||||
|
||||
watch(unraidApiClient, async(newVal) => {
|
||||
watch(unraidApiClient, async (newVal) => {
|
||||
if (newVal) {
|
||||
const apiResponse = await newVal.query({
|
||||
query: getNotifications,
|
||||
@@ -66,30 +66,41 @@ const { teleportTarget, determineTeleportTarget } = useTeleport();
|
||||
<BellIcon class="w-6 h-6" />
|
||||
</SheetTrigger>
|
||||
|
||||
<SheetContent :to="teleportTarget" class="w-full max-w-[400px] sm:max-w-[540px]">
|
||||
<SheetContent
|
||||
:to="teleportTarget"
|
||||
class="w-full overflow-y-scroll max-w-[400px] sm:max-w-[540px]"
|
||||
>
|
||||
<SheetHeader>
|
||||
<SheetTitle>Notifications</SheetTitle>
|
||||
</SheetHeader>
|
||||
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<div class="w-auto flex flex-row justify-start items-center gap-1 p-2 rounded">
|
||||
<Button
|
||||
v-for="opt in ['Unread', 'Archived']"
|
||||
:key="opt"
|
||||
:variant="fetchType === opt ? 'secondary' : undefined"
|
||||
class="py-2 px-4 text-left"
|
||||
@click="setFetchType(opt.toUpperCase() as 'UNREAD' | 'ARCHIVED')"
|
||||
>
|
||||
{{ opt }}
|
||||
</Button>
|
||||
</div>
|
||||
<div class="w-auto flex flex-row justify-start items-center gap-1 p-2 rounded">
|
||||
<Tabs default-value="unread" class="">
|
||||
<div class="flex flex-row justify-between items-center flex-wrap gap-2">
|
||||
<TabsList class="ml-[1px]">
|
||||
<TabsTrigger
|
||||
class="text-[1rem] leading-[1.3rem]"
|
||||
value="unread"
|
||||
@click="setFetchType('UNREAD')"
|
||||
>
|
||||
Unread
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
class="text-[1rem] leading-[1.3rem]"
|
||||
value="archived"
|
||||
@="setFetchType('ARCHIVED')"
|
||||
>
|
||||
Archived
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
class="py-2 px-4 text-left"
|
||||
size="sm"
|
||||
class="text-[1rem] leading-[1.3rem]"
|
||||
>
|
||||
{{ `Archive All` }}
|
||||
</Button>
|
||||
|
||||
<Select>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Filter" />
|
||||
@@ -104,15 +115,23 @@ const { teleportTarget, determineTeleportTarget } = useTeleport();
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divide-y divide-gray-200">
|
||||
<NotificationsItem
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
v-bind="notification"
|
||||
/>
|
||||
</div>
|
||||
<TabsContent value="unread">
|
||||
<ScrollArea>
|
||||
<div class="divide-y divide-gray-200">
|
||||
<NotificationsItem
|
||||
v-for="notification in notifications"
|
||||
:key="notification.id"
|
||||
v-bind="notification"
|
||||
/>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="archived">
|
||||
<p>Archived</p>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<SheetFooter class="text-center">
|
||||
<p>Future pagination station</p>
|
||||
|
||||
29
web/components/shadcn/scroll-area/ScrollArea.vue
Normal file
29
web/components/shadcn/scroll-area/ScrollArea.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import {
|
||||
ScrollAreaCorner,
|
||||
ScrollAreaRoot,
|
||||
type ScrollAreaRootProps,
|
||||
ScrollAreaViewport,
|
||||
} from 'radix-vue'
|
||||
import ScrollBar from './ScrollBar.vue'
|
||||
import { cn } from '@/helpers/utils'
|
||||
|
||||
const props = defineProps<ScrollAreaRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ScrollAreaRoot v-bind="delegatedProps" :class="cn('relative overflow-hidden', props.class)">
|
||||
<ScrollAreaViewport class="h-full w-full rounded-[inherit]">
|
||||
<slot />
|
||||
</ScrollAreaViewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaCorner />
|
||||
</ScrollAreaRoot>
|
||||
</template>
|
||||
30
web/components/shadcn/scroll-area/ScrollBar.vue
Normal file
30
web/components/shadcn/scroll-area/ScrollBar.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { type HTMLAttributes, computed } from 'vue'
|
||||
import { ScrollAreaScrollbar, type ScrollAreaScrollbarProps, ScrollAreaThumb } from 'radix-vue'
|
||||
import { cn } from '@/helpers/utils'
|
||||
|
||||
const props = withDefaults(defineProps<ScrollAreaScrollbarProps & { class?: HTMLAttributes['class'] }>(), {
|
||||
orientation: 'vertical',
|
||||
})
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ScrollAreaScrollbar
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn('flex touch-none select-none transition-colors',
|
||||
orientation === 'vertical'
|
||||
&& 'h-full w-2.5 border-l border-l-transparent p-px',
|
||||
orientation === 'horizontal'
|
||||
&& 'h-2.5 flex-col border-t border-t-transparent p-px',
|
||||
props.class)"
|
||||
>
|
||||
<ScrollAreaThumb class="relative flex-1 rounded-full bg-border" />
|
||||
</ScrollAreaScrollbar>
|
||||
</template>
|
||||
2
web/components/shadcn/scroll-area/index.ts
Normal file
2
web/components/shadcn/scroll-area/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as ScrollArea } from './ScrollArea.vue'
|
||||
export { default as ScrollBar } from './ScrollBar.vue'
|
||||
Reference in New Issue
Block a user