Files
RenewCMS/web/src/components/DuplicateGroups.vue
T

123 lines
3.8 KiB
Vue

<script setup lang="ts">
import { t } from '@/i18n'
import MediaThumb from '@/components/MediaThumb.vue'
import type { MediaItem } from '@/composables/useMedia'
import type { DuplicateGroup } from '@/lib/mediaDuplicates'
import { formatMediaDate, pluralArticles } from '@/lib/mediaFormat'
const FOOTER =
'Suggested: the most used copy, then the oldest. Choosing another copy switches articles to it.'
defineProps<{
groups: DuplicateGroup[]
offline: boolean
}>()
const emit = defineEmits<{
merge: [group: DuplicateGroup, keep: MediaItem]
open: [id: number, opener: HTMLElement]
}>()
function uploaded(copy: MediaItem) {
const by = copy.uploaded_by ? t(' by {0}', [copy.uploaded_by]) : ''
return t('Uploaded {0}{1}', [formatMediaDate(copy.created_at), by])
}
</script>
<template>
<div class="flex flex-col gap-4">
<section
v-for="(group, index) in groups"
:key="group.hash"
data-testid="duplicate-group"
class="border border-default bg-default"
>
<header class="flex flex-col gap-0.5 border-b border-default px-3.5 py-3">
<h3 class="font-semibold text-highlighted">{{ t('Group') }} {{ index + 1 }}</h3>
<span class="text-muted">{{ group.header }}</span>
</header>
<div
class="grid md:grid-cols-(--columns) md:gap-4 md:p-4"
:style="{
'--columns': `repeat(${group.copies.length}, minmax(0, 1fr))`,
}"
>
<div
v-for="copy in group.copies"
:key="copy.id"
data-testid="duplicate-copy"
:data-suggested="copy.id === group.suggested.id || undefined"
class="flex flex-col gap-3 max-md:border-b max-md:border-muted max-md:p-3.5 max-md:last:border-b-0 md:border md:p-3"
:class="
copy.id === group.suggested.id
? 'bg-secondary-200 text-primary-900 md:border-primary dark:bg-secondary-300'
: 'bg-default text-default md:border-default'
"
>
<div class="flex gap-3 max-md:items-start md:flex-col">
<MediaThumb
:item="copy"
class="border border-default max-md:h-18 max-md:w-24 md:aspect-4/3 md:w-full"
/>
<div class="flex min-w-0 flex-col gap-1">
<div class="flex flex-wrap items-center gap-1.5">
<span
class="truncate font-medium"
:class="
copy.id === group.suggested.id
? 'text-primary-900'
: 'text-highlighted'
"
>{{ copy.filename }}</span
>
<span
v-if="copy.id === group.suggested.id"
class="inline-flex h-5 items-center bg-primary px-1.5 text-xs font-medium text-inverted"
>{{ t('Suggested') }}</span
>
</div>
<span
class="text-xs/5"
:class="
copy.id === group.suggested.id
? 'text-primary-600'
: 'text-muted'
"
>{{ uploaded(copy) }}</span
>
<button
type="button"
data-testid="duplicate-used-in"
class="self-start font-medium underline underline-offset-3 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
@click="emit('open', copy.id, $event.currentTarget as HTMLElement)"
>
{{ t('Used in') }}
{{ pluralArticles(copy.used_in) }}
</button>
</div>
</div>
<UButton
icon="i-lucide-merge"
:color="copy.id === group.suggested.id ? 'primary' : 'neutral'"
:variant="copy.id === group.suggested.id ? 'solid' : 'outline'"
:disabled="offline"
class="h-11 justify-center px-3 md:h-8"
:class="
copy.id === group.suggested.id
? ''
: 'border border-accented bg-default text-highlighted ring-0'
"
@click="emit('merge', group, copy)"
>
{{ t('Merge into this one') }}
</UButton>
</div>
</div>
<footer class="border-t border-default px-3.5 py-2.5 text-xs/5 text-muted">
{{ t(FOOTER) }}
</footer>
</section>
</div>
</template>