mirror of
https://github.com/modrinth/code.git
synced 2026-08-27 10:04:52 +00:00
feat: backups alignment with Figma (#5559)
* feat: backup admonitions * feat: align modals + fix backupitem * fix: body needs opac 80 * fix: lint
This commit is contained in:
@@ -17,7 +17,6 @@ import { defineMessages, useVIntl } from '../../../composables/i18n'
|
||||
import { commonMessages } from '../../../utils'
|
||||
import ButtonStyled from '../../base/ButtonStyled.vue'
|
||||
import OverflowMenu, { type Option as OverflowOption } from '../../base/OverflowMenu.vue'
|
||||
import ProgressBar from '../../base/ProgressBar.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
@@ -48,12 +47,6 @@ const props = withDefaults(
|
||||
},
|
||||
)
|
||||
|
||||
const backupQueued = computed(
|
||||
() =>
|
||||
props.backup.status === 'pending' ||
|
||||
props.backup.task?.create?.progress === 0 ||
|
||||
(props.backup.status === 'in_progress' && !props.backup.task?.create),
|
||||
)
|
||||
const failedToCreate = computed(
|
||||
() => props.backup.status === 'error' || props.backup.status === 'timed_out',
|
||||
)
|
||||
@@ -63,30 +56,30 @@ const inactiveStates = ['failed', 'cancelled', 'done']
|
||||
const creating = computed(() => {
|
||||
const task = props.backup.task?.create
|
||||
if (task && task.progress < 1 && !inactiveStates.includes(task.state)) {
|
||||
return task
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
(props.backup.status === 'in_progress' || props.backup.status === 'pending') &&
|
||||
!props.backup.task?.restore
|
||||
) {
|
||||
return { progress: 0, state: 'ongoing' as const }
|
||||
return true
|
||||
}
|
||||
return undefined
|
||||
return false
|
||||
})
|
||||
|
||||
const restoring = computed(() => {
|
||||
const task = props.backup.task?.restore
|
||||
if (task && task.progress < 1 && !inactiveStates.includes(task.state)) {
|
||||
return task
|
||||
return true
|
||||
}
|
||||
return undefined
|
||||
return false
|
||||
})
|
||||
|
||||
const restoreQueued = computed(() => restoring.value?.progress === 0)
|
||||
|
||||
const failedToRestore = computed(() => props.backup.task?.restore?.state === 'failed')
|
||||
|
||||
const activeOperation = computed(() => creating.value || restoring.value)
|
||||
|
||||
const backupIcon = computed(() => {
|
||||
if (props.backup.automated) {
|
||||
return ClockIcon
|
||||
@@ -97,8 +90,7 @@ const backupIcon = computed(() => {
|
||||
const overflowMenuOptions = computed<OverflowOption[]>(() => {
|
||||
const options: OverflowOption[] = []
|
||||
|
||||
// Download only available when not creating
|
||||
if (!creating.value) {
|
||||
if (!activeOperation.value) {
|
||||
options.push({
|
||||
id: 'download',
|
||||
action: () => emit('download'),
|
||||
@@ -109,8 +101,7 @@ const overflowMenuOptions = computed<OverflowOption[]>(() => {
|
||||
|
||||
options.push({ id: 'rename', action: () => emit('rename') })
|
||||
|
||||
// Delete only available when not creating (has separate Cancel button)
|
||||
if (!creating.value) {
|
||||
if (!activeOperation.value) {
|
||||
options.push({ divider: true })
|
||||
options.push({
|
||||
id: 'delete',
|
||||
@@ -138,22 +129,6 @@ const messages = defineMessages({
|
||||
id: 'servers.backups.item.rename',
|
||||
defaultMessage: 'Rename',
|
||||
},
|
||||
queuedForBackup: {
|
||||
id: 'servers.backups.item.queued-for-backup',
|
||||
defaultMessage: 'Backup queued',
|
||||
},
|
||||
queuedForRestore: {
|
||||
id: 'servers.backups.item.queued-for-restore',
|
||||
defaultMessage: 'Restore queued',
|
||||
},
|
||||
creatingBackup: {
|
||||
id: 'servers.backups.item.creating-backup',
|
||||
defaultMessage: 'Creating backup...',
|
||||
},
|
||||
restoringBackup: {
|
||||
id: 'servers.backups.item.restoring-backup',
|
||||
defaultMessage: 'Restoring from backup...',
|
||||
},
|
||||
failedToCreateBackup: {
|
||||
id: 'servers.backups.item.failed-to-create-backup',
|
||||
defaultMessage: 'Failed to create backup',
|
||||
@@ -179,7 +154,7 @@ const messages = defineMessages({
|
||||
<template>
|
||||
<div
|
||||
class="grid items-center gap-4 rounded-2xl bg-bg-raised p-4 shadow-md"
|
||||
:class="preview ? 'grid-cols-2' : 'grid-cols-[auto_1fr_auto] md:grid-cols-[1fr_400px_1fr]'"
|
||||
:class="preview ? 'grid-cols-1' : 'grid-cols-[auto_1fr_auto] md:grid-cols-[1fr_400px_1fr]'"
|
||||
>
|
||||
<div class="flex flex-row gap-4 items-center">
|
||||
<div
|
||||
@@ -199,7 +174,10 @@ const messages = defineMessages({
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 text-sm text-secondary">
|
||||
<template v-if="failedToCreate || failedToRestore">
|
||||
<template v-if="preview">
|
||||
<span>{{ formatDateTime(backup.created_at) }}</span>
|
||||
</template>
|
||||
<template v-else-if="failedToCreate || failedToRestore">
|
||||
<XIcon class="size-4 text-red" />
|
||||
<span class="text-red">
|
||||
{{
|
||||
@@ -228,95 +206,42 @@ const messages = defineMessages({
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!preview"
|
||||
class="col-span-full row-start-2 flex flex-col gap-2 md:col-span-1 md:row-start-auto md:items-center"
|
||||
>
|
||||
<template v-if="creating || restoring">
|
||||
<ProgressBar
|
||||
:progress="(creating || restoring)!.progress"
|
||||
:color="creating ? 'brand' : 'purple'"
|
||||
:waiting="(creating || restoring)!.progress === 0"
|
||||
:label="
|
||||
formatMessage(
|
||||
creating
|
||||
? backupQueued
|
||||
? messages.queuedForBackup
|
||||
: messages.creatingBackup
|
||||
: restoreQueued
|
||||
? messages.queuedForRestore
|
||||
: messages.restoringBackup,
|
||||
)
|
||||
"
|
||||
:label-class="creating ? 'text-contrast' : 'text-purple'"
|
||||
show-progress
|
||||
full-width
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="w-full font-medium text-contrast md:text-center">
|
||||
{{ formatDateTime(backup.created_at) }}
|
||||
</span>
|
||||
<!-- TODO: Uncomment when API supports size field -->
|
||||
<!-- <span class="text-secondary">{{ formatBytes(backup.size) }}</span> -->
|
||||
</template>
|
||||
<span class="w-full font-medium text-contrast md:text-center">
|
||||
{{ formatDateTime(backup.created_at) }}
|
||||
</span>
|
||||
<!-- TODO: Uncomment when API supports size field -->
|
||||
<!-- <span class="text-secondary">{{ formatBytes(backup.size) }}</span> -->
|
||||
</div>
|
||||
|
||||
<div v-if="!preview" class="flex shrink-0 items-center gap-2 md:justify-self-end">
|
||||
<template v-if="failedToCreate">
|
||||
<ButtonStyled>
|
||||
<button @click="() => emit('retry')">
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
{{ formatMessage(commonMessages.retryButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="() => emit('delete', true)">
|
||||
<TrashIcon class="size-5" />
|
||||
{{ formatMessage(commonMessages.deleteLabel) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<template v-else-if="creating">
|
||||
<ButtonStyled type="outlined">
|
||||
<button class="!border-[1px] !border-surface-5" @click="() => emit('delete')">
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu :options="overflowMenuOptions">
|
||||
<MoreVerticalIcon class="size-5" />
|
||||
<template #rename>
|
||||
<EditIcon class="size-5" /> {{ formatMessage(messages.rename) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ButtonStyled color="brand" type="outlined">
|
||||
<button
|
||||
v-tooltip="props.restoreDisabled"
|
||||
class="!border-[1px]"
|
||||
:disabled="!!props.restoreDisabled"
|
||||
@click="() => emit('restore')"
|
||||
>
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
{{ formatMessage(messages.restore) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu :options="overflowMenuOptions">
|
||||
<MoreVerticalIcon class="size-5" />
|
||||
<template #download>
|
||||
<DownloadIcon class="size-5" /> {{ formatMessage(commonMessages.downloadButton) }}
|
||||
</template>
|
||||
<template #rename>
|
||||
<EditIcon class="size-5" /> {{ formatMessage(messages.rename) }}
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon class="size-5" /> {{ formatMessage(commonMessages.deleteLabel) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<ButtonStyled v-if="!activeOperation" color="brand" type="outlined">
|
||||
<button
|
||||
v-tooltip="props.restoreDisabled"
|
||||
class="!border-[1px]"
|
||||
:disabled="!!props.restoreDisabled"
|
||||
@click="() => emit('restore')"
|
||||
>
|
||||
<RotateCounterClockwiseIcon class="size-5" />
|
||||
{{ formatMessage(messages.restore) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu :options="overflowMenuOptions">
|
||||
<MoreVerticalIcon class="size-5" />
|
||||
<template #download>
|
||||
<DownloadIcon class="size-5" /> {{ formatMessage(commonMessages.downloadButton) }}
|
||||
</template>
|
||||
<template #rename>
|
||||
<EditIcon class="size-5" /> {{ formatMessage(messages.rename) }}
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon class="size-5" /> {{ formatMessage(commonMessages.deleteLabel) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
|
||||
<pre v-if="!preview && showDebugInfo" class="w-full rounded-xl bg-surface-4 p-2 text-xs">{{
|
||||
|
||||
Reference in New Issue
Block a user