* Projects page * Continue work on bulk edit * editLinks is now bulkEdit * Bulk Edit Links completed * Edit URL clear fields. * Create project button + other bulk buttons. * Pagination (w/o reactivity.) * Apply suggestions from code review Co-authored-by: triphora <emmaffle@modrinth.com> * Sorting fixed, broken page count though? * Only make editable projects selectable + remove delete button * Shorthand * Start using computed * Fix pagination * Add Pagination Switching * Final Style Changes * Cleanup * Action Affects dropdown * Switch to checkbox swizzle * Projects dashboard, the most hellish thing I have ever worked on * Rewrite project dashboard without tables * why's that there * Fix mod message icon * New project settings page * Remove extra slash * Bulk project route and improve styling of links UI * Remove beta label from Monetization * Relevant page links in project settings * Don't vertically center header rows * Improve error messages, add remove project icon button, add saving feedback, begin project checklist, fix license settings * Remove contextual link from project settings, disable WIP checklist * Fix bulk edit * Project checklist, add featured gallery image to project pages, fix random bugs * Remove old check * Remove icon border on grid mode and hide project status card when unnecessary * Fix build * Make checklist progress smaller and add collapsing * Remove uneven gap on nav cards * Improve wrapping of checklist * Replace project settings header link with status * Fix bugs + status stuff * Fix warns + compile error * Update wording * Hide environment type nag for project types without it * Make member dropdown match Co-authored-by: mineblock11 <93472213+mineblock11@users.noreply.github.com> Co-authored-by: triphora <emmaffle@modrinth.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me> Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com>
441 lines
11 KiB
Vue
441 lines
11 KiB
Vue
<template>
|
|
<div
|
|
v-if="
|
|
$auth.user &&
|
|
currentMember &&
|
|
nags.filter((x) => x.condition).length > 0 &&
|
|
project.status === 'draft'
|
|
"
|
|
class="author-actions universal-card"
|
|
>
|
|
<div class="header__row">
|
|
<div class="header__title">
|
|
<h2>Publishing checklist</h2>
|
|
<div class="checklist">
|
|
<span class="checklist__title">Progress:</span>
|
|
<div class="checklist__items">
|
|
<div
|
|
v-for="nag in nags"
|
|
:key="`checklist-${nag.id}`"
|
|
v-tooltip="nag.title"
|
|
class="circle"
|
|
:class="'circle ' + (!nag.condition ? 'done ' : '') + nag.status"
|
|
>
|
|
<CheckIcon v-if="!nag.condition" />
|
|
<RequiredIcon v-else-if="nag.status === 'required'" />
|
|
<SuggestionIcon v-else-if="nag.status === 'suggestion'" />
|
|
<ModerationIcon v-else-if="nag.status === 'review'" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="input-group">
|
|
<button
|
|
class="square-button"
|
|
:class="{ 'not-collapsed': !collapsed }"
|
|
@click="toggleCollapsed()"
|
|
>
|
|
<DropdownIcon />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div v-if="!collapsed" class="grid-display width-16">
|
|
<div
|
|
v-for="nag in nags.filter((x) => x.condition)"
|
|
:key="nag.id"
|
|
class="grid-display__item"
|
|
>
|
|
<span class="label">
|
|
<RequiredIcon
|
|
v-if="nag.status === 'required'"
|
|
v-tooltip="'Required'"
|
|
:class="nag.status"
|
|
/>
|
|
<SuggestionIcon
|
|
v-else-if="nag.status === 'suggestion'"
|
|
v-tooltip="'Suggestion'"
|
|
:class="nag.status"
|
|
/>
|
|
<ModerationIcon
|
|
v-else-if="nag.status === 'review'"
|
|
v-tooltip="'Review'"
|
|
:class="nag.status"
|
|
/>{{ nag.title }}</span
|
|
>
|
|
{{ nag.description }}
|
|
<NuxtLink
|
|
v-if="nag.link"
|
|
:class="{ invisible: nag.link.hide }"
|
|
class="goto-link"
|
|
:to="`/${project.project_type}/${
|
|
project.slug ? project.slug : project.id
|
|
}/${nag.link.path}`"
|
|
>
|
|
{{ nag.link.title }}
|
|
<ChevronRightIcon
|
|
class="featured-header-chevron"
|
|
aria-hidden="true"
|
|
/>
|
|
</NuxtLink>
|
|
<button
|
|
v-else-if="nag.action"
|
|
class="iconified-button moderation-button"
|
|
:disabled="nag.action.disabled()"
|
|
@click="nag.action.onClick"
|
|
>
|
|
<SendIcon />
|
|
{{ nag.action.title }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import ChevronRightIcon from '~/assets/images/utils/chevron-right.svg?inline'
|
|
import DropdownIcon from '~/assets/images/utils/dropdown.svg?inline'
|
|
import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
|
import RequiredIcon from '~/assets/images/utils/asterisk.svg?inline'
|
|
import SuggestionIcon from '~/assets/images/utils/lightbulb.svg?inline'
|
|
import ModerationIcon from '~/assets/images/sidebar/admin.svg?inline'
|
|
import SendIcon from '~/assets/images/utils/send.svg?inline'
|
|
|
|
export default {
|
|
name: 'ProjectPublishingChecklist',
|
|
components: {
|
|
ChevronRightIcon,
|
|
DropdownIcon,
|
|
CheckIcon,
|
|
RequiredIcon,
|
|
SuggestionIcon,
|
|
ModerationIcon,
|
|
SendIcon,
|
|
},
|
|
props: {
|
|
project: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
versions: {
|
|
type: Array,
|
|
required: true,
|
|
},
|
|
currentMember: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
isSettings: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
collapsed: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
routeName: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
setProcessing: {
|
|
type: Function,
|
|
default() {
|
|
return () => {
|
|
this.$notify({
|
|
group: 'main',
|
|
title: 'An error occurred',
|
|
text: 'setProcessing function not found',
|
|
type: 'error',
|
|
})
|
|
}
|
|
},
|
|
},
|
|
toggleCollapsed: {
|
|
type: Function,
|
|
default() {
|
|
return () => {
|
|
this.$notify({
|
|
group: 'main',
|
|
title: 'An error occurred',
|
|
text: 'toggleCollapsed function not found',
|
|
type: 'error',
|
|
})
|
|
}
|
|
},
|
|
},
|
|
},
|
|
computed: {
|
|
featuredGalleryImage() {
|
|
return this.project.gallery.find((img) => img.featured)
|
|
},
|
|
nags() {
|
|
return [
|
|
{
|
|
condition:
|
|
this.project.body === '' ||
|
|
this.project.body.startsWith('# Placeholder description'),
|
|
title: 'Add a description',
|
|
id: 'add-description',
|
|
description:
|
|
"A description that clearly describes the project's purpose and function is required.",
|
|
status: 'required',
|
|
link: {
|
|
path: 'settings/description',
|
|
title: 'Visit description settings',
|
|
hide: this.routeName === 'type-id-settings-description',
|
|
},
|
|
},
|
|
{
|
|
condition: !this.project.icon_url,
|
|
title: 'Add an icon',
|
|
id: 'add-icon',
|
|
description:
|
|
'Your project should have a nice-looking icon to uniquely identify your project at a glance.',
|
|
status: 'suggestion',
|
|
link: {
|
|
path: 'settings',
|
|
title: 'Visit general settings',
|
|
hide: this.routeName === 'type-id-settings',
|
|
},
|
|
},
|
|
{
|
|
condition: !this.featuredGalleryImage,
|
|
title: 'Feature a gallery image',
|
|
id: 'feature-gallery-image',
|
|
description:
|
|
'Featured gallery images may be the first impression of many users.',
|
|
status: 'suggestion',
|
|
link: {
|
|
path: 'gallery',
|
|
title: 'Visit gallery page',
|
|
hide: this.routeName === 'type-id-gallery',
|
|
},
|
|
},
|
|
{
|
|
condition: this.versions.length < 1,
|
|
title: 'Upload a version',
|
|
id: 'upload-version',
|
|
description:
|
|
'At least one version is required for a project to be submitted for review.',
|
|
status: 'required',
|
|
link: {
|
|
path: 'versions',
|
|
title: 'Visit versions page',
|
|
hide: this.routeName === 'type-id-versions',
|
|
},
|
|
},
|
|
{
|
|
condition: this.project.categories.length < 1,
|
|
title: 'Select tags',
|
|
id: 'select-tags',
|
|
description: 'Select all tags that apply to your project.',
|
|
status: 'suggestion',
|
|
link: {
|
|
path: 'settings/tags',
|
|
title: 'Visit tag settings',
|
|
hide: this.routeName === 'type-id-settings-tags',
|
|
},
|
|
},
|
|
{
|
|
hide:
|
|
this.project.project_type === 'resourcepack' ||
|
|
this.project.project_type === 'plugin' ||
|
|
this.project.project_type === 'shader' ||
|
|
this.project.project_type === 'datapack',
|
|
condition:
|
|
this.project.client_side === 'unknown' ||
|
|
this.project.server_side === 'unknown',
|
|
title: 'Select supported environments',
|
|
id: 'select-environments',
|
|
description: `Select if the ${this.$formatProjectType(
|
|
this.project.project_type
|
|
).toLowerCase()} functions on the client-side and/or server-side.`,
|
|
status: 'required',
|
|
link: {
|
|
path: 'settings',
|
|
title: 'Visit general settings',
|
|
hide: this.routeName === 'type-id-settings',
|
|
},
|
|
},
|
|
{
|
|
condition: this.project.status === 'draft',
|
|
title: 'Submit for review',
|
|
id: 'submit-for-review',
|
|
description:
|
|
'Your project is only viewable by members of the project. It must be reviewed by moderators in order to be published.',
|
|
status: 'review',
|
|
link: null,
|
|
action: {
|
|
onClick: this.submitForReview,
|
|
title: 'Submit for review',
|
|
disabled: () =>
|
|
this.nags.filter((x) => x.condition && x.status === 'required')
|
|
.length > 0,
|
|
},
|
|
},
|
|
]
|
|
.filter((x) => !x.hide)
|
|
.sort((a, b) =>
|
|
this.sortByTrue(
|
|
!a.condition,
|
|
!b.condition,
|
|
this.sortByTrue(
|
|
a.status === 'required',
|
|
b.status === 'required',
|
|
this.sortByFalse(a.status === 'review', b.status === 'review')
|
|
)
|
|
)
|
|
)
|
|
},
|
|
},
|
|
methods: {
|
|
sortByTrue(a, b, ifEqual = 0) {
|
|
if (a === b) {
|
|
return ifEqual
|
|
} else if (a) {
|
|
return -1
|
|
} else {
|
|
return 1
|
|
}
|
|
},
|
|
sortByFalse(a, b, ifEqual = 0) {
|
|
if (a === b) {
|
|
return ifEqual
|
|
} else if (b) {
|
|
return -1
|
|
} else {
|
|
return 1
|
|
}
|
|
},
|
|
async submitForReview() {
|
|
if (
|
|
this.nags.filter((x) => x.condition && x.status === 'required')
|
|
.length === 0
|
|
) {
|
|
await this.setProcessing()
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.author-actions {
|
|
&:empty {
|
|
display: none;
|
|
}
|
|
|
|
.invisible {
|
|
visibility: hidden;
|
|
}
|
|
|
|
.header__row {
|
|
align-items: center;
|
|
column-gap: var(--spacing-card-lg);
|
|
row-gap: var(--spacing-card-md);
|
|
max-width: 100%;
|
|
|
|
.header__title {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
column-gap: var(--spacing-card-lg);
|
|
row-gap: var(--spacing-card-md);
|
|
flex-basis: min-content;
|
|
|
|
h2 {
|
|
margin: 0 auto 0 0;
|
|
}
|
|
}
|
|
|
|
button {
|
|
svg {
|
|
transition: transform 0.25s ease-in-out;
|
|
}
|
|
|
|
&.not-collapsed svg {
|
|
transform: rotate(180deg);
|
|
}
|
|
}
|
|
}
|
|
|
|
.grid-display__item .label {
|
|
display: flex;
|
|
gap: var(--spacing-card-xs);
|
|
align-items: center;
|
|
|
|
.required {
|
|
color: var(--color-special-red);
|
|
}
|
|
|
|
.suggestion {
|
|
color: var(--color-special-purple);
|
|
}
|
|
|
|
.review {
|
|
color: var(--color-special-orange);
|
|
}
|
|
}
|
|
|
|
.checklist {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: var(--spacing-card-xs);
|
|
width: fit-content;
|
|
flex-wrap: wrap;
|
|
max-width: 100%;
|
|
|
|
.checklist__title {
|
|
font-weight: bold;
|
|
margin-right: var(--spacing-card-xs);
|
|
color: var(--color-text-dark);
|
|
}
|
|
|
|
.checklist__items {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: var(--spacing-card-xs);
|
|
width: fit-content;
|
|
max-width: 100%;
|
|
}
|
|
|
|
.circle {
|
|
--circle-size: 2rem;
|
|
--background-color: var(--color-bg);
|
|
--content-color: var(--color-special-gray);
|
|
width: var(--circle-size);
|
|
height: var(--circle-size);
|
|
border-radius: 50%;
|
|
background-color: var(--background-color);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
|
|
svg {
|
|
color: var(--content-color);
|
|
width: calc(var(--circle-size) / 2);
|
|
height: calc(var(--circle-size) / 2);
|
|
}
|
|
|
|
&.required {
|
|
--content-color: var(--color-special-red);
|
|
}
|
|
|
|
&.suggestion {
|
|
--content-color: var(--color-special-purple);
|
|
}
|
|
|
|
&.review {
|
|
--content-color: var(--color-special-orange);
|
|
}
|
|
|
|
&.done {
|
|
--background-color: var(--color-special-green);
|
|
--content-color: var(--color-brand-inverted);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|