Compare commits

...
25 changed files with 769 additions and 702 deletions
@@ -1,111 +1,83 @@
<template>
<div>
<Modal ref="modalOpen" header="Transfer Projects">
<div class="universal-modal items">
<div class="table">
<div class="table-head table-row">
<div class="check-cell table-cell">
<Checkbox
:model-value="selectedProjects.length === props.projects.length"
@update:model-value="toggleSelectedProjects()"
/>
<NewModal ref="modalOpen" header="Transfer projects" no-padding width="60rem">
<div class="max-h-[70vh] overflow-y-auto">
<Table
class="!rounded-none !border-0"
:columns="projectTableColumns"
:data="props.projects"
row-key="id"
table-min-width="42rem"
table-layout="auto"
>
<template #empty-state>
<div class="flex h-48 items-center justify-center text-secondary">
No projects available to transfer.
</div>
<div class="table-cell">Icon</div>
<div class="table-cell">Name</div>
<div class="table-cell">ID</div>
<div class="table-cell">Type</div>
<div class="table-cell" />
</div>
<div v-for="project in props.projects" :key="`project-${project.id}`" class="table-row">
<div class="check-cell table-cell">
<Checkbox
:disabled="(project.permissions & EDIT_DETAILS) === EDIT_DETAILS"
:model-value="selectedProjects.includes(project)"
@update:model-value="
selectedProjects.includes(project)
? (selectedProjects = selectedProjects.filter((it) => it !== project))
: selectedProjects.push(project)
"
/>
</div>
<div class="table-cell">
<nuxt-link tabindex="-1" :to="`/project/${project.slug ? project.slug : project.id}`">
<Avatar
</template>
<template #header-select>
<Checkbox
class="h-full w-full justify-center"
:model-value="allTransferableProjectsSelected"
@update:model-value="toggleAllTransferableProjects()"
/>
</template>
<template #cell-select="{ row: project }">
<Checkbox
class="h-full w-full justify-center"
:disabled="isProjectTransferDisabled(project)"
:model-value="isProjectSelected(project)"
@update:model-value="toggleProjectSelection(project)"
/>
</template>
<template #cell-name="{ row: project }">
<nuxt-link class="project-name-cell" :to="getProjectUrl(project)">
<span
class="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded text-primary"
>
<img
v-if="project.icon_url"
:src="project.icon_url"
aria-hidden="true"
:alt="'Icon for ' + project.name"
no-shadow
:alt="'Icon for ' + project.title"
class="h-full w-full rounded object-cover"
/>
</nuxt-link>
</div>
<div class="table-cell">
<span class="project-title">
<nuxt-link
class="hover-link wrap-as-needed"
:to="`/project/${project.slug ? project.slug : project.id}`"
>
{{ project.title }}
</nuxt-link>
<BoxIcon v-else class="h-full w-full" />
</span>
</div>
<div class="table-cell">
<span class="wrap-as-needed">{{ project.title }}</span>
</nuxt-link>
</template>
<template #cell-id="{ row: project }">
<div class="flex items-center">
<CopyCode :text="project.id" />
</div>
<div class="table-cell">
<BoxIcon />
<span>{{
formatProjectType(
$getProjectTypeForDisplay(
project.project_types?.[0] ?? 'project',
project.loaders,
),
)
}}</span>
</template>
<template #cell-type="{ row: project }">
<div class="flex items-center">
{{ getProjectDisplayType(project) }}
</div>
<div class="table-cell">
<ButtonStyled circular>
<nuxt-link :to="`/project/${project.slug ? project.slug : project.id}/settings`">
<SettingsIcon />
</nuxt-link>
</ButtonStyled>
</div>
</div>
</div>
<div class="push-right input-group">
</template>
</Table>
</div>
<template #actions>
<div class="flex justify-end gap-2">
<ButtonStyled type="outlined">
<button @click="$refs.modalOpen?.hide()">
<button @click="hide()">
<XIcon />
Cancel
{{ formatMessage(commonMessages.cancelButton) }}
</button>
</ButtonStyled>
<ButtonStyled color="brand">
<button :disabled="!selectedProjects?.length" @click="onSubmitHandler()">
<button :disabled="selectedProjects.length === 0" @click="submitTransfer()">
<TransferIcon />
<span>
Transfer
<span>
{{
selectedProjects.length === props.projects.length
? 'All'
: selectedProjects.length
}}
</span>
<span>
{{ ' ' }}
{{ selectedProjects.length === 1 ? 'project' : 'projects' }}
</span>
</span>
Transfer {{ selectedProjects.length }}
{{ selectedProjects.length === 1 ? 'project' : 'projects' }}
</button>
</ButtonStyled>
</div>
</div>
</Modal>
</template>
</NewModal>
<ButtonStyled>
<button @click="$refs.modalOpen?.show()">
<button @click="show($event)">
<TransferIcon />
<span>Transfer projects</span>
</button>
@@ -114,12 +86,21 @@
</template>
<script setup>
import { BoxIcon, SettingsIcon, TransferIcon, XIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, Checkbox, CopyCode, Modal } from '@modrinth/ui'
import { BoxIcon, TransferIcon, XIcon } from '@modrinth/assets'
import {
ButtonStyled,
Checkbox,
commonMessages,
CopyCode,
NewModal,
Table,
useVIntl,
} from '@modrinth/ui'
import { formatProjectType } from '@modrinth/utils'
import { getProjectTypeForUrl } from '~/helpers/projects.js'
const EDIT_DETAILS = 1 << 2
const modalOpen = ref(null)
const props = defineProps({
projects: {
@@ -128,126 +109,113 @@ const props = defineProps({
},
})
// define emit for submission
const emit = defineEmits(['submit'])
const { formatMessage } = useVIntl()
const selectedProjects = ref([])
const modalOpen = ref(null)
const selectedProjectIds = ref([])
const toggleSelectedProjects = () => {
if (selectedProjects.value.length === props.projects.length) {
selectedProjects.value = []
} else {
selectedProjects.value = props.projects
}
const projectTableColumns = [
{
key: 'select',
width: '3rem',
headerClass: '!p-0',
cellClass: '!overflow-visible !p-0',
},
{
key: 'name',
label: 'Name',
width: '22rem',
},
{
key: 'id',
label: 'ID',
width: '13rem',
cellClass: '!overflow-visible',
},
{
key: 'type',
label: 'Type',
width: '10rem',
},
]
const transferableProjects = computed(() =>
props.projects.filter((project) => !isProjectTransferDisabled(project)),
)
const selectedProjects = computed(() =>
props.projects.filter((project) => selectedProjectIds.value.includes(project.id)),
)
const allTransferableProjectsSelected = computed(
() =>
transferableProjects.value.length > 0 &&
transferableProjects.value.every((project) => selectedProjectIds.value.includes(project.id)),
)
function isProjectTransferDisabled(project) {
return (project.permissions & EDIT_DETAILS) === EDIT_DETAILS
}
const onSubmitHandler = () => {
if (selectedProjects.value.length === 0) {
function isProjectSelected(project) {
return selectedProjectIds.value.includes(project.id)
}
function toggleProjectSelection(project) {
if (isProjectTransferDisabled(project)) return
if (isProjectSelected(project)) {
selectedProjectIds.value = selectedProjectIds.value.filter((id) => id !== project.id)
return
}
emit('submit', selectedProjects.value)
selectedProjects.value = []
selectedProjectIds.value = [...selectedProjectIds.value, project.id]
}
function toggleAllTransferableProjects() {
selectedProjectIds.value = allTransferableProjectsSelected.value
? []
: transferableProjects.value.map((project) => project.id)
}
function getProjectUrl(project) {
const projectType = getProjectTypeForUrl(project.project_type, project.loaders)
return '/' + projectType + '/' + (project.slug || project.id)
}
function getProjectDisplayType(project) {
return formatProjectType(getProjectTypeForUrl(project.project_type, project.loaders))
}
function show(event) {
modalOpen.value?.show(event)
}
function hide() {
modalOpen.value?.hide()
}
function submitTransfer() {
if (selectedProjects.value.length === 0) return
emit('submit', selectedProjects.value)
selectedProjectIds.value = []
hide()
}
</script>
<style lang="scss" scoped>
.table {
display: grid;
border-radius: var(--radius-md);
overflow: hidden;
margin-top: var(--gap-md);
border: 1px solid var(--color-divider);
background-color: var(--color-raised-bg);
.table-row {
grid-template-columns: 2.75rem 3.75rem 2fr 1fr 1fr 3.5rem;
}
.table-cell {
display: flex;
align-items: center;
gap: var(--gap-xs);
padding: var(--gap-md);
padding-left: 0;
}
.check-cell {
padding-left: var(--gap-md);
}
@media screen and (max-width: 750px) {
display: flex;
flex-direction: column;
.table-row {
display: grid;
grid-template: 'checkbox icon name type settings' 'checkbox icon id type settings';
grid-template-columns:
min-content min-content minmax(min-content, 2fr)
minmax(min-content, 1fr) min-content;
:nth-child(1) {
grid-area: checkbox;
}
:nth-child(2) {
grid-area: icon;
}
:nth-child(3) {
grid-area: name;
}
:nth-child(4) {
grid-area: id;
padding-top: 0;
}
:nth-child(5) {
grid-area: type;
}
:nth-child(6) {
grid-area: settings;
}
}
.table-head {
grid-template: 'checkbox settings';
grid-template-columns: min-content minmax(min-content, 1fr);
:nth-child(2),
:nth-child(3),
:nth-child(4),
:nth-child(5) {
display: none;
}
}
}
@media screen and (max-width: 560px) {
.table-row {
display: grid;
grid-template: 'checkbox icon name settings' 'checkbox icon id settings' 'checkbox icon type settings' 'checkbox icon status settings';
grid-template-columns: min-content min-content minmax(min-content, 1fr) min-content;
:nth-child(5) {
padding-top: 0;
}
}
.table-head {
grid-template: 'checkbox settings';
grid-template-columns: min-content minmax(min-content, 1fr);
}
}
.project-name-cell {
display: flex;
min-width: 0;
min-height: 3.5rem;
align-items: center;
gap: var(--spacing-card-sm);
color: inherit;
text-decoration: none;
}
.items {
display: flex;
flex-direction: column;
gap: var(--gap-md);
padding: var(--gap-md);
.project-name-cell:hover,
.project-name-cell:focus-visible {
text-decoration: underline;
}
</style>
@@ -2999,9 +2999,6 @@
"muralpay.warning.wallet-address": {
"message": "Überprüfe deine Wallet-Adresse. An eine falsche Adresse gesendete Gelder können nicht wiederhergestellt werden."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Die Änderungen werden auf {count, plural, one {# Projekt} other {# Projekte}} angewendet."
},
"profile.bio.fallback.creator": {
"message": "Ein Modrinth Ersteller."
},
@@ -2999,9 +2999,6 @@
"muralpay.warning.wallet-address": {
"message": "Überprüfe deine Wallet-Adresse. An eine falsche Adresse gesendete Gelder können nicht wiederhergestellt werden."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Änderungen werden auf {count, plural, one {# Projekt} other {# Projekte}} angewendet."
},
"profile.bio.fallback.creator": {
"message": "Ein Modrinth-Ersteller."
},
@@ -3041,9 +3041,6 @@
"organization.label.projects": {
"message": "projects"
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Changes will be applied to {count, plural, one {# project} other {# projects}}."
},
"profile.bio.fallback.creator": {
"message": "A Modrinth creator."
},
@@ -2999,9 +2999,6 @@
"muralpay.warning.wallet-address": {
"message": "Verifica dos veces la dirección de tu billetera. Los fondos enviados a una dirección incorrecta no se pueden recuperar."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Estos cambios serán aplicados {count, plural, one {al proyecto} other {a los # proyectos}}."
},
"profile.bio.fallback.creator": {
"message": "Un creador de Modrinth."
},
@@ -2822,9 +2822,6 @@
"muralpay.warning.wallet-address": {
"message": "Verifique la dirección de su billetera. Los fondos enviados a una dirección incorrecta no se pueden recuperar."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Estos cambios serán aplicados {count, plural, one {al proyecto} other {a los # proyectos}}."
},
"profile.bio.fallback.creator": {
"message": "Creador en Modrinth."
},
@@ -3005,9 +3005,6 @@
"muralpay.warning.wallet-address": {
"message": "Vérifiez votre adresse. Les fonds envoyés à une adresse incorrecte ne pourront pas être récupérés."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Les modifications seront appliquées à {count, plural, one {# projet} other {# projets}}."
},
"profile.bio.fallback.creator": {
"message": "Un créateur Modrinth."
},
@@ -2897,9 +2897,6 @@
"muralpay.warning.wallet-address": {
"message": "Nézd át jól a pénztárca címedet. Az egyenleg rossz címre lesz küldve az nem szerezhető vissza."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "A módosítások {count, plural, one {# projktre} other {# projektekre}} fognak vonatkozni."
},
"profile.bio.fallback.creator": {
"message": "Egy Modrinth fejlesztő."
},
@@ -2990,9 +2990,6 @@
"muralpay.warning.wallet-address": {
"message": "Controlla nuovamente il tuo conto. Fondi mandati a un conto errato non possono essere recuperati."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Le modifiche verranno applicate a {count, plural, one {# progetto} other {# progetti}}."
},
"profile.bio.fallback.creator": {
"message": "Creatore su Modrinth."
},
@@ -2999,9 +2999,6 @@
"muralpay.warning.wallet-address": {
"message": "지갑 주소를 다시 확인하세요. 잘못된 주소로 송금된 자금은 복구할 수 없습니다."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "변경 사항이 {count, plural, one {# 개 프로젝트} other {# 개 프로젝트}}에 적용됩니다."
},
"profile.bio.fallback.creator": {
"message": "Modrinth 창작자입니다."
},
@@ -2684,9 +2684,6 @@
"muralpay.warning.wallet-address": {
"message": "Semak semula alamat dompet anda. Dana yang dihantar ke alamat yang salah tidak boleh dipulihkan."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Pemasangan akan diterapkan kepada {count, plural, other {# projek}}."
},
"profile.bio.fallback.creator": {
"message": "Seorang pencipta Modrinth."
},
@@ -2996,9 +2996,6 @@
"muralpay.warning.wallet-address": {
"message": "Controleer uw portemonnee adres nogmaals. Geld dat naar een onjuist adres is overgemaakt, kan niet worden teruggevonden."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "De wijzigingen worden toegepast op {count, plural, one {# project} other {# projects}}."
},
"profile.bio.fallback.creator": {
"message": "Een Modrinth creator."
},
@@ -2609,9 +2609,6 @@
"muralpay.warning.wallet-address": {
"message": "Dobbeltsjekk lommeboksadressa di. Penger sendt til feil adresse kan ikke bli gjenoppretta."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Endringer blir utført på {count, plural, one {prosjekt} other {prosjekter}}."
},
"profile.bio.fallback.creator": {
"message": "En Modrinth-skaper."
},
@@ -2993,9 +2993,6 @@
"muralpay.warning.wallet-address": {
"message": "Sprawdź podwójnie adres portfela. Fundusze przesłane na nieprawidłowy portfel nie mogą być odzyskane."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Zmiany zostaną zastosowane dla {count, plural, one {# projektu} other {# projektów}}."
},
"profile.bio.fallback.creator": {
"message": "Twórca na Modrinth."
},
@@ -3005,9 +3005,6 @@
"muralpay.warning.wallet-address": {
"message": "Verifique novamente seu endereço da carteira. Fundos enviados para um endereço incorreto não podem ser recuperados."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "As alterações serão aplicadas a {count, plural, one {# projeto} other {# projetos}}."
},
"profile.bio.fallback.creator": {
"message": "Um criador Modrinth."
},
@@ -2996,9 +2996,6 @@
"muralpay.warning.wallet-address": {
"message": "Перепроверьте адрес кошелька. Отправку средств на неверный адрес невозможно отменить."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Изменения будут применены к {count, plural, one {# проекту} other {# проектам}}."
},
"profile.bio.fallback.creator": {
"message": "Автор на Modrinth."
},
@@ -2675,9 +2675,6 @@
"muralpay.rail.fiat-zar.name": {
"message": "Banköverföring (ZAR)"
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Ändringar kommer tillämpas på {count} projekt."
},
"profile.bio.fallback.creator": {
"message": "En Modrinth kreatör."
},
@@ -2999,9 +2999,6 @@
"muralpay.warning.wallet-address": {
"message": "Cüzdan adresinizi iki kez kontrol edin. Yanlış adrese gönderilen fonlar kurtarılamaz."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Değişiklikler {count, plural, one {# project} other {# projects}} için uygulanacaktır."
},
"profile.bio.fallback.creator": {
"message": "Bir Modrinth içerik üreticisi."
},
@@ -3002,9 +3002,6 @@
"muralpay.warning.wallet-address": {
"message": "Ще раз перевірте адресу свого гаманця. Кошти, надіслані на неправильну адресу, не можуть бути відновлені."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "Зміни будуть застосовані для {count, plural, one {# проєкту} few {# проєктів} many {# проєктів} other {# проєктів}}."
},
"profile.bio.fallback.creator": {
"message": "Творець Modrinth."
},
@@ -2792,9 +2792,6 @@
"muralpay.warning.wallet-address": {
"message": "Hãy kiểm tra kỹ địa chỉ ví. Số tiền gửi đến địa chỉ không chính xác sẽ không thể khôi phục."
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "{count, plural, other {# dự án}} sẽ được thay đổi."
},
"profile.bio.fallback.creator": {
"message": "Một nhà sáng tạo trên Modrinth."
},
@@ -3002,9 +3002,6 @@
"muralpay.warning.wallet-address": {
"message": "请再次确认你的钱包地址。发送到错误地址的资金将无法追回。"
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "将对{count, plural,one {# 项目}other {# 项目}}应用更改。"
},
"profile.bio.fallback.creator": {
"message": "一位 Modrinth 创作者。"
},
@@ -3005,9 +3005,6 @@
"muralpay.warning.wallet-address": {
"message": "請仔細檢查你的錢包帳戶。發送到錯誤帳戶的款項將無法追回。"
},
"organization.settings.projects.edit-links.affected-projects": {
"message": "變更將套用於 {count, plural, other {# 個專案}}。"
},
"profile.bio.fallback.creator": {
"message": "一位 Modrinth 創作者。"
},
+14 -11
View File
@@ -188,22 +188,25 @@
</div>
</template>
<template #header-select>
<div v-tooltip="formatMessage(messages.selectAllBulkEditableProjects)">
<div
v-tooltip="formatMessage(messages.selectAllBulkEditableProjects)"
class="h-full w-full"
>
<Checkbox
class="h-full w-full justify-center"
:model-value="allBulkEditableProjectsSelected"
@update:model-value="toggleAllBulkEditableProjects()"
/>
</div>
</template>
<template #cell-select="{ row: project }">
<div>
<Checkbox
v-tooltip="getBulkEditDisabledTooltip(project)"
:disabled="isProjectBulkEditDisabled(project)"
:model-value="isProjectSelected(project)"
@update:model-value="toggleProjectSelection(project)"
/>
</div>
<Checkbox
v-tooltip="getBulkEditDisabledTooltip(project)"
class="h-full w-full justify-center"
:disabled="isProjectBulkEditDisabled(project)"
:model-value="isProjectSelected(project)"
@update:model-value="toggleProjectSelection(project)"
/>
</template>
<template #cell-name="{ row: project }">
<nuxt-link class="project-name-cell" :to="getProjectUrl(project)">
@@ -542,8 +545,8 @@ const projectTableColumns = computed(() => [
{
key: 'select',
width: '3rem',
headerClass: '!text-center',
cellClass: '!overflow-visible',
headerClass: '!p-0',
cellClass: '!overflow-visible !p-0',
},
{
key: 'name',
@@ -18,7 +18,7 @@
<Avatar size="sm" :src="organization.icon_url" />
<div class="flex flex-col justify-center gap-1">
<h2 class="m-0 text-base">
<nuxt-link :to="`/organization/${organization.slug}/settings`">
<nuxt-link :to="`/organization/${organization.slug}`">
{{ organization.name }}
</nuxt-link>
</h2>
File diff suppressed because it is too large Load Diff