123 lines
3.8 KiB
Vue
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>
|