223 lines
6.4 KiB
Vue
223 lines
6.4 KiB
Vue
<template>
|
|
<div class="dashboard-overview">
|
|
<section class="universal-card dashboard-header">
|
|
<Avatar :src="auth.user.avatar_url" size="md" circle :alt="auth.user.username" />
|
|
<div class="username">
|
|
<h1>
|
|
{{ auth.user.username }}
|
|
</h1>
|
|
<NuxtLink class="goto-link" :to="`/user/${auth.user.username}`">
|
|
Visit your profile
|
|
<ChevronRightIcon class="featured-header-chevron" aria-hidden="true" />
|
|
</NuxtLink>
|
|
</div>
|
|
</section>
|
|
<div class="dashboard-notifications">
|
|
<section class="universal-card">
|
|
<div class="header__row">
|
|
<h2 class="header__title">Notifications</h2>
|
|
<nuxt-link
|
|
v-if="notifications.length > 0"
|
|
class="goto-link"
|
|
to="/dashboard/notifications"
|
|
>
|
|
See all <ChevronRightIcon />
|
|
</nuxt-link>
|
|
</div>
|
|
<template v-if="notifications.length > 0">
|
|
<NotificationItem
|
|
v-for="notification in notifications"
|
|
:key="notification.id"
|
|
:notifications="notifications"
|
|
class="universal-card recessed"
|
|
:notification="notification"
|
|
:auth="auth"
|
|
raised
|
|
compact
|
|
@update:notifications="() => refresh()"
|
|
/>
|
|
<nuxt-link
|
|
v-if="extraNotifs > 0"
|
|
class="goto-link view-more-notifs"
|
|
to="/dashboard/notifications"
|
|
>
|
|
View {{ extraNotifs }} more notification{{ extraNotifs === 1 ? '' : 's' }}
|
|
<ChevronRightIcon />
|
|
</nuxt-link>
|
|
</template>
|
|
<div v-else class="universal-body">
|
|
<p>You have no unread notifications.</p>
|
|
<nuxt-link class="iconified-button" to="/dashboard/notifications/history">
|
|
<HistoryIcon /> View notification history
|
|
</nuxt-link>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="dashboard-analytics">
|
|
<section class="universal-card">
|
|
<h2>Analytics</h2>
|
|
<div class="grid-display">
|
|
<div class="grid-display__item">
|
|
<div class="label">Total downloads</div>
|
|
<div class="value">
|
|
{{ $formatNumber(projects.reduce((agg, x) => agg + x.downloads, 0)) }}
|
|
</div>
|
|
<span
|
|
>from
|
|
{{ downloadsProjectCount }}
|
|
project{{ downloadsProjectCount === 1 ? '' : 's' }}</span
|
|
>
|
|
<!-- <NuxtLink class="goto-link" to="/dashboard/analytics"-->
|
|
<!-- >View breakdown-->
|
|
<!-- <ChevronRightIcon-->
|
|
<!-- class="featured-header-chevron"-->
|
|
<!-- aria-hidden="true"-->
|
|
<!-- /></NuxtLink>-->
|
|
</div>
|
|
<div class="grid-display__item">
|
|
<div class="label">Total followers</div>
|
|
<div class="value">
|
|
{{ $formatNumber(projects.reduce((agg, x) => agg + x.followers, 0)) }}
|
|
</div>
|
|
<span>
|
|
<span
|
|
>from {{ followersProjectCount }} project{{
|
|
followersProjectCount === 1 ? '' : 's'
|
|
}}</span
|
|
></span
|
|
>
|
|
</div>
|
|
<div class="grid-display__item">
|
|
<div class="label">Current balance</div>
|
|
<div class="value">
|
|
{{ $formatMoney(auth.user.payout_data.balance, true) }}
|
|
</div>
|
|
<NuxtLink
|
|
v-if="auth.user.payout_data.balance > 0"
|
|
class="goto-link"
|
|
to="/dashboard/revenue"
|
|
>
|
|
Withdraw earnings
|
|
<ChevronRightIcon class="featured-header-chevron" aria-hidden="true" />
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script setup>
|
|
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg'
|
|
import HistoryIcon from '~/assets/images/utils/history.svg'
|
|
import Avatar from '~/components/ui/Avatar.vue'
|
|
import NotificationItem from '~/components/ui/NotificationItem.vue'
|
|
import { fetchExtraNotificationData, groupNotifications } from '~/helpers/notifications.js'
|
|
|
|
useHead({
|
|
title: 'Dashboard - Modrinth',
|
|
})
|
|
|
|
const auth = await useAuth()
|
|
|
|
const [{ data: projects }] = await Promise.all([
|
|
useAsyncData(`user/${auth.value.user.id}/projects`, () =>
|
|
useBaseFetch(`user/${auth.value.user.id}/projects`)
|
|
),
|
|
])
|
|
|
|
const downloadsProjectCount = computed(
|
|
() => projects.value.filter((project) => project.downloads > 0).length
|
|
)
|
|
const followersProjectCount = computed(
|
|
() => projects.value.filter((project) => project.followers > 0).length
|
|
)
|
|
|
|
const { data, refresh } = await useAsyncData(async () => {
|
|
const notifications = await useBaseFetch(`user/${auth.value.user.id}/notifications`)
|
|
|
|
const filteredNotifications = notifications.filter((notif) => !notif.read)
|
|
const slice = filteredNotifications.slice(0, 30) // send first 30 notifs to be grouped before trimming to 3
|
|
|
|
return fetchExtraNotificationData(slice).then((notifications) => {
|
|
notifications = groupNotifications(notifications).slice(0, 3)
|
|
return { notifications, extraNotifs: filteredNotifications.length - slice.length }
|
|
})
|
|
})
|
|
|
|
const notifications = computed(() => {
|
|
if (data.value === null) {
|
|
return []
|
|
}
|
|
return data.value.notifications
|
|
})
|
|
|
|
const extraNotifs = computed(() => (data.value ? data.value.extraNotifs : 0))
|
|
</script>
|
|
<style lang="scss">
|
|
.dashboard-overview {
|
|
display: grid;
|
|
grid-template:
|
|
'header header'
|
|
'notifications analytics' / 1fr auto;
|
|
gap: var(--spacing-card-md);
|
|
|
|
> .universal-card {
|
|
margin: 0;
|
|
}
|
|
|
|
@media screen and (max-width: 750px) {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
}
|
|
|
|
.dashboard-notifications {
|
|
grid-area: notifications;
|
|
//display: flex;
|
|
//flex-direction: column;
|
|
//gap: var(--spacing-card-md);
|
|
|
|
a.view-more-notifs {
|
|
display: flex;
|
|
width: fit-content;
|
|
margin-left: auto;
|
|
}
|
|
}
|
|
|
|
.dashboard-analytics {
|
|
grid-area: analytics;
|
|
}
|
|
|
|
.dashboard-header {
|
|
display: flex;
|
|
gap: var(--spacing-card-bg);
|
|
grid-area: header;
|
|
|
|
.username {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--spacing-card-sm);
|
|
justify-content: center;
|
|
word-break: break-word;
|
|
|
|
h1 {
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 650px) {
|
|
.avatar {
|
|
width: 4rem;
|
|
height: 4rem;
|
|
}
|
|
|
|
.username {
|
|
h1 {
|
|
font-size: var(--font-size-xl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|