forked from didirus/AstralRinth
811 lines
25 KiB
Vue
811 lines
25 KiB
Vue
<template>
|
|
<div>
|
|
<ModalConfirm
|
|
v-if="auth.user && auth.user.id === creator.id"
|
|
ref="deleteModal"
|
|
:title="formatMessage(messages.deleteModalTitle)"
|
|
:description="formatMessage(messages.deleteModalDescription)"
|
|
:has-to-type="false"
|
|
:proceed-label="formatMessage(commonMessages.deleteLabel)"
|
|
@proceed="deleteCollection()"
|
|
/>
|
|
<div class="normal-page">
|
|
<div class="normal-page__sidebar">
|
|
<div class="card">
|
|
<div class="card__overlay input-group">
|
|
<template v-if="canEdit && isEditing === false">
|
|
<Button @click="isEditing = true">
|
|
<EditIcon aria-hidden="true" />
|
|
{{ formatMessage(commonMessages.editButton) }}
|
|
</Button>
|
|
<Button id="delete-collection" @click="() => $refs.deleteModal.show()">
|
|
<TrashIcon aria-hidden="true" />
|
|
{{ formatMessage(commonMessages.deleteLabel) }}
|
|
</Button>
|
|
</template>
|
|
<template v-else-if="canEdit && isEditing === true">
|
|
<PopoutMenu class="btn">
|
|
<EditIcon aria-hidden="true" /> {{ formatMessage(messages.editIconButton) }}
|
|
<template #menu>
|
|
<span class="icon-edit-menu">
|
|
<FileInput
|
|
id="project-icon"
|
|
:max-size="262144"
|
|
:show-icon="true"
|
|
accept="image/png,image/jpeg,image/gif,image/webp"
|
|
class="btn btn-transparent upload"
|
|
style="white-space: nowrap"
|
|
prompt="Upload icon"
|
|
aria-label="Upload icon"
|
|
@change="showPreviewImage"
|
|
>
|
|
<UploadIcon aria-hidden="true" />
|
|
{{ formatMessage(messages.uploadIconButton) }}
|
|
</FileInput>
|
|
<Button
|
|
v-if="!deletedIcon && (previewImage || collection.icon_url)"
|
|
style="white-space: nowrap"
|
|
transparent
|
|
@click="
|
|
() => {
|
|
deletedIcon = true;
|
|
previewImage = null;
|
|
}
|
|
"
|
|
>
|
|
<TrashIcon aria-hidden="true" />
|
|
{{ formatMessage(messages.deleteIconButton) }}
|
|
</Button>
|
|
</span>
|
|
</template>
|
|
</PopoutMenu>
|
|
</template>
|
|
</div>
|
|
<!-- Editing -->
|
|
<template v-if="isEditing">
|
|
<div class="inputs universal-labels">
|
|
<div class="avatar-section">
|
|
<Avatar
|
|
size="md"
|
|
:src="deletedIcon ? null : previewImage ? previewImage : collection.icon_url"
|
|
/>
|
|
</div>
|
|
<label for="collection-title">
|
|
<span class="label__title"> {{ formatMessage(commonMessages.titleLabel) }} </span>
|
|
</label>
|
|
<input id="collection-title" v-model="name" maxlength="255" type="text" />
|
|
<label for="collection-description">
|
|
<span class="label__title">
|
|
{{ formatMessage(commonMessages.descriptionLabel) }}
|
|
</span>
|
|
</label>
|
|
<div class="textarea-wrapper">
|
|
<textarea id="collection-description" v-model="summary" maxlength="255" />
|
|
</div>
|
|
<label for="visibility">
|
|
<span class="label__title">
|
|
{{ formatMessage(commonMessages.visibilityLabel) }}
|
|
</span>
|
|
</label>
|
|
<DropdownSelect
|
|
id="visibility"
|
|
v-model="visibility"
|
|
:options="['listed', 'unlisted', 'private']"
|
|
:disabled="visibility === 'rejected'"
|
|
:multiple="false"
|
|
:display-name="
|
|
(s) => {
|
|
if (s === 'listed') return formatMessage(commonMessages.publicLabel);
|
|
return formatMessage(commonMessages[`${s}Label`]);
|
|
}
|
|
"
|
|
:searchable="false"
|
|
/>
|
|
</div>
|
|
<div class="push-right input-group">
|
|
<Button @click="isEditing = false">
|
|
<XIcon aria-hidden="true" />
|
|
{{ formatMessage(commonMessages.cancelButton) }}
|
|
</Button>
|
|
<Button color="primary" @click="saveChanges()">
|
|
<SaveIcon aria-hidden="true" />
|
|
{{ formatMessage(commonMessages.saveButton) }}
|
|
</Button>
|
|
</div>
|
|
</template>
|
|
<!-- Content -->
|
|
<template v-if="!isEditing">
|
|
<div class="page-header__icon">
|
|
<Avatar size="md" :src="collection.icon_url" />
|
|
</div>
|
|
<div class="page-header__text">
|
|
<h1 class="title">{{ collection.name }}</h1>
|
|
|
|
<div>
|
|
<span class="collection-label">
|
|
<BoxIcon aria-hidden="true" /> {{ formatMessage(messages.collectionLabel) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="collection-info">
|
|
<div class="metadata-item markdown-body collection-description">
|
|
<p>{{ collection.description }}</p>
|
|
</div>
|
|
|
|
<hr class="card-divider" />
|
|
|
|
<div v-if="canEdit" class="primary-stat">
|
|
<template v-if="collection.status === 'listed'">
|
|
<WorldIcon class="primary-stat__icon" aria-hidden="true" />
|
|
<div class="primary-stat__text">
|
|
<strong> {{ formatMessage(commonMessages.publicLabel) }} </strong>
|
|
</div>
|
|
</template>
|
|
<template v-else-if="collection.status === 'unlisted'">
|
|
<LinkIcon class="primary-stat__icon" aria-hidden="true" />
|
|
<div class="primary-stat__text">
|
|
<strong> {{ formatMessage(commonMessages.unlistedLabel) }} </strong>
|
|
</div>
|
|
</template>
|
|
<template v-else-if="collection.status === 'private'">
|
|
<LockIcon class="primary-stat__icon" aria-hidden="true" />
|
|
<div class="primary-stat__text">
|
|
<strong> {{ formatMessage(commonMessages.privateLabel) }} </strong>
|
|
</div>
|
|
</template>
|
|
<template v-else-if="collection.status === 'rejected'">
|
|
<XIcon class="primary-stat__icon" aria-hidden="true" />
|
|
<div class="primary-stat__text">
|
|
<strong> {{ formatMessage(commonMessages.rejectedLabel) }} </strong>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="primary-stat">
|
|
<LibraryIcon class="primary-stat__icon" aria-hidden="true" />
|
|
<div v-if="projects" class="primary-stat__text">
|
|
<IntlFormatted
|
|
:message-id="messages.projectsCountLabel"
|
|
:values="{ count: formatCompactNumber(projects.length || 0) }"
|
|
>
|
|
<template #stat="{ children }">
|
|
<span class="primary-stat__counter">
|
|
<component :is="() => normalizeChildren(children)" />
|
|
</span>
|
|
</template>
|
|
</IntlFormatted>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metadata-item">
|
|
<div
|
|
v-tooltip="
|
|
formatMessage(commonMessages.dateAtTimeTooltip, {
|
|
date: new Date(collection.created),
|
|
time: new Date(collection.created),
|
|
})
|
|
"
|
|
class="date"
|
|
>
|
|
<CalendarIcon aria-hidden="true" />
|
|
<label>
|
|
{{
|
|
formatMessage(messages.createdAtLabel, {
|
|
ago: formatRelativeTime(collection.created),
|
|
})
|
|
}}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="collection.id !== 'following'" class="metadata-item">
|
|
<div
|
|
v-tooltip="
|
|
formatMessage(commonMessages.dateAtTimeTooltip, {
|
|
date: new Date(collection.updated),
|
|
time: new Date(collection.updated),
|
|
})
|
|
"
|
|
class="date"
|
|
>
|
|
<UpdatedIcon aria-hidden="true" />
|
|
<label>
|
|
{{
|
|
formatMessage(messages.updatedAtLabel, {
|
|
ago: formatRelativeTime(collection.updated),
|
|
})
|
|
}}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="card-divider" />
|
|
|
|
<div class="collection-info">
|
|
<h2 class="card-header">{{ formatMessage(messages.curatedByLabel) }}</h2>
|
|
<div class="metadata-item">
|
|
<nuxt-link
|
|
class="team-member columns button-transparent"
|
|
:to="'/user/' + creator.username"
|
|
>
|
|
<Avatar :src="creator.avatar_url" :alt="creator.username" size="sm" circle />
|
|
|
|
<div class="member-info">
|
|
<p class="name">{{ creator.username }}</p>
|
|
<p class="role">{{ formatMessage(messages.ownerLabel) }}</p>
|
|
</div>
|
|
</nuxt-link>
|
|
</div>
|
|
<!-- <hr class="card-divider" />
|
|
<div class="input-group">
|
|
<Button @click="() => $refs.shareModal.show()">
|
|
<ShareIcon />
|
|
Share
|
|
</Button>
|
|
</div> -->
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<AdPlaceholder />
|
|
</div>
|
|
<div class="normal-page__content">
|
|
<nav class="navigation-card">
|
|
<NavRow
|
|
:links="[
|
|
{
|
|
label: formatMessage(commonMessages.allProjectType),
|
|
href: `/collection/${collection.id}`,
|
|
},
|
|
...projectTypes.map((x) => {
|
|
return {
|
|
label: formatMessage(getProjectTypeMessage(x, true)),
|
|
href: `/collection/${collection.id}/${x}s`,
|
|
};
|
|
}),
|
|
]"
|
|
/>
|
|
<button
|
|
v-tooltip="
|
|
formatMessage(
|
|
commonMessages[`${cosmetics.searchDisplayMode.collection || 'list'}InputView`],
|
|
)
|
|
"
|
|
:aria-label="
|
|
formatMessage(
|
|
commonMessages[`${cosmetics.searchDisplayMode.collection || 'list'}InputView`],
|
|
)
|
|
"
|
|
class="square-button"
|
|
@click="cycleSearchDisplayMode()"
|
|
>
|
|
<GridIcon v-if="cosmetics.searchDisplayMode.collection === 'grid'" />
|
|
<ImageIcon v-else-if="cosmetics.searchDisplayMode.collection === 'gallery'" />
|
|
<ListIcon v-else />
|
|
</button>
|
|
</nav>
|
|
|
|
<div
|
|
v-if="projects && projects?.length > 0"
|
|
:class="
|
|
'project-list display-mode--' + (cosmetics.searchDisplayMode.collection || 'list')
|
|
"
|
|
>
|
|
<ProjectCard
|
|
v-for="project in (route.params.projectType !== undefined
|
|
? projects.filter(
|
|
(x) =>
|
|
x.project_type ===
|
|
route.params.projectType.substr(0, route.params.projectType.length - 1),
|
|
)
|
|
: projects
|
|
)
|
|
.slice()
|
|
.sort((a, b) => b.downloads - a.downloads)"
|
|
:id="project.id"
|
|
:key="project.id"
|
|
:type="project.project_type"
|
|
:categories="project.categories"
|
|
:created-at="project.published"
|
|
:updated-at="project.updated"
|
|
:description="project.description"
|
|
:downloads="project.downloads ? project.downloads.toString() : '0'"
|
|
:follows="project.followers ? project.followers.toString() : '0'"
|
|
:featured-image="project.gallery.find((element) => element.featured)?.url"
|
|
:icon-url="project.icon_url"
|
|
:name="project.title"
|
|
:client-side="project.client_side"
|
|
:server-side="project.server_side"
|
|
:color="project.color"
|
|
:show-updated-date="!canEdit && collection.id !== 'following'"
|
|
:show-created-date="!canEdit && collection.id !== 'following'"
|
|
>
|
|
<button
|
|
v-if="canEdit"
|
|
class="iconified-button remove-btn"
|
|
@click="
|
|
() => {
|
|
removeProjects = [project];
|
|
saveChanges();
|
|
}
|
|
"
|
|
>
|
|
<TrashIcon aria-hidden="true" />
|
|
{{ formatMessage(messages.removeProjectButton) }}
|
|
</button>
|
|
<button
|
|
v-if="collection.id === 'following'"
|
|
class="iconified-button"
|
|
@click="unfollowProject(project)"
|
|
>
|
|
<TrashIcon aria-hidden="true" />
|
|
{{ formatMessage(messages.unfollowProjectButton) }}
|
|
</button>
|
|
</ProjectCard>
|
|
</div>
|
|
<div v-else class="error">
|
|
<UpToDate class="icon" /><br />
|
|
<span v-if="auth.user && auth.user.id === creator.id" class="preserve-lines text">
|
|
<IntlFormatted :message-id="messages.noProjectsAuthLabel">
|
|
<template #create-link="{ children }">
|
|
<a class="link" @click.prevent="$router.push('/mods')">
|
|
<component :is="() => children" />
|
|
</a>
|
|
</template>
|
|
</IntlFormatted>
|
|
</span>
|
|
<span v-else class="text">{{ formatMessage(messages.noProjectsLabel) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
CalendarIcon,
|
|
EditIcon,
|
|
XIcon,
|
|
SaveIcon,
|
|
UploadIcon,
|
|
TrashIcon,
|
|
LinkIcon,
|
|
LockIcon,
|
|
GridIcon,
|
|
ImageIcon,
|
|
ListIcon,
|
|
UpdatedIcon,
|
|
LibraryIcon,
|
|
BoxIcon,
|
|
} from "@modrinth/assets";
|
|
import { PopoutMenu, FileInput, DropdownSelect, Avatar, Button } from "@modrinth/ui";
|
|
|
|
import WorldIcon from "assets/images/utils/world.svg";
|
|
import UpToDate from "assets/images/illustrations/up_to_date.svg";
|
|
import { addNotification } from "~/composables/notifs.js";
|
|
import ModalConfirm from "~/components/ui/ModalConfirm.vue";
|
|
import NavRow from "~/components/ui/NavRow.vue";
|
|
import ProjectCard from "~/components/ui/ProjectCard.vue";
|
|
import AdPlaceholder from "~/components/ui/AdPlaceholder.vue";
|
|
|
|
const vintl = useVIntl();
|
|
const { formatMessage } = vintl;
|
|
const formatRelativeTime = useRelativeTime();
|
|
const formatCompactNumber = useCompactNumber();
|
|
|
|
const messages = defineMessages({
|
|
collectionDescription: {
|
|
id: "collection.description",
|
|
defaultMessage: "{description} - View the collection {name} by {username} on Modrinth",
|
|
},
|
|
collectionLabel: {
|
|
id: "collection.label.collection",
|
|
defaultMessage: "Collection",
|
|
},
|
|
collectionTitle: {
|
|
id: "collection.title",
|
|
defaultMessage: "{name} - Collection",
|
|
},
|
|
editIconButton: {
|
|
id: "collection.button.edit-icon",
|
|
defaultMessage: "Edit icon",
|
|
},
|
|
deleteIconButton: {
|
|
id: "collection.button.delete-icon",
|
|
defaultMessage: "Delete icon",
|
|
},
|
|
createdAtLabel: {
|
|
id: "collection.label.created-at",
|
|
defaultMessage: "Created {ago}",
|
|
},
|
|
collectionNotFoundError: {
|
|
id: "collection.error.not-found",
|
|
defaultMessage: "Collection not found",
|
|
},
|
|
curatedByLabel: {
|
|
id: "collection.label.curated-by",
|
|
defaultMessage: "Curated by",
|
|
},
|
|
deleteModalDescription: {
|
|
id: "collection.delete-modal.description",
|
|
defaultMessage: "This will remove this collection forever. This action cannot be undone.",
|
|
},
|
|
deleteModalTitle: {
|
|
id: "collection.delete-modal.title",
|
|
defaultMessage: "Are you sure you want to delete this collection?",
|
|
},
|
|
followingCollectionDescription: {
|
|
id: "collection.description.following",
|
|
defaultMessage: "Auto-generated collection of all the projects you're following.",
|
|
},
|
|
noProjectsLabel: {
|
|
id: "collection.label.no-projects",
|
|
defaultMessage: "This collection has no projects!",
|
|
},
|
|
noProjectsAuthLabel: {
|
|
id: "collection.label.no-projects-auth",
|
|
defaultMessage:
|
|
"You don't have any projects.\nWould you like to <create-link>add one</create-link>?",
|
|
},
|
|
ownerLabel: {
|
|
id: "collection.label.owner",
|
|
defaultMessage: "Owner",
|
|
},
|
|
projectsCountLabel: {
|
|
id: "collection.label.projects-count",
|
|
defaultMessage:
|
|
"{count, plural, one {<stat>{count}</stat> project} other {<stat>{count}</stat> projects}}",
|
|
},
|
|
removeProjectButton: {
|
|
id: "collection.button.remove-project",
|
|
defaultMessage: "Remove project",
|
|
},
|
|
unfollowProjectButton: {
|
|
id: "collection.button.unfollow-project",
|
|
defaultMessage: "Unfollow project",
|
|
},
|
|
updatedAtLabel: {
|
|
id: "collection.label.updated-at",
|
|
defaultMessage: "Updated {ago}",
|
|
},
|
|
uploadIconButton: {
|
|
id: "collection.button.upload-icon",
|
|
defaultMessage: "Upload icon",
|
|
},
|
|
});
|
|
|
|
const data = useNuxtApp();
|
|
const route = useNativeRoute();
|
|
const auth = await useAuth();
|
|
const cosmetics = useCosmetics();
|
|
const tags = useTags();
|
|
|
|
const isEditing = ref(false);
|
|
|
|
function cycleSearchDisplayMode() {
|
|
cosmetics.value.searchDisplayMode.collection = data.$cycleValue(
|
|
cosmetics.value.searchDisplayMode.collection,
|
|
tags.value.projectViewModes,
|
|
);
|
|
}
|
|
|
|
let collection, refreshCollection, creator, projects, refreshProjects;
|
|
|
|
try {
|
|
if (route.params.id === "following") {
|
|
collection = ref({
|
|
id: "following",
|
|
icon_url: "https://cdn.modrinth.com/follow-collection.png",
|
|
name: formatMessage(commonMessages.followedProjectsLabel),
|
|
description: formatMessage(messages.followingCollectionDescription),
|
|
status: "private",
|
|
user: auth.value.user.id,
|
|
created: auth.value.user.created,
|
|
updated: auth.value.user.created,
|
|
});
|
|
[{ data: projects, refresh: refreshProjects }] = await Promise.all([
|
|
useAsyncData(
|
|
`user/${auth.value.user.id}/follows`,
|
|
() => useBaseFetch(`user/${auth.value.user.id}/follows`),
|
|
{
|
|
transform: (projects) => {
|
|
for (const project of projects) {
|
|
project.categories = project.categories.concat(project.loaders);
|
|
}
|
|
|
|
return projects;
|
|
},
|
|
},
|
|
),
|
|
]);
|
|
creator = ref(auth.value.user);
|
|
refreshCollection = async () => {};
|
|
} else {
|
|
const val = await useAsyncData(`collection/${route.params.id}`, () =>
|
|
useBaseFetch(`collection/${route.params.id}`, { apiVersion: 3 }),
|
|
);
|
|
collection = val.data;
|
|
refreshCollection = val.refresh;
|
|
[{ data: creator }, { data: projects, refresh: refreshProjects }] = await Promise.all([
|
|
await useAsyncData(`user/${collection.value.user}`, () =>
|
|
useBaseFetch(`user/${collection.value.user}`),
|
|
),
|
|
await useAsyncData(
|
|
`projects?ids=${encodeURIComponent(JSON.stringify(collection.value.projects))}]`,
|
|
() =>
|
|
useBaseFetch(
|
|
`projects?ids=${encodeURIComponent(JSON.stringify(collection.value.projects))}`,
|
|
),
|
|
{
|
|
transform: (projects) => {
|
|
for (const project of projects) {
|
|
project.categories = project.categories.concat(project.loaders);
|
|
}
|
|
|
|
return projects;
|
|
},
|
|
},
|
|
),
|
|
]);
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
throw createError({
|
|
fatal: true,
|
|
statusCode: 404,
|
|
message: formatMessage(messages.collectionNotFoundError),
|
|
});
|
|
}
|
|
|
|
if (!collection.value) {
|
|
throw createError({
|
|
fatal: true,
|
|
statusCode: 404,
|
|
message: formatMessage(messages.collectionNotFoundError),
|
|
});
|
|
}
|
|
|
|
const title = computed(() =>
|
|
formatMessage(messages.collectionTitle, { name: collection.value.name }),
|
|
);
|
|
|
|
useSeoMeta({
|
|
title,
|
|
description: () =>
|
|
formatMessage(messages.collectionDescription, {
|
|
name: collection.value.name,
|
|
description: collection.value.description,
|
|
username: creator.value.username,
|
|
}),
|
|
ogTitle: title,
|
|
ogDescription: collection.value.description,
|
|
ogImage: collection.value.icon_url ?? "https://cdn.modrinth.com/placeholder.png",
|
|
robots: collection.value.status === "listed" ? "all" : "noindex",
|
|
});
|
|
|
|
const canEdit = computed(
|
|
() =>
|
|
auth.value.user &&
|
|
auth.value.user.id === collection.value.user &&
|
|
collection.value.id !== "following",
|
|
);
|
|
|
|
const projectTypes = computed(() => {
|
|
const projectSet = new Set(
|
|
projects.value?.map((project) => project?.project_type).filter((x) => x !== undefined) || [],
|
|
);
|
|
projectSet.delete("project");
|
|
return Array.from(projectSet);
|
|
});
|
|
|
|
const icon = ref(null);
|
|
const deletedIcon = ref(false);
|
|
const previewImage = ref(null);
|
|
|
|
const name = ref(collection.value.name);
|
|
const summary = ref(collection.value.description);
|
|
const visibility = ref(collection.value.status);
|
|
const removeProjects = ref([]);
|
|
|
|
async function unfollowProject(project) {
|
|
await userUnfollowProject(project);
|
|
projects.value = projects.value.filter((x) => x.id !== project.id);
|
|
}
|
|
|
|
async function saveChanges() {
|
|
startLoading();
|
|
try {
|
|
if (deletedIcon.value) {
|
|
await useBaseFetch(`collection/${collection.value.id}/icon`, {
|
|
method: "DELETE",
|
|
apiVersion: 3,
|
|
});
|
|
} else if (icon.value) {
|
|
const ext = icon.value?.type?.split("/").pop();
|
|
if (!ext) throw new Error("Invalid file type");
|
|
await useBaseFetch(`collection/${collection.value.id}/icon?ext=${ext}`, {
|
|
method: "PATCH",
|
|
body: icon.value,
|
|
apiVersion: 3,
|
|
});
|
|
}
|
|
|
|
const projectsToRemove = removeProjects.value?.map((p) => p.id) ?? [];
|
|
const newProjects = projects.value
|
|
.filter((p) => !projectsToRemove.includes(p.id))
|
|
.map((p) => p.id);
|
|
const newProjectIds = projectsToRemove.length > 0 ? newProjects : undefined;
|
|
|
|
await useBaseFetch(`collection/${collection.value.id}`, {
|
|
method: "PATCH",
|
|
body: {
|
|
name: name.value,
|
|
description: summary.value,
|
|
status: visibility.value,
|
|
new_projects: newProjectIds,
|
|
},
|
|
apiVersion: 3,
|
|
});
|
|
|
|
await refreshCollection();
|
|
await refreshProjects();
|
|
|
|
name.value = collection.value.name;
|
|
summary.value = collection.value.description;
|
|
visibility.value = collection.value.status;
|
|
removeProjects.value = [];
|
|
|
|
isEditing.value = false;
|
|
} catch (err) {
|
|
addNotification({
|
|
group: "main",
|
|
title: formatMessage(commonMessages.errorNotificationTitle),
|
|
text: err,
|
|
type: "error",
|
|
});
|
|
}
|
|
await initUserCollections();
|
|
stopLoading();
|
|
}
|
|
|
|
async function deleteCollection() {
|
|
startLoading();
|
|
try {
|
|
await useBaseFetch(`collection/${collection.value.id}`, {
|
|
method: "DELETE",
|
|
apiVersion: 3,
|
|
});
|
|
await navigateTo("/dashboard/collections");
|
|
} catch (err) {
|
|
addNotification({
|
|
group: "main",
|
|
title: formatMessage(commonMessages.errorNotificationTitle),
|
|
text: err.data.description,
|
|
type: "error",
|
|
});
|
|
}
|
|
await initUserCollections();
|
|
stopLoading();
|
|
}
|
|
|
|
function showPreviewImage(files) {
|
|
const reader = new FileReader();
|
|
icon.value = files[0];
|
|
deletedIcon.value = false;
|
|
reader.readAsDataURL(icon.value);
|
|
reader.onload = (event) => {
|
|
previewImage.value = event.target.result;
|
|
};
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.animated-dropdown {
|
|
// Omorphia's dropdowns are harcoded in width, so we need to override that
|
|
width: 100% !important;
|
|
}
|
|
|
|
.inputs {
|
|
margin-bottom: 1rem;
|
|
|
|
input {
|
|
margin-top: 0.5rem;
|
|
width: 100%;
|
|
}
|
|
|
|
textarea {
|
|
min-height: 10rem;
|
|
}
|
|
|
|
label {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
.team-member {
|
|
align-items: center;
|
|
padding: 0.25rem 0.5rem;
|
|
|
|
.member-info {
|
|
overflow: hidden;
|
|
margin: auto 0 auto 0.75rem;
|
|
|
|
.name {
|
|
font-weight: bold;
|
|
}
|
|
|
|
p {
|
|
font-size: var(--font-size-sm);
|
|
margin: 0.2rem 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
.remove-btn {
|
|
margin-top: auto;
|
|
}
|
|
|
|
.card {
|
|
padding: var(--spacing-card-lg);
|
|
|
|
.page-header__icon {
|
|
margin-block: 0;
|
|
}
|
|
|
|
.card__overlay {
|
|
top: var(--spacing-card-lg);
|
|
right: var(--spacing-card-lg);
|
|
}
|
|
}
|
|
|
|
.collection-info {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.date {
|
|
color: var(--color-text-secondary);
|
|
font-size: var(--font-size-nm);
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 0.25rem;
|
|
cursor: default;
|
|
|
|
.label {
|
|
margin-right: 0.25rem;
|
|
}
|
|
|
|
svg {
|
|
height: 1rem;
|
|
margin-right: 0.25rem;
|
|
}
|
|
}
|
|
|
|
.card-header {
|
|
font-size: 1.125rem;
|
|
font-weight: bold;
|
|
color: var(--color-heading);
|
|
margin-bottom: 0.5rem;
|
|
width: fit-content;
|
|
}
|
|
|
|
.title {
|
|
margin: var(--gap-md) 0 var(--spacing-card-xs) 0;
|
|
font-size: var(--font-size-xl);
|
|
color: var(--color-text-dark);
|
|
}
|
|
|
|
.collection-label {
|
|
font-weight: 500;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.collection-description {
|
|
margin-top: var(--spacing-card-sm);
|
|
margin-bottom: 0;
|
|
}
|
|
</style>
|