* Begin UI for threads and moderation overhaul * Hide close button on non-report threads * Fix review age coloring * Add project count * Remove action buttons from queue page and add queued date to project page * Hook up to actual data * Remove unused icon * Get up to 1000 projects in queue * prettier * more prettier * Changed all the things * lint * rebuild * Add omorphia * Workaround formatjs bug in ThreadSummary.vue * Fix notifications page on prod * Fix a few notifications and threads bugs * lockfile * Fix duplicate button styles * more fixes and polishing * More fixes * Remove legacy pages * More bugfixes * Add some error catching for reports and notifications * More error handling * fix lint * Add inbox links * Remove loading component and rename member header * Rely on threads always existing * Handle if project update notifs are not grouped * oops * Fix chips on notifications page * Import ModalModeration * finish threads --------- Co-authored-by: triphora <emma@modrinth.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me>
210 lines
6.1 KiB
Vue
210 lines
6.1 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>
|
|
<section class="universal-card dashboard-notifications">
|
|
<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"
|
|
v-model:notifications="allNotifs"
|
|
class="universal-card recessed"
|
|
:notification="notification"
|
|
raised
|
|
compact
|
|
/>
|
|
<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">
|
|
<HistoryIcon /> View notification history
|
|
</nuxt-link>
|
|
</div>
|
|
</section>
|
|
<section class="universal-card dashboard-analytics">
|
|
<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">Total revenue</div>
|
|
<div class="value">
|
|
{{ $formatMoney(payouts.all_time, true) }}
|
|
</div>
|
|
<span>{{ $formatMoney(payouts.last_month, true) }} in the last month</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 >= minWithdraw"
|
|
class="goto-link"
|
|
to="/dashboard/revenue"
|
|
>
|
|
Withdraw earnings
|
|
<ChevronRightIcon class="featured-header-chevron" aria-hidden="true" />
|
|
</NuxtLink>
|
|
<span v-else>${{ minWithdraw }} is the withdraw minimum</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</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 { fetchNotifications, groupNotifications } from '~/helpers/notifications.js'
|
|
|
|
useHead({
|
|
title: 'Dashboard - Modrinth',
|
|
})
|
|
|
|
const auth = await useAuth()
|
|
const app = useNuxtApp()
|
|
|
|
const [{ data: projects }, { data: payouts }] = await Promise.all([
|
|
useAsyncData(`user/${auth.value.user.id}/projects`, () =>
|
|
useBaseFetch(`user/${auth.value.user.id}/projects`, app.$defaultHeaders())
|
|
),
|
|
useAsyncData(`user/${auth.value.user.id}/payouts`, () =>
|
|
useBaseFetch(`user/${auth.value.user.id}/payouts`, app.$defaultHeaders())
|
|
),
|
|
])
|
|
|
|
const minWithdraw = ref(0.26)
|
|
|
|
const downloadsProjectCount = computed(
|
|
() => projects.value.filter((project) => project.downloads > 0).length
|
|
)
|
|
const followersProjectCount = computed(
|
|
() => projects.value.filter((project) => project.followers > 0).length
|
|
)
|
|
|
|
const allNotifs = groupNotifications(await fetchNotifications())
|
|
|
|
const notifications = computed(() => allNotifs.slice(0, 3))
|
|
const extraNotifs = computed(() => allNotifs.length - notifications.value.length)
|
|
</script>
|
|
<style lang="scss">
|
|
.dashboard-overview {
|
|
display: grid;
|
|
grid-template:
|
|
'header header'
|
|
'notifications analytics' / 1fr 1fr;
|
|
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>
|