Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate to @nuxt/ui-pro v3 #86

Draft
wants to merge 10 commits into
base: v3
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
147 changes: 147 additions & 0 deletions app/_components/home/HomeChart.client.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<script setup lang="ts">
import { eachDayOfInterval, eachWeekOfInterval, eachMonthOfInterval, format } from 'date-fns'
import { VisXYContainer, VisLine, VisAxis, VisArea, VisCrosshair, VisTooltip } from '@unovis/vue'
import type { Period, Range } from '~/types'

const cardRef = ref<HTMLElement | null>(null)

const props = defineProps({
period: {
type: String as PropType<Period>,
required: true
},
range: {
type: Object as PropType<Range>,
required: true
}
})

type DataRecord = {
date: Date
amount: number
}

const { width } = useElementSize(cardRef)

// We use `useAsyncData` here to have same random data on the client and server
const { data } = await useAsyncData<DataRecord[]>(async () => {
const dates = ({
daily: eachDayOfInterval,
weekly: eachWeekOfInterval,
monthly: eachMonthOfInterval
})[props.period](props.range)

const min = 1000
const max = 10000

return dates.map(date => ({ date, amount: Math.floor(Math.random() * (max - min + 1)) + min }))
}, {
watch: [() => props.period, () => props.range],
default: () => []
})

const x = (_: DataRecord, i: number) => i
const y = (d: DataRecord) => d.amount

const total = computed(() => data.value.reduce((acc: number, { amount }) => acc + amount, 0))

const formatNumber = new Intl.NumberFormat('en', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format

const formatDate = (date: Date): string => {
return ({
daily: format(date, 'd MMM'),
weekly: format(date, 'd MMM'),
monthly: format(date, 'MMM yyy')
})[props.period]
}

const xTicks = (i: number) => {
if (i === 0 || i === data.value.length - 1 || !data.value[i]) {
return ''
}

return formatDate(data.value[i].date)
}

const template = (d: DataRecord) => `${formatDate(d.date)}: ${formatNumber(d.amount)}`
</script>

<template>
<UDashboardCard
ref="cardRef"
:ui="{ body: { padding: '!pb-3 !px-0' } as any }"
>
<template #header>
<div>
<p class="text-sm text-gray-500 dark:text-gray-400 font-medium mb-1">
Revenue
</p>
<p class="text-3xl text-gray-900 dark:text-white font-semibold">
{{ formatNumber(total) }}
</p>
</div>
</template>

<VisXYContainer
:data="data"
:padding="{ top: 10 }"
class="h-96"
:width="width"
>
<VisLine
:x="x"
:y="y"
color="rgb(var(--color-primary-DEFAULT))"
/>
<VisArea
:x="x"
:y="y"
color="rgb(var(--color-primary-DEFAULT))"
:opacity="0.1"
/>

<VisAxis
type="x"
:x="x"
:tick-format="xTicks"
/>

<VisCrosshair
color="rgb(var(--color-primary-DEFAULT))"
:template="template"
/>

<VisTooltip />
</VisXYContainer>
</UDashboardCard>
</template>

<style scoped>
.unovis-xy-container {
--vis-crosshair-line-stroke-color: rgb(var(--color-primary-500));
--vis-crosshair-circle-stroke-color: #fff;

--vis-axis-grid-color: rgb(var(--color-gray-200));
--vis-axis-tick-color: rgb(var(--color-gray-200));
--vis-axis-tick-label-color: rgb(var(--color-gray-400));

--vis-tooltip-background-color: #fff;
--vis-tooltip-border-color: rgb(var(--color-gray-200));
--vis-tooltip-text-color: rgb(var(--color-gray-900));
}

.dark {
.unovis-xy-container {
--vis-crosshair-line-stroke-color: rgb(var(--color-primary-400));
--vis-crosshair-circle-stroke-color: rgb(var(--color-gray-900));

--vis-axis-grid-color: rgb(var(--color-gray-800));
--vis-axis-tick-color: rgb(var(--color-gray-800));
--vis-axis-tick-label-color: rgb(var(--color-gray-500));

--vis-tooltip-background-color: rgb(var(--color-gray-900));
--vis-tooltip-border-color: rgb(var(--color-gray-800));
--vis-tooltip-text-color: #fff;
}
}
</style>
19 changes: 19 additions & 0 deletions app/_components/home/HomeChart.server.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<UDashboardCard
ref="cardRef"
:ui="{ body: { padding: '!pb-3 !px-0' } }"
>
<template #header>
<div>
<p class="text-sm text-gray-500 dark:text-gray-400 font-medium mb-1">
Revenue
</p>
<p class="text-3xl text-gray-900 dark:text-white font-semibold">
---
</p>
</div>
</template>

<div class="h-96" />
</UDashboardCard>
</template>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
190 changes: 190 additions & 0 deletions app/_pages/inbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<script setup lang="ts">

Check failure on line 1 in app/_pages/inbox.vue

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest, 22)

Component name "inbox" should always be multi-word
import type { Mail } from '~/types'

const tabItems = [{
label: 'All'
}, {
label: 'Unread'
}]
const selectedTab = ref(0)

const dropdownItems = [[{
label: 'Mark as unread',
icon: 'i-heroicons-check-circle'
}, {
label: 'Mark as important',
icon: 'i-heroicons-exclamation-circle'
}], [{
label: 'Star thread',
icon: 'i-heroicons-star'
}, {
label: 'Mute thread',
icon: 'i-heroicons-pause-circle'
}]]

const { data: mails } = await useFetch<Mail[]>('/api/mails', { default: () => [] })

// Filter mails based on the selected tab
const filteredMails = computed(() => {
if (selectedTab.value === 1) {
return mails.value.filter(mail => !!mail.unread)
}

return mails.value
})

const selectedMail = ref<Mail | null>()

const isMailPanelOpen = computed({
get() {
return !!selectedMail.value
},
set(value: boolean) {
if (!value) {
selectedMail.value = null
}
}
})

// Reset selected mail if it's not in the filtered mails
watch(filteredMails, () => {
if (!filteredMails.value.find(mail => mail.id === selectedMail.value?.id)) {
selectedMail.value = null
}
})
</script>

<template>
<UDashboardPage>
<UDashboardPanel
id="inbox"
:width="400"
:resizable="{ min: 300, max: 500 }"
>
<UDashboardNavbar
title="Inbox"
:badge="filteredMails.length"
>
<template #right>
<UTabs
v-model="selectedTab"
:items="tabItems"
:ui="{ wrapper: '', list: { height: 'h-9', tab: { height: 'h-7', size: 'text-[13px]' } } }"
/>
</template>
</UDashboardNavbar>

<!-- ~/components/inbox/InboxList.vue -->
<InboxList
v-model="selectedMail"
:mails="filteredMails"
/>
</UDashboardPanel>

<UDashboardPanel
v-model="isMailPanelOpen"
collapsible
grow
side="right"
>
<template v-if="selectedMail">
<UDashboardNavbar>
<template #toggle>
<UDashboardNavbarToggle icon="i-heroicons-x-mark" />

<UDivider
orientation="vertical"
class="mx-1.5 lg:hidden"
/>
</template>

<template #left>
<UTooltip text="Archive">
<UButton
icon="i-heroicons-archive-box"
color="gray"
variant="ghost"
/>
</UTooltip>

<UTooltip text="Move to junk">
<UButton
icon="i-heroicons-archive-box-x-mark"
color="gray"
variant="ghost"
/>
</UTooltip>

<UDivider
orientation="vertical"
class="mx-1.5"
/>

<UPopover :popper="{ placement: 'bottom-start' }">
<template #default="{ open }">
<UTooltip
text="Snooze"
:prevent="open"
>
<UButton
icon="i-heroicons-clock"
color="gray"
variant="ghost"
:class="[open && 'bg-gray-50 dark:bg-gray-800']"
/>
</UTooltip>
</template>

<template #panel="{ close }">
<DatePicker @close="close" />
</template>
</UPopover>
</template>

<template #right>
<UTooltip text="Reply">
<UButton
icon="i-heroicons-arrow-uturn-left"
color="gray"
variant="ghost"
/>
</UTooltip>

<UTooltip text="Forward">
<UButton
icon="i-heroicons-arrow-uturn-right"
color="gray"
variant="ghost"
/>
</UTooltip>

<UDivider
orientation="vertical"
class="mx-1.5"
/>

<UDropdown :items="dropdownItems">
<UButton
icon="i-heroicons-ellipsis-vertical"
color="gray"
variant="ghost"
/>
</UDropdown>
</template>
</UDashboardNavbar>

<!-- ~/components/inbox/InboxMail.vue -->
<InboxMail :mail="selectedMail" />
</template>
<div
v-else
class="flex-1 hidden lg:flex items-center justify-center"
>
<UIcon
name="i-heroicons-inbox"
class="w-32 h-32 text-gray-400 dark:text-gray-500"
/>
</div>
</UDashboardPanel>
</UDashboardPage>
</template>
Loading