* Make project cards right-align their last element Spaces out elements in a `.project-card` using `justify-content: space-between;`. Fixes modrinth/knossos#170 * Automatically set URL for auth redirect * Make login button use base url or current origin Allows the login button to work in dev environment * Remove Axios base URL trailing slash * Update authUrl() on dashboard to match default * Remove 'code' query from URL on page load Allow non-exact paths to highlight mod & dashboard tabs Fixes modrinth/knossos#200 * Make page 5 button visible on page 4 (pagination) Fixes modrinth/knossos#184 * Color links on legal pages Fixes modrinth/knossos#166 * Set max notifications to 5 and ignore duplicates Fixes modrinth/knossos#175 * Add space above report button when no user desc Fixes modrinth/knossos#143 * Better text spacing from edge of mobile screen Fixes modrinth/knossos#179 * Fix slanted bars in modrinth/knossos#57 * Fix checkbox grid and role label Fixes modrinth/knossos#191 * Move mod 'settings' button to the far right Fixes modrinth/knossos#138 * Abbreviate minutes to min. when time is too long Not a perfect solution imo, but works for now Fixes modrinth/knossos#193 * Fix mobile header margins & add breakpoints Fixes modrinth/knossos#203 * Clean up nuxt config Silence babel warning & styleResources * Upgrade sass-loader to 10.1.1 and remove warning * Remove added horizontal footer padding https://github.com/modrinth/knossos/pull/199#discussion_r629011624 * Improve mobile header fix * Fix up minor inconsistencies in mod header * Remove hard coded date * Cleans up pagination to be more intuitive * Fixes member invite input on moble * Fix login button when searching mods * Improved mobile mod search Consistently sized pagination buttons Breakpoint for sort buttons on smaller screens * Consistent link style on text-only pages * Better 4k support * Slightly better mobile project-card support Shuffles categories under mod icon when there is room * Animate homepage typewriter effect backwards * Tiny commit to align mod icons in mod headers * Make processing status include 'Under Review' This can be later updated once the backend has a separate status * Create vercel.json * Update domain auto detection * Test vercel NODE_ENV * Remove console.log for debugging hosting services * Make mobile first + fix shrinked text circle size * Optimize SVG * Change media queries to be more mobile first * Remove `|| window.location.origin` * re-deploy vercel * Change "Processing" message to "Under review"
308 lines
7.3 KiB
Vue
308 lines
7.3 KiB
Vue
<template>
|
|
<article class="project-card">
|
|
<div class="columns">
|
|
<div class="icon">
|
|
<nuxt-link v-if="isModrinth" :to="'/mod/' + id">
|
|
<img
|
|
:src="iconUrl || 'https://cdn.modrinth.com/placeholder.svg?inline'"
|
|
:alt="name"
|
|
loading="lazy"
|
|
/>
|
|
</nuxt-link>
|
|
<Categories :categories="categories" class="left-categories" />
|
|
</div>
|
|
<div class="info">
|
|
<div class="top">
|
|
<h2 class="title">
|
|
<nuxt-link v-if="isModrinth" :to="'/mod/' + id">{{
|
|
name
|
|
}}</nuxt-link>
|
|
<a v-else :href="pageUrl">{{ name }}</a>
|
|
</h2>
|
|
<p v-if="author" class="author">
|
|
by <nuxt-link :to="'/user/' + author">{{ author }}</nuxt-link>
|
|
</p>
|
|
</div>
|
|
<p class="description">
|
|
{{ description }}
|
|
</p>
|
|
<div :class="{ vertical: editMode }" class="bottom">
|
|
<div class="stats">
|
|
<div v-if="status !== null" class="stat">
|
|
<div class="info">
|
|
<h4>Status</h4>
|
|
<span v-if="status === 'approved'" class="badge green">
|
|
Approved
|
|
</span>
|
|
<span v-if="status === 'rejected'" class="badge red">
|
|
Rejected
|
|
</span>
|
|
<span v-if="status === 'draft'" class="badge yellow"
|
|
>Draft</span
|
|
>
|
|
<span v-if="status === 'processing'" class="badge yellow">
|
|
Under review
|
|
</span>
|
|
<span v-if="status === 'unlisted'" class="badge gray">
|
|
Unlisted
|
|
</span>
|
|
<span v-if="status === 'unknown'" class="badge gray">
|
|
Unknown
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat">
|
|
<DownloadIcon aria-hidden="true" />
|
|
<div class="info">
|
|
<h4>Downloads</h4>
|
|
<p class="value">{{ formatNumber(downloads) }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="stat">
|
|
<CalendarIcon aria-hidden="true" />
|
|
<div class="info">
|
|
<h4>Created</h4>
|
|
<p
|
|
v-tooltip="
|
|
$dayjs(createdAt).format(
|
|
'[Created on] YYYY-MM-DD [at] HH:mm A'
|
|
)
|
|
"
|
|
class="value"
|
|
>
|
|
{{ $dayjs(createdAt).fromNow() }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="stat">
|
|
<EditIcon aria-hidden="true" />
|
|
<div class="info">
|
|
<h4>Updated</h4>
|
|
<p
|
|
v-tooltip="
|
|
$dayjs(updatedAt).format(
|
|
'[Updated on] YYYY-MM-DD [at] HH:mm A'
|
|
)
|
|
"
|
|
class="value"
|
|
>
|
|
{{ $dayjs(updatedAt).fromNow() }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div v-if="latestVersion" class="stat">
|
|
<TagIcon aria-hidden="true" />
|
|
<div class="info">
|
|
<h4>Available For</h4>
|
|
<p class="value">
|
|
{{ latestVersion }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Categories :categories="categories" class="right-categories" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="editMode" class="buttons">
|
|
<slot />
|
|
</div>
|
|
</article>
|
|
</template>
|
|
|
|
<script>
|
|
import Categories from '~/components/ui/search/Categories'
|
|
|
|
import CalendarIcon from '~/assets/images/utils/calendar.svg?inline'
|
|
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
|
import EditIcon from '~/assets/images/utils/edit.svg?inline'
|
|
import TagIcon from '~/assets/images/utils/tag.svg?inline'
|
|
|
|
export default {
|
|
name: 'ProjectCard',
|
|
components: {
|
|
Categories,
|
|
CalendarIcon,
|
|
DownloadIcon,
|
|
EditIcon,
|
|
TagIcon,
|
|
},
|
|
props: {
|
|
id: {
|
|
type: String,
|
|
default: 'modrinth-0',
|
|
},
|
|
name: {
|
|
type: String,
|
|
default: 'Mod Name',
|
|
},
|
|
author: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
description: {
|
|
type: String,
|
|
default: 'A mod description',
|
|
},
|
|
pageUrl: {
|
|
type: String,
|
|
default: '#',
|
|
},
|
|
authorUrl: {
|
|
type: String,
|
|
default: '#',
|
|
},
|
|
iconUrl: {
|
|
type: String,
|
|
default: '#',
|
|
},
|
|
downloads: {
|
|
type: String,
|
|
default: '0',
|
|
},
|
|
createdAt: {
|
|
type: String,
|
|
default: '0000-00-00',
|
|
},
|
|
updatedAt: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
latestVersion: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
categories: {
|
|
type: Array,
|
|
default() {
|
|
return []
|
|
},
|
|
},
|
|
editMode: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
status: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
isModrinth: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
methods: {
|
|
formatNumber(x) {
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.project-card {
|
|
@extend %row;
|
|
@extend %card-spaced-b;
|
|
width: 100%;
|
|
flex-direction: column;
|
|
|
|
@media screen and (min-width: 1024px) {
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.icon {
|
|
margin: auto 0;
|
|
img {
|
|
width: 6rem;
|
|
height: 6rem;
|
|
margin: var(--spacing-card-md);
|
|
border-radius: var(--size-rounded-icon);
|
|
object-fit: contain;
|
|
}
|
|
}
|
|
.info {
|
|
@extend %column;
|
|
flex-grow: 1;
|
|
.top {
|
|
@extend %row;
|
|
flex-wrap: wrap;
|
|
flex-shrink: 0;
|
|
margin-top: var(--spacing-card-md);
|
|
margin-right: var(--spacing-card-md);
|
|
.title {
|
|
margin: 0;
|
|
color: var(--color-text-dark);
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
.author {
|
|
margin: auto 0 0 0.5rem;
|
|
color: var(--color-text);
|
|
}
|
|
}
|
|
.description {
|
|
margin: var(--spacing-card-sm) var(--spacing-card-md) 0 0;
|
|
height: 100%;
|
|
color: var(--color-text-dark);
|
|
}
|
|
.bottom {
|
|
@extend %column;
|
|
flex-shrink: 0;
|
|
margin-top: var(--spacing-card-sm);
|
|
margin-right: var(--spacing-card-md);
|
|
margin-bottom: var(--spacing-card-md);
|
|
|
|
@media screen and (min-width: 1024px) {
|
|
flex-direction: row;
|
|
&.vertical {
|
|
flex-direction: column;
|
|
.categories {
|
|
margin-top: var(--spacing-card-sm);
|
|
}
|
|
}
|
|
}
|
|
|
|
.stats {
|
|
@extend %row;
|
|
flex-wrap: wrap;
|
|
|
|
@media screen and (min-width: 900px) {
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
.stat {
|
|
@extend %stat;
|
|
}
|
|
}
|
|
.categories {
|
|
@media screen and (min-width: 1024px) {
|
|
flex-direction: row;
|
|
margin: auto 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.left-categories {
|
|
display: none;
|
|
}
|
|
@media screen and (max-width: 560px) {
|
|
.left-categories {
|
|
display: flex;
|
|
margin: 0 0 0.75rem 0.75rem;
|
|
width: 7rem;
|
|
}
|
|
.right-categories {
|
|
display: none;
|
|
}
|
|
}
|
|
.buttons {
|
|
@extend %column;
|
|
margin-bottom: 1rem;
|
|
|
|
@media screen and (min-width: 1024px) {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
}
|
|
</style>
|