Add transfer history and unify back elements with breadcrumbs (#1088)

* Add transfer history and unify back elements with breadcrumbs

* Increase padding of breadcrumbs, include previous query params, more consistent link underlining

* Prettier

* Add project type text and link to project pages

* Remove console.log
This commit is contained in:
Prospector 2023-04-15 12:08:11 -07:00 committed by GitHub
parent 257b35e4ae
commit fd28da2a3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 320 additions and 47 deletions

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 3v5h5"></path>
<path d="M3.05 13A9 9 0 1 0 6 5.3L3 8"></path>
<path d="M12 7v5l4 2"></path>
</svg>

After

Width:  |  Height:  |  Size: 299 B

View File

@ -605,6 +605,7 @@
&:focus-visible,
&:hover {
color: var(--color-link-hover);
text-decoration: underline;
}
&:active {
@ -1310,6 +1311,7 @@ textarea.known-error {
.goto-link:hover,
.goto-link:focus-visible {
color: var(--color-link-hover);
text-decoration: underline;
}
.goto-link:active {

View File

@ -21,6 +21,11 @@
<!-- Team members -->
<template v-else-if="type === 'accepted'"><CheckIcon /> Accepted</template>
<template v-else-if="type === 'pending'"> <ProcessingIcon /> Pending </template>
<!-- Transaction statuses -->
<template v-else-if="type === 'success'"><CheckIcon /> Success</template>
<!-- Other -->
<template v-else> <span class="circle" /> {{ $capitalizeString(type) }} </template>
</span>
</template>
@ -105,6 +110,7 @@ export default {
&.type--accepted,
&.type--admin,
&.type--success,
&.green {
--badge-color: var(--color-special-green);
}

View File

@ -0,0 +1,54 @@
<template>
<nav class="breadcrumbs">
<template v-for="(link, index) in linkStack" :key="index">
<NuxtLink
:to="link.href"
class="breadcrumb goto-link"
:class="{ trim: link.allowTrimming ? link.allowTrimming : false }"
>
{{ link.label }}
</NuxtLink>
<ChevronRightIcon />
</template>
<span class="breadcrumb">{{ currentTitle }}</span>
</nav>
</template>
<script setup>
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg'
defineProps({
linkStack: {
type: Array,
default: () => [],
},
currentTitle: {
type: String,
required: true,
},
})
</script>
<style lang="scss" scoped>
.breadcrumbs {
//padding: var(--spacing-card-md) var(--spacing-card-lg);
display: flex;
margin-bottom: var(--spacing-card-bg);
align-items: center;
flex-wrap: wrap;
svg {
width: 1.25rem;
height: 1.25rem;
}
a.breadcrumb {
padding-block: var(--spacing-card-xs);
&.trim {
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
</style>

View File

@ -2,6 +2,17 @@
<div v-if="$route.name.startsWith('type-id-settings')" class="normal-page">
<div class="normal-page__sidebar">
<aside class="universal-card">
<Breadcrumbs
current-title="Settings"
:link-stack="[
{ href: `/dashboard/projects`, label: 'Projects' },
{
href: `/${project.project_type}/${project.slug ? project.slug : project.id}`,
label: project.title,
allowTrimming: true,
},
]"
/>
<div class="settings-header">
<Avatar
:src="project.icon_url"
@ -18,64 +29,67 @@
</div>
<h2>Project settings</h2>
<NavStack>
<NavStackItem :link="`/${project.project_type}/${project.slug}/settings`" label="General">
<NavStackItem
:link="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
label="General"
>
<SettingsIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/settings/tags`"
:link="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/settings/tags`"
label="Tags"
>
<CategoriesIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/settings/description`"
:link="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/settings/description`"
label="Description"
>
<DescriptionIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/settings/license`"
:link="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/settings/license`"
label="License"
>
<LicenseIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/settings/links`"
:link="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/settings/links`"
label="Links"
>
<LinksIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/settings/members`"
:link="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/settings/members`"
label="Members"
>
<UsersIcon />
</NavStackItem>
<h3>Relevant pages</h3>
<h3>Upload</h3>
<NavStackItem
:link="`/${project.project_type}/${project.slug}`"
label="View project"
chevron
>
<EyeIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/gallery`"
:link="`/${project.project_type}/${project.slug ? project.slug : project.id}/gallery`"
label="Gallery"
chevron
>
<GalleryIcon />
</NavStackItem>
<NavStackItem
:link="`/${project.project_type}/${project.slug}/versions`"
:link="`/${project.project_type}/${project.slug ? project.slug : project.id}/versions`"
label="Versions"
chevron
>
<VersionIcon />
</NavStackItem>
<NavStackItem link="/dashboard/projects" label="All projects" chevron>
<SettingsIcon />
</NavStackItem>
</NavStack>
</aside>
</div>
@ -192,7 +206,17 @@
<h1 class="title">
{{ project.title }}
</h1>
<Badge v-if="$auth.user && currentMember" :type="project.status" class="status-badge" />
<nuxt-link
class="title-link project-type"
:to="`/${$getProjectTypeForUrl(project.actualProjectType, project.loaders)}s`"
>
<BoxIcon />
<span>{{
$formatProjectType(
$getProjectTypeForDisplay(project.actualProjectType, project.loaders)
)
}}</span>
</nuxt-link>
<p class="description">
{{ project.description }}
</p>
@ -201,6 +225,11 @@
:type="project.actualProjectType"
class="categories"
>
<Badge
v-if="$auth.user && currentMember"
:type="project.status"
class="status-badge"
/>
<EnvironmentIndicator
:client-side="project.client_side"
:server-side="project.server_side"
@ -442,7 +471,7 @@
<div class="featured-header">
<h2 class="card-header">Featured versions</h2>
<nuxt-link
v-if="versions.length > 0 || currentMember"
v-if="$route.name !== 'type-id-versions' && (versions.length > 0 || currentMember)"
:to="`/${project.project_type}/${
project.slug ? project.slug : project.id
}/versions#all-versions`"
@ -645,7 +674,7 @@
/>
<div v-if="$auth.user && currentMember" class="input-group">
<nuxt-link
:to="`/${project.project_type}/${project.slug}/settings`"
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/settings`"
class="iconified-button"
>
<SettingsIcon /> Settings
@ -685,6 +714,7 @@ import OpenCollectiveIcon from '~/assets/images/external/opencollective.svg'
import UnknownIcon from '~/assets/images/utils/unknown-donation.svg'
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg'
import EyeIcon from '~/assets/images/utils/eye.svg'
import BoxIcon from '~/assets/images/utils/box.svg'
import Promotion from '~/components/ads/Promotion'
import Badge from '~/components/ui/Badge'
import Categories from '~/components/ui/search/Categories'
@ -710,6 +740,7 @@ import CrossIcon from '~/assets/images/utils/x.svg'
import EditIcon from '~/assets/images/utils/edit.svg'
import ModerationIcon from '~/assets/images/sidebar/admin.svg'
import { renderString } from '~/helpers/parse'
import Breadcrumbs from '~/components/ui/Breadcrumbs.vue'
const data = useNuxtApp()
const route = useRoute()
@ -1049,8 +1080,21 @@ const collapsedChecklist = ref(false)
font-size: var(--font-size-xl);
}
.status-badge {
margin-top: var(--spacing-card-sm);
.project-type {
text-decoration: none;
font-weight: 500;
svg {
vertical-align: top;
margin-right: 0.25em;
}
&:hover,
&:focus-visible {
span {
text-decoration: underline;
}
}
}
.description {

View File

@ -62,6 +62,15 @@
</div>
</Modal>
<div class="version-page__title universal-card">
<Breadcrumbs
:current-title="version.name"
:link-stack="[
{
href: getPreviousLink(),
label: getPreviousLabel(),
},
]"
/>
<div class="version-header">
<template v-if="isEditing">
<input
@ -151,19 +160,6 @@
<DownloadIcon aria-hidden="true" />
Download
</a>
<nuxt-link
:to="
$router.options.history.state.back &&
($router.options.history.state.back.includes('changelog') ||
$router.options.history.state.back.includes('versions'))
? $router.options.history.state.back
: `/${project.project_type}/${project.slug ? project.slug : project.id}/versions`
"
class="iconified-button"
>
<BackIcon aria-hidden="true" />
Back to list
</nuxt-link>
<button
v-if="$auth.user && !currentMember"
class="iconified-button"
@ -533,19 +529,19 @@
:allow-empty="false"
/>
<template v-else>
<VersionBadge
<Badge
v-if="version.version_type === 'release'"
class="value"
type="release"
color="green"
/>
<VersionBadge
<Badge
v-else-if="version.version_type === 'beta'"
class="value"
type="beta"
color="orange"
/>
<VersionBadge
<Badge
v-else-if="version.version_type === 'alpha'"
class="value"
type="alpha"
@ -678,8 +674,9 @@ import { inferVersionInfo } from '~/helpers/infer'
import { createDataPackVersion } from '~/helpers/package'
import { renderHighlightedString } from '~/helpers/highlight'
import VersionBadge from '~/components/ui/Badge'
import Avatar from '~/components/ui/Avatar'
import Badge from '~/components/ui/Badge'
import Breadcrumbs from '~/components/ui/Breadcrumbs'
import CopyCode from '~/components/ui/CopyCode'
import Categories from '~/components/ui/search/Categories'
import ModalConfirm from '~/components/ui/ModalConfirm'
@ -704,12 +701,14 @@ import BackIcon from '~/assets/images/utils/left-arrow.svg'
import BoxIcon from '~/assets/images/utils/box.svg'
import RightArrowIcon from '~/assets/images/utils/right-arrow.svg'
import Modal from '~/components/ui/Modal.vue'
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg'
export default defineNuxtComponent({
components: {
Modal,
FileInput,
Checkbox,
ChevronRightIcon,
Chips,
Categories,
DownloadIcon,
@ -725,8 +724,9 @@ export default defineNuxtComponent({
TransferIcon,
UploadIcon,
BackIcon,
VersionBadge,
Avatar,
Badge,
Breadcrumbs,
CopyCode,
ModalConfirm,
ModalReport,
@ -961,6 +961,25 @@ export default defineNuxtComponent({
},
},
methods: {
getPreviousLink() {
if (this.$router.options.history.state.back) {
if (
this.$router.options.history.state.back.includes('/changelog') ||
this.$router.options.history.state.back.includes('/versions')
) {
return this.$router.options.history.state.back
}
}
return `/${this.project.project_type}/${
this.project.slug ? this.project.slug : this.project.id
}/versions`
},
getPreviousLabel() {
return this.$router.options.history.state.back &&
this.$router.options.history.state.back.endsWith('/changelog')
? 'Changelog'
: 'Versions'
},
acceptFileFromProjectType,
renderHighlightedString,
async addDependency(dependencyAddMode, newDependencyId, newDependencyType, hideErrors) {

View File

@ -232,7 +232,7 @@
<nuxt-link
tabindex="-1"
:to="`/${$getProjectTypeForUrl(project.project_type, project.loaders)}/${
project.slug
project.slug ? project.slug : project.id
}`"
>
<Avatar
@ -254,7 +254,7 @@
<nuxt-link
class="hover-link wrap-as-needed"
:to="`/${$getProjectTypeForUrl(project.project_type, project.loaders)}/${
project.slug
project.slug ? project.slug : project.id
}`"
>
{{ project.title }}
@ -278,7 +278,7 @@
<nuxt-link
class="square-button"
:to="`/${$getProjectTypeForUrl(project.project_type, project.loaders)}/${
project.slug
project.slug ? project.slug : project.id
}/settings`"
>
<SettingsIcon />

View File

@ -26,6 +26,9 @@
<TransferIcon /> Transfer to
{{ $formatWallet(auth.user.payout_data.payout_wallet) }}
</button>
<NuxtLink class="iconified-button" to="/dashboard/revenue/transfers">
<HistoryIcon /> View transfer history
</NuxtLink>
<NuxtLink class="iconified-button" to="/settings/monetization">
<SettingsIcon /> Monetization settings
</NuxtLink>
@ -90,10 +93,11 @@
<script>
import TransferIcon from '~/assets/images/utils/transfer.svg'
import SettingsIcon from '~/assets/images/utils/settings.svg'
import HistoryIcon from '~/assets/images/utils/history.svg'
import ModalTransfer from '~/components/ui/ModalTransfer'
export default defineNuxtComponent({
components: { TransferIcon, SettingsIcon, ModalTransfer },
components: { TransferIcon, SettingsIcon, HistoryIcon, ModalTransfer },
async setup() {
const auth = await useAuth()

View File

@ -0,0 +1,138 @@
<template>
<div>
<section class="universal-card payout-history">
<Breadcrumbs
current-title="Transfer history"
:link-stack="[{ href: '/dashboard/revenue', label: 'Revenue' }]"
/>
<h2>Transfer history</h2>
<p>
All of your transfers from your Modrinth balance to your PayPal or Venmo accounts will be
listed here:
</p>
<div class="grid-table">
<div class="grid-table__row grid-table__header">
<div class="desktop">Date</div>
<div class="desktop">Status</div>
<div class="desktop">Amount</div>
<div class="mobile">Transaction</div>
</div>
<div
v-for="(payout, index) in payouts.payouts.filter((x) => x.status === 'success')"
:key="`payout-${index}`"
class="grid-table__row"
>
<div>{{ $dayjs(payout.created).format('MMMM D, YYYY [at] h:mm A') }}</div>
<div><Badge :type="payout.status" /></div>
<div class="amount">{{ $formatMoney(payout.amount) }}</div>
</div>
</div>
</section>
</div>
</template>
<script setup>
import Badge from '~/components/ui/Badge.vue'
import Breadcrumbs from '~/components/ui/Breadcrumbs.vue'
useHead({
title: 'Transfer history - Modrinth',
})
const auth = await useAuth()
const app = useNuxtApp()
const [raw] = await Promise.all([
useBaseFetch(`user/${auth.value.user.id}/payouts`, app.$defaultHeaders()),
])
const payouts = ref(raw)
</script>
<style lang="scss" scoped>
.grid-table {
display: grid;
grid-template-columns: auto auto auto;
border-radius: var(--size-rounded-sm);
overflow: hidden;
margin-top: var(--spacing-card-md);
.grid-table__header {
.mobile {
display: none;
}
}
.grid-table__row {
display: contents;
> div {
display: flex;
flex-direction: column;
justify-content: center;
padding: var(--spacing-card-sm);
// Left edge of table
&:first-child,
&.mobile {
padding-left: var(--spacing-card-bg);
}
// Right edge of table
&:last-child {
padding-right: var(--spacing-card-bg);
}
}
&:nth-child(2n + 1) > div {
background-color: var(--color-table-alternate-row);
}
> div {
padding-top: var(--spacing-card-bg);
padding-bottom: var(--spacing-card-bg);
}
&.grid-table__header > div {
background-color: var(--color-bg);
font-weight: bold;
color: var(--color-text-dark);
}
}
@media screen and (max-width: 560px) {
display: flex;
flex-direction: column;
.grid-table__row {
display: flex;
flex-direction: column;
> div {
padding: var(--spacing-card-xs) var(--spacing-card-bg);
&:first-child,
&.mobile {
padding-top: var(--spacing-card-bg);
}
&:last-child,
&.mobile {
padding-bottom: var(--spacing-card-bg);
}
}
}
.grid-table__header {
.mobile {
display: flex;
}
.desktop {
display: none;
}
}
}
.amount {
color: var(--color-heading);
font-weight: 500;
}
}
</style>

View File

@ -44,7 +44,7 @@
v-for="project in row"
:key="project.id"
class="project button-animation"
:to="`/${project.project_type}/${project.slug}`"
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}`"
>
<Avatar :src="project.icon_url" :alt="project.title" size="sm" loading="lazy" />
<div class="project-info">