You've already forked AstralRinth
forked from didirus/AstralRinth
refactor: migrate to common eslint+prettier configs (#4168)
* refactor: migrate to common eslint+prettier configs * fix: prettier frontend * feat: config changes * fix: lint issues * fix: lint * fix: type imports * fix: cyclical import issue * fix: lockfile * fix: missing dep * fix: switch to tabs * fix: continue switch to tabs * fix: rustfmt parity * fix: moderation lint issue * fix: lint issues * fix: ui intl * fix: lint issues * Revert "fix: rustfmt parity" This reverts commit cb99d2376c321d813d4b7fc7e2a213bb30a54711. * feat: revert last rs
This commit is contained in:
@@ -1,478 +1,480 @@
|
||||
<template>
|
||||
<div v-if="showInvitation" class="universal-card information invited my-4">
|
||||
<h2>{{ getFormattedMessage(messages.invitationTitle) }}</h2>
|
||||
<p v-if="currentMember?.project_role">
|
||||
{{ formatMessage(messages.invitationWithRole, { role: currentMember.project_role }) }}
|
||||
</p>
|
||||
<p v-else>{{ getFormattedMessage(messages.invitationNoRole) }}</p>
|
||||
<div class="input-group">
|
||||
<ButtonStyled color="brand">
|
||||
<button class="brand-button" @click="acceptInvite()">
|
||||
<CheckIcon />
|
||||
{{ getFormattedMessage(messages.accept) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="declineInvite">
|
||||
<XIcon />
|
||||
{{ getFormattedMessage(messages.decline) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
currentMember &&
|
||||
visibleNags.length > 0 &&
|
||||
(project.status === 'draft' || tags.rejectedStatuses.includes(project.status))
|
||||
"
|
||||
class="universal-card my-4"
|
||||
>
|
||||
<div class="flex max-w-full flex-wrap items-center gap-x-6 gap-y-4">
|
||||
<div class="flex flex-auto flex-wrap items-center gap-x-6 gap-y-4">
|
||||
<h2 class="my-0 mr-auto">{{ getFormattedMessage(messages.publishingChecklist) }}</h2>
|
||||
<div class="flex flex-row gap-2">
|
||||
<div class="flex items-center gap-1">
|
||||
<AsteriskIcon class="size-4 text-red" />
|
||||
<span class="text-secondary">{{ getFormattedMessage(messages.required) }}</span>
|
||||
</div>
|
||||
|
|
||||
<div class="flex items-center gap-1">
|
||||
<TriangleAlertIcon class="size-4 text-orange" />
|
||||
<span class="text-secondary">{{ getFormattedMessage(messages.warning) }}</span>
|
||||
</div>
|
||||
|
|
||||
<div class="flex items-center gap-1">
|
||||
<LightBulbIcon class="size-4 text-purple" />
|
||||
<span class="text-secondary">{{ getFormattedMessage(messages.suggestion) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<ButtonStyled circular>
|
||||
<button :class="!collapsed && '[&>svg]:rotate-180'" @click="toggleCollapsed()">
|
||||
<DropdownIcon class="duration-250 transition-transform ease-in-out" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!collapsed" class="grid-display width-16 mt-4">
|
||||
<div v-for="nag in visibleNags" :key="nag.id" class="grid-display__item">
|
||||
<span class="flex items-center gap-2 font-semibold">
|
||||
<component
|
||||
:is="nag.icon || getDefaultIcon(nag.status)"
|
||||
v-tooltip="getStatusTooltip(nag.status)"
|
||||
:class="[
|
||||
'size-4',
|
||||
nag.status === 'required' && 'text-red',
|
||||
nag.status === 'warning' && 'text-orange',
|
||||
nag.status === 'suggestion' && 'text-purple',
|
||||
]"
|
||||
:aria-label="getStatusTooltip(nag.status)"
|
||||
/>
|
||||
{{ getFormattedMessage(nag.title) }}
|
||||
</span>
|
||||
{{ getNagDescription(nag) }}
|
||||
<NuxtLink
|
||||
v-if="nag.link && shouldShowLink(nag)"
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/${
|
||||
nag.link.path
|
||||
}`"
|
||||
class="goto-link"
|
||||
>
|
||||
{{ getFormattedMessage(nag.link.title) }}
|
||||
<ChevronRightIcon aria-hidden="true" class="featured-header-chevron" />
|
||||
</NuxtLink>
|
||||
<ButtonStyled
|
||||
v-if="nag.status === 'special-submit-action' && nag.id === 'submit-for-review'"
|
||||
color="orange"
|
||||
@click="submitForReview"
|
||||
>
|
||||
<button
|
||||
:disabled="!canSubmitForReview"
|
||||
v-tooltip="
|
||||
!canSubmitForReview ? getFormattedMessage(messages.submitChecklistTooltip) : undefined
|
||||
"
|
||||
>
|
||||
<SendIcon />
|
||||
{{ getFormattedMessage(messages.submitForReview) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showInvitation" class="universal-card information invited my-4">
|
||||
<h2>{{ getFormattedMessage(messages.invitationTitle) }}</h2>
|
||||
<p v-if="currentMember?.project_role">
|
||||
{{ formatMessage(messages.invitationWithRole, { role: currentMember.project_role }) }}
|
||||
</p>
|
||||
<p v-else>{{ getFormattedMessage(messages.invitationNoRole) }}</p>
|
||||
<div class="input-group">
|
||||
<ButtonStyled color="brand">
|
||||
<button class="brand-button" @click="acceptInvite()">
|
||||
<CheckIcon />
|
||||
{{ getFormattedMessage(messages.accept) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="declineInvite">
|
||||
<XIcon />
|
||||
{{ getFormattedMessage(messages.decline) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
currentMember &&
|
||||
visibleNags.length > 0 &&
|
||||
(project.status === 'draft' || tags.rejectedStatuses.includes(project.status))
|
||||
"
|
||||
class="universal-card my-4"
|
||||
>
|
||||
<div class="flex max-w-full flex-wrap items-center gap-x-6 gap-y-4">
|
||||
<div class="flex flex-auto flex-wrap items-center gap-x-6 gap-y-4">
|
||||
<h2 class="my-0 mr-auto">
|
||||
{{ getFormattedMessage(messages.publishingChecklist) }}
|
||||
</h2>
|
||||
<div class="flex flex-row gap-2">
|
||||
<div class="flex items-center gap-1">
|
||||
<AsteriskIcon class="size-4 text-red" />
|
||||
<span class="text-secondary">{{ getFormattedMessage(messages.required) }}</span>
|
||||
</div>
|
||||
|
|
||||
<div class="flex items-center gap-1">
|
||||
<TriangleAlertIcon class="size-4 text-orange" />
|
||||
<span class="text-secondary">{{ getFormattedMessage(messages.warning) }}</span>
|
||||
</div>
|
||||
|
|
||||
<div class="flex items-center gap-1">
|
||||
<LightBulbIcon class="size-4 text-purple" />
|
||||
<span class="text-secondary">{{ getFormattedMessage(messages.suggestion) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<ButtonStyled circular>
|
||||
<button :class="!collapsed && '[&>svg]:rotate-180'" @click="handleToggleCollapsed()">
|
||||
<DropdownIcon class="duration-250 transition-transform ease-in-out" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!collapsed" class="grid-display width-16 mt-4">
|
||||
<div v-for="nag in visibleNags" :key="nag.id" class="grid-display__item">
|
||||
<span class="flex items-center gap-2 font-semibold">
|
||||
<component
|
||||
:is="nag.icon || getDefaultIcon(nag.status)"
|
||||
v-tooltip="getStatusTooltip(nag.status)"
|
||||
:class="[
|
||||
'size-4',
|
||||
nag.status === 'required' && 'text-red',
|
||||
nag.status === 'warning' && 'text-orange',
|
||||
nag.status === 'suggestion' && 'text-purple',
|
||||
]"
|
||||
:aria-label="getStatusTooltip(nag.status)"
|
||||
/>
|
||||
{{ getFormattedMessage(nag.title) }}
|
||||
</span>
|
||||
{{ getNagDescription(nag) }}
|
||||
<NuxtLink
|
||||
v-if="nag.link && shouldShowLink(nag)"
|
||||
:to="`/${project.project_type}/${project.slug ? project.slug : project.id}/${
|
||||
nag.link.path
|
||||
}`"
|
||||
class="goto-link"
|
||||
>
|
||||
{{ getFormattedMessage(nag.link.title) }}
|
||||
<ChevronRightIcon aria-hidden="true" class="featured-header-chevron" />
|
||||
</NuxtLink>
|
||||
<ButtonStyled
|
||||
v-if="nag.status === 'special-submit-action' && nag.id === 'submit-for-review'"
|
||||
color="orange"
|
||||
@click="submitForReview"
|
||||
>
|
||||
<button
|
||||
v-tooltip="
|
||||
!canSubmitForReview ? getFormattedMessage(messages.submitChecklistTooltip) : undefined
|
||||
"
|
||||
:disabled="!canSubmitForReview"
|
||||
>
|
||||
<SendIcon />
|
||||
{{ getFormattedMessage(messages.submitForReview) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
AsteriskIcon,
|
||||
CheckIcon,
|
||||
ChevronRightIcon,
|
||||
DropdownIcon,
|
||||
LightBulbIcon,
|
||||
ScaleIcon,
|
||||
SendIcon,
|
||||
TriangleAlertIcon,
|
||||
XIcon,
|
||||
} from "@modrinth/assets";
|
||||
import type { Nag, NagContext, NagStatus } from "@modrinth/moderation";
|
||||
import { nags } from "@modrinth/moderation";
|
||||
import { ButtonStyled, injectNotificationManager } from "@modrinth/ui";
|
||||
import type { Project, User, Version } from "@modrinth/utils";
|
||||
import { defineMessages, useVIntl, type MessageDescriptor } from "@vintl/vintl";
|
||||
import type { Component } from "vue";
|
||||
import { acceptTeamInvite, removeTeamMember } from "~/helpers/teams.js";
|
||||
AsteriskIcon,
|
||||
CheckIcon,
|
||||
ChevronRightIcon,
|
||||
DropdownIcon,
|
||||
LightBulbIcon,
|
||||
ScaleIcon,
|
||||
SendIcon,
|
||||
TriangleAlertIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import type { Nag, NagContext, NagStatus } from '@modrinth/moderation'
|
||||
import { nags } from '@modrinth/moderation'
|
||||
import { ButtonStyled, injectNotificationManager } from '@modrinth/ui'
|
||||
import type { Project, User, Version } from '@modrinth/utils'
|
||||
import { defineMessages, type MessageDescriptor, useVIntl } from '@vintl/vintl'
|
||||
import type { Component } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const { addNotification } = injectNotificationManager();
|
||||
import { acceptTeamInvite, removeTeamMember } from '~/helpers/teams.js'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
interface Tags {
|
||||
rejectedStatuses: string[];
|
||||
rejectedStatuses: string[]
|
||||
}
|
||||
|
||||
interface Auth {
|
||||
user: {
|
||||
id: string;
|
||||
};
|
||||
user: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
interface Member {
|
||||
accepted?: boolean;
|
||||
project_role?: string;
|
||||
user?: Partial<User>;
|
||||
accepted?: boolean
|
||||
project_role?: string
|
||||
user?: Partial<User>
|
||||
}
|
||||
|
||||
interface Props {
|
||||
project: Project;
|
||||
versions?: Version[];
|
||||
currentMember?: Member | null;
|
||||
allMembers?: Member[] | null;
|
||||
isSettings?: boolean;
|
||||
collapsed?: boolean;
|
||||
routeName?: string;
|
||||
auth: Auth;
|
||||
tags: Tags;
|
||||
setProcessing?: (processing: boolean) => void;
|
||||
toggleCollapsed?: () => void;
|
||||
updateMembers?: () => void | Promise<void>;
|
||||
project: Project
|
||||
versions?: Version[]
|
||||
currentMember?: Member | null
|
||||
allMembers?: Member[] | null
|
||||
isSettings?: boolean
|
||||
collapsed?: boolean
|
||||
routeName?: string
|
||||
auth: Auth
|
||||
tags: Tags
|
||||
setProcessing?: (processing: boolean) => void
|
||||
toggleCollapsed?: () => void
|
||||
updateMembers?: () => void | Promise<void>
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
invitationTitle: {
|
||||
id: "project-member-header.invitation-title",
|
||||
defaultMessage: "Invitation to join project",
|
||||
},
|
||||
invitationWithRole: {
|
||||
id: "project-member-header.invitation-with-role",
|
||||
defaultMessage: "You've been invited be a member of this project with the role of '{role}'.",
|
||||
},
|
||||
invitationNoRole: {
|
||||
id: "project-member-header.invitation-no-role",
|
||||
defaultMessage:
|
||||
"You've been invited to join this project. Please accept or decline the invitation.",
|
||||
},
|
||||
accept: {
|
||||
id: "project-member-header.accept",
|
||||
defaultMessage: "Accept",
|
||||
},
|
||||
decline: {
|
||||
id: "project-member-header.decline",
|
||||
defaultMessage: "Decline",
|
||||
},
|
||||
publishingChecklist: {
|
||||
id: "project-member-header.publishing-checklist",
|
||||
defaultMessage: "Publishing checklist",
|
||||
},
|
||||
submitForReview: {
|
||||
id: "project-member-header.submit-for-review",
|
||||
defaultMessage: "Submit for review",
|
||||
},
|
||||
submitForReviewDesc: {
|
||||
id: "project-member-header.submit-for-review-desc",
|
||||
defaultMessage:
|
||||
"Your project is only viewable by members of the project. It must be reviewed by moderators in order to be published.",
|
||||
},
|
||||
resubmitForReview: {
|
||||
id: "project-member-header.resubmit-for-review",
|
||||
defaultMessage: "Resubmit for review",
|
||||
},
|
||||
resubmitForReviewDesc: {
|
||||
id: "project-member-header.resubmit-for-review-desc",
|
||||
defaultMessage:
|
||||
"Your project has been {status} by Modrinth's staff. In most cases, you can resubmit for review after addressing the staff's message.",
|
||||
},
|
||||
showKey: {
|
||||
id: "project-member-header.show-key",
|
||||
defaultMessage: "Toggle key",
|
||||
},
|
||||
keyTitle: {
|
||||
id: "project-member-header.key-title",
|
||||
defaultMessage: "Status Key",
|
||||
},
|
||||
action: {
|
||||
id: "project-member-header.action",
|
||||
defaultMessage: "Action",
|
||||
},
|
||||
visitModerationPage: {
|
||||
id: "project-member-header.visit-moderation-page",
|
||||
defaultMessage: "Visit moderation page",
|
||||
},
|
||||
submitChecklistTooltip: {
|
||||
id: "project-member-header.submit-checklist-tooltip",
|
||||
defaultMessage: "You must complete the required steps in the publishing checklist!",
|
||||
},
|
||||
successJoin: {
|
||||
id: "project-member-header.success-join",
|
||||
defaultMessage: "You have joined the project team",
|
||||
},
|
||||
errorJoin: {
|
||||
id: "project-member-header.error-join",
|
||||
defaultMessage: "Failed to accept team invitation",
|
||||
},
|
||||
successDecline: {
|
||||
id: "project-member-header.success-decline",
|
||||
defaultMessage: "You have declined the team invitation",
|
||||
},
|
||||
errorDecline: {
|
||||
id: "project-member-header.error-decline",
|
||||
defaultMessage: "Failed to decline team invitation",
|
||||
},
|
||||
success: {
|
||||
id: "project-member-header.success",
|
||||
defaultMessage: "Success",
|
||||
},
|
||||
error: {
|
||||
id: "project-member-header.error",
|
||||
defaultMessage: "Error",
|
||||
},
|
||||
required: {
|
||||
id: "project-member-header.required",
|
||||
defaultMessage: "Required",
|
||||
},
|
||||
warning: {
|
||||
id: "project-member-header.warning",
|
||||
defaultMessage: "Warning",
|
||||
},
|
||||
suggestion: {
|
||||
id: "project-member-header.suggestion",
|
||||
defaultMessage: "Suggestion",
|
||||
},
|
||||
});
|
||||
invitationTitle: {
|
||||
id: 'project-member-header.invitation-title',
|
||||
defaultMessage: 'Invitation to join project',
|
||||
},
|
||||
invitationWithRole: {
|
||||
id: 'project-member-header.invitation-with-role',
|
||||
defaultMessage: "You've been invited be a member of this project with the role of '{role}'.",
|
||||
},
|
||||
invitationNoRole: {
|
||||
id: 'project-member-header.invitation-no-role',
|
||||
defaultMessage:
|
||||
"You've been invited to join this project. Please accept or decline the invitation.",
|
||||
},
|
||||
accept: {
|
||||
id: 'project-member-header.accept',
|
||||
defaultMessage: 'Accept',
|
||||
},
|
||||
decline: {
|
||||
id: 'project-member-header.decline',
|
||||
defaultMessage: 'Decline',
|
||||
},
|
||||
publishingChecklist: {
|
||||
id: 'project-member-header.publishing-checklist',
|
||||
defaultMessage: 'Publishing checklist',
|
||||
},
|
||||
submitForReview: {
|
||||
id: 'project-member-header.submit-for-review',
|
||||
defaultMessage: 'Submit for review',
|
||||
},
|
||||
submitForReviewDesc: {
|
||||
id: 'project-member-header.submit-for-review-desc',
|
||||
defaultMessage:
|
||||
'Your project is only viewable by members of the project. It must be reviewed by moderators in order to be published.',
|
||||
},
|
||||
resubmitForReview: {
|
||||
id: 'project-member-header.resubmit-for-review',
|
||||
defaultMessage: 'Resubmit for review',
|
||||
},
|
||||
resubmitForReviewDesc: {
|
||||
id: 'project-member-header.resubmit-for-review-desc',
|
||||
defaultMessage:
|
||||
"Your project has been {status} by Modrinth's staff. In most cases, you can resubmit for review after addressing the staff's message.",
|
||||
},
|
||||
showKey: {
|
||||
id: 'project-member-header.show-key',
|
||||
defaultMessage: 'Toggle key',
|
||||
},
|
||||
keyTitle: {
|
||||
id: 'project-member-header.key-title',
|
||||
defaultMessage: 'Status Key',
|
||||
},
|
||||
action: {
|
||||
id: 'project-member-header.action',
|
||||
defaultMessage: 'Action',
|
||||
},
|
||||
visitModerationPage: {
|
||||
id: 'project-member-header.visit-moderation-page',
|
||||
defaultMessage: 'Visit moderation page',
|
||||
},
|
||||
submitChecklistTooltip: {
|
||||
id: 'project-member-header.submit-checklist-tooltip',
|
||||
defaultMessage: 'You must complete the required steps in the publishing checklist!',
|
||||
},
|
||||
successJoin: {
|
||||
id: 'project-member-header.success-join',
|
||||
defaultMessage: 'You have joined the project team',
|
||||
},
|
||||
errorJoin: {
|
||||
id: 'project-member-header.error-join',
|
||||
defaultMessage: 'Failed to accept team invitation',
|
||||
},
|
||||
successDecline: {
|
||||
id: 'project-member-header.success-decline',
|
||||
defaultMessage: 'You have declined the team invitation',
|
||||
},
|
||||
errorDecline: {
|
||||
id: 'project-member-header.error-decline',
|
||||
defaultMessage: 'Failed to decline team invitation',
|
||||
},
|
||||
success: {
|
||||
id: 'project-member-header.success',
|
||||
defaultMessage: 'Success',
|
||||
},
|
||||
error: {
|
||||
id: 'project-member-header.error',
|
||||
defaultMessage: 'Error',
|
||||
},
|
||||
required: {
|
||||
id: 'project-member-header.required',
|
||||
defaultMessage: 'Required',
|
||||
},
|
||||
warning: {
|
||||
id: 'project-member-header.warning',
|
||||
defaultMessage: 'Warning',
|
||||
},
|
||||
suggestion: {
|
||||
id: 'project-member-header.suggestion',
|
||||
defaultMessage: 'Suggestion',
|
||||
},
|
||||
})
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
function getNagDescription(nag: Nag): string {
|
||||
if (typeof nag.description === "function") {
|
||||
return nag.description(nagContext.value);
|
||||
}
|
||||
return formatMessage(nag.description);
|
||||
if (typeof nag.description === 'function') {
|
||||
return nag.description(nagContext.value)
|
||||
}
|
||||
return formatMessage(nag.description)
|
||||
}
|
||||
|
||||
function getFormattedMessage(message: string | MessageDescriptor): string {
|
||||
if (typeof message === "string") {
|
||||
return message;
|
||||
}
|
||||
return formatMessage(message);
|
||||
if (typeof message === 'string') {
|
||||
return message
|
||||
}
|
||||
return formatMessage(message)
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
versions: () => [],
|
||||
currentMember: null,
|
||||
allMembers: null,
|
||||
isSettings: false,
|
||||
collapsed: false,
|
||||
routeName: "",
|
||||
});
|
||||
versions: () => [],
|
||||
currentMember: null,
|
||||
allMembers: null,
|
||||
isSettings: false,
|
||||
collapsed: false,
|
||||
routeName: '',
|
||||
setProcessing: () => {},
|
||||
toggleCollapsed: () => {},
|
||||
updateMembers: async () => {},
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
toggleCollapsed: [];
|
||||
updateMembers: [];
|
||||
setProcessing: [processing: boolean];
|
||||
}>();
|
||||
toggleCollapsed: []
|
||||
updateMembers: []
|
||||
setProcessing: [processing: boolean]
|
||||
}>()
|
||||
|
||||
const nagContext = computed<NagContext>(() => ({
|
||||
project: props.project,
|
||||
versions: props.versions,
|
||||
currentMember: props.currentMember as User,
|
||||
currentRoute: props.routeName,
|
||||
tags: props.tags,
|
||||
submitProject: submitForReview,
|
||||
}));
|
||||
|
||||
const showKey = ref(false);
|
||||
function toggleKey(): void {
|
||||
showKey.value = !showKey.value;
|
||||
}
|
||||
project: props.project,
|
||||
versions: props.versions,
|
||||
currentMember: props.currentMember as User,
|
||||
currentRoute: props.routeName,
|
||||
tags: props.tags,
|
||||
submitProject: submitForReview,
|
||||
}))
|
||||
|
||||
const canSubmitForReview = computed(() => {
|
||||
return (
|
||||
applicableNags.value.filter((nag) => nag.status === "required" && !isNagComplete(nag))
|
||||
.length === 0
|
||||
);
|
||||
});
|
||||
return (
|
||||
applicableNags.value.filter((nag) => nag.status === 'required' && !isNagComplete(nag))
|
||||
.length === 0
|
||||
)
|
||||
})
|
||||
|
||||
async function submitForReview() {
|
||||
if (canSubmitForReview) {
|
||||
await setProcessing(true);
|
||||
}
|
||||
if (canSubmitForReview.value) {
|
||||
await handleSetProcessing(true)
|
||||
}
|
||||
}
|
||||
|
||||
const applicableNags = computed<Nag[]>(() => {
|
||||
return nags.filter((nag) => {
|
||||
return nag.shouldShow(nagContext.value);
|
||||
});
|
||||
});
|
||||
return nags.filter((nag) => {
|
||||
return nag.shouldShow(nagContext.value)
|
||||
})
|
||||
})
|
||||
|
||||
function isNagComplete(nag: Nag): boolean {
|
||||
const context = nagContext.value;
|
||||
return !nag.shouldShow(context);
|
||||
const context = nagContext.value
|
||||
return !nag.shouldShow(context)
|
||||
}
|
||||
|
||||
const visibleNags = computed<Nag[]>(() => {
|
||||
const finalNags = applicableNags.value.filter((nag) => !isNagComplete(nag));
|
||||
const finalNags = applicableNags.value.filter((nag) => !isNagComplete(nag))
|
||||
|
||||
if (props.project.status === "draft") {
|
||||
finalNags.push({
|
||||
id: "submit-for-review",
|
||||
title: messages.submitForReview,
|
||||
description: () => formatMessage(messages.submitForReviewDesc),
|
||||
status: "special-submit-action",
|
||||
shouldShow: (ctx) => ctx.project.status === "draft",
|
||||
});
|
||||
}
|
||||
if (props.project.status === 'draft') {
|
||||
finalNags.push({
|
||||
id: 'submit-for-review',
|
||||
title: messages.submitForReview,
|
||||
description: () => formatMessage(messages.submitForReviewDesc),
|
||||
status: 'special-submit-action',
|
||||
shouldShow: (ctx) => ctx.project.status === 'draft',
|
||||
})
|
||||
}
|
||||
|
||||
if (props.tags.rejectedStatuses.includes(props.project.status)) {
|
||||
finalNags.push({
|
||||
id: "resubmit-for-review",
|
||||
title: messages.resubmitForReview,
|
||||
description: (ctx) =>
|
||||
formatMessage(messages.resubmitForReviewDesc, { status: ctx.project.status }),
|
||||
status: "special-submit-action",
|
||||
shouldShow: (ctx) => ctx.tags.rejectedStatuses.includes(ctx.project.status),
|
||||
link: {
|
||||
path: "moderation",
|
||||
title: messages.visitModerationPage,
|
||||
shouldShow: () => props.routeName !== "type-id-moderation",
|
||||
},
|
||||
});
|
||||
}
|
||||
if (props.tags.rejectedStatuses.includes(props.project.status)) {
|
||||
finalNags.push({
|
||||
id: 'resubmit-for-review',
|
||||
title: messages.resubmitForReview,
|
||||
description: (ctx) =>
|
||||
formatMessage(messages.resubmitForReviewDesc, { status: ctx.project.status }),
|
||||
status: 'special-submit-action',
|
||||
shouldShow: (ctx) => ctx.tags.rejectedStatuses.includes(ctx.project.status),
|
||||
link: {
|
||||
path: 'moderation',
|
||||
title: messages.visitModerationPage,
|
||||
shouldShow: () => props.routeName !== 'type-id-moderation',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
finalNags.sort((a, b) => {
|
||||
const statusOrder = { required: 0, warning: 1, suggestion: 2, "special-submit-action": 3 };
|
||||
return statusOrder[a.status] - statusOrder[b.status];
|
||||
});
|
||||
finalNags.sort((a, b) => {
|
||||
const statusOrder = { required: 0, warning: 1, suggestion: 2, 'special-submit-action': 3 }
|
||||
return statusOrder[a.status] - statusOrder[b.status]
|
||||
})
|
||||
|
||||
return finalNags;
|
||||
});
|
||||
return finalNags
|
||||
})
|
||||
|
||||
function shouldShowLink(nag: Nag): boolean {
|
||||
return nag.link?.shouldShow ? nag.link.shouldShow(nagContext.value) : false;
|
||||
return nag.link?.shouldShow ? nag.link.shouldShow(nagContext.value) : false
|
||||
}
|
||||
|
||||
function getDefaultIcon(status: NagStatus): Component {
|
||||
switch (status) {
|
||||
case "required":
|
||||
return AsteriskIcon;
|
||||
case "warning":
|
||||
return TriangleAlertIcon;
|
||||
case "suggestion":
|
||||
return LightBulbIcon;
|
||||
case "special-submit-action":
|
||||
return ScaleIcon;
|
||||
default:
|
||||
return AsteriskIcon;
|
||||
}
|
||||
switch (status) {
|
||||
case 'required':
|
||||
return AsteriskIcon
|
||||
case 'warning':
|
||||
return TriangleAlertIcon
|
||||
case 'suggestion':
|
||||
return LightBulbIcon
|
||||
case 'special-submit-action':
|
||||
return ScaleIcon
|
||||
default:
|
||||
return AsteriskIcon
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusTooltip(status: NagStatus): string {
|
||||
switch (status) {
|
||||
case "required":
|
||||
return formatMessage(messages.required);
|
||||
case "warning":
|
||||
return formatMessage(messages.warning);
|
||||
case "suggestion":
|
||||
return formatMessage(messages.suggestion);
|
||||
default:
|
||||
return formatMessage(messages.required);
|
||||
}
|
||||
switch (status) {
|
||||
case 'required':
|
||||
return formatMessage(messages.required)
|
||||
case 'warning':
|
||||
return formatMessage(messages.warning)
|
||||
case 'suggestion':
|
||||
return formatMessage(messages.suggestion)
|
||||
default:
|
||||
return formatMessage(messages.required)
|
||||
}
|
||||
}
|
||||
|
||||
const showInvitation = computed<boolean>(() => {
|
||||
if (props.allMembers && props.auth) {
|
||||
const member = props.allMembers.find((x) => x?.user?.id === props.auth.user.id);
|
||||
return !!member && !member.accepted;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (props.allMembers && props.auth) {
|
||||
const member = props.allMembers.find((x) => x?.user?.id === props.auth.user.id)
|
||||
return !!member && !member.accepted
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
function toggleCollapsed(): void {
|
||||
if (props.toggleCollapsed) {
|
||||
props.toggleCollapsed();
|
||||
} else {
|
||||
emit("toggleCollapsed");
|
||||
}
|
||||
function handleToggleCollapsed(): void {
|
||||
if (props.toggleCollapsed) {
|
||||
props.toggleCollapsed()
|
||||
} else {
|
||||
emit('toggleCollapsed')
|
||||
}
|
||||
}
|
||||
|
||||
async function updateMembers(): Promise<void> {
|
||||
if (props.updateMembers) {
|
||||
await props.updateMembers();
|
||||
} else {
|
||||
emit("updateMembers");
|
||||
}
|
||||
async function handleUpdateMembers(): Promise<void> {
|
||||
if (props.updateMembers) {
|
||||
await props.updateMembers()
|
||||
} else {
|
||||
emit('updateMembers')
|
||||
}
|
||||
}
|
||||
|
||||
function setProcessing(processing: boolean): void {
|
||||
if (props.setProcessing) {
|
||||
props.setProcessing(processing);
|
||||
} else {
|
||||
emit("setProcessing", processing);
|
||||
}
|
||||
function handleSetProcessing(processing: boolean): void {
|
||||
if (props.setProcessing) {
|
||||
props.setProcessing(processing)
|
||||
} else {
|
||||
emit('setProcessing', processing)
|
||||
}
|
||||
}
|
||||
|
||||
async function acceptInvite(): Promise<void> {
|
||||
try {
|
||||
setProcessing(true);
|
||||
await acceptTeamInvite(props.project.team);
|
||||
await updateMembers();
|
||||
addNotification({
|
||||
title: formatMessage(messages.success),
|
||||
text: formatMessage(messages.successJoin),
|
||||
type: "success",
|
||||
});
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
title: formatMessage(messages.error),
|
||||
text: formatMessage(messages.errorJoin),
|
||||
type: "error",
|
||||
});
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
}
|
||||
try {
|
||||
handleSetProcessing(true)
|
||||
await acceptTeamInvite(props.project.team)
|
||||
await handleUpdateMembers()
|
||||
addNotification({
|
||||
title: formatMessage(messages.success),
|
||||
text: formatMessage(messages.successJoin),
|
||||
type: 'success',
|
||||
})
|
||||
} catch {
|
||||
addNotification({
|
||||
title: formatMessage(messages.error),
|
||||
text: formatMessage(messages.errorJoin),
|
||||
type: 'error',
|
||||
})
|
||||
} finally {
|
||||
handleSetProcessing(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function declineInvite(): Promise<void> {
|
||||
try {
|
||||
setProcessing(true);
|
||||
await removeTeamMember(props.project.team, props.auth.user.id);
|
||||
await updateMembers();
|
||||
addNotification({
|
||||
title: formatMessage(messages.success),
|
||||
text: formatMessage(messages.successDecline),
|
||||
type: "success",
|
||||
});
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
title: formatMessage(messages.error),
|
||||
text: formatMessage(messages.errorDecline),
|
||||
type: "error",
|
||||
});
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
}
|
||||
try {
|
||||
handleSetProcessing(true)
|
||||
await removeTeamMember(props.project.team, props.auth.user.id)
|
||||
await handleUpdateMembers()
|
||||
addNotification({
|
||||
title: formatMessage(messages.success),
|
||||
text: formatMessage(messages.successDecline),
|
||||
type: 'success',
|
||||
})
|
||||
} catch {
|
||||
addNotification({
|
||||
title: formatMessage(messages.error),
|
||||
text: formatMessage(messages.errorDecline),
|
||||
type: 'error',
|
||||
})
|
||||
} finally {
|
||||
handleSetProcessing(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.duration-250 {
|
||||
transition-duration: 250ms;
|
||||
transition-duration: 250ms;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user