refactor(admin): restructure adult verification and erotic moderation views for improved layout

- Updated the AdultVerificationView and EroticModerationView components to utilize a new layout structure with content scrolling and hidden overflow for better user experience.
- Adjusted styles in styles.scss to support the new layout, ensuring proper height and overflow handling for content sections.
This commit is contained in:
Torsten Schulz (local)
2026-03-27 11:18:42 +01:00
parent 25b658acce
commit 02837c7b73
3 changed files with 178 additions and 165 deletions

View File

@@ -276,13 +276,16 @@ main,
}
.app-content__inner > .contenthidden {
height: auto;
overflow: visible;
flex: 1 1 auto;
min-height: 0;
height: 100%;
overflow: hidden;
}
.app-content__inner > .contenthidden > .contentscroll {
height: auto;
overflow: visible;
min-height: 0;
height: 100%;
overflow: auto;
}
.surface-card {

View File

@@ -1,97 +1,101 @@
<template>
<div class="adult-verification">
<section class="adult-verification__hero surface-card">
<span class="adult-verification__eyebrow">Administration</span>
<h1>{{ $t('admin.adultVerification.title') }}</h1>
<p>{{ $t('admin.adultVerification.intro') }}</p>
</section>
<div class="contenthidden">
<div class="contentscroll">
<div class="adult-verification">
<section class="adult-verification__hero surface-card">
<span class="adult-verification__eyebrow">Administration</span>
<h1>{{ $t('admin.adultVerification.title') }}</h1>
<p>{{ $t('admin.adultVerification.intro') }}</p>
</section>
<section class="adult-verification__filters surface-card">
<button
v-for="option in filterOptions"
:key="option.value"
type="button"
:class="{ active: statusFilter === option.value }"
@click="changeFilter(option.value)"
>
{{ option.label }}
</button>
</section>
<section class="adult-verification__filters surface-card">
<button
v-for="option in filterOptions"
:key="option.value"
type="button"
:class="{ active: statusFilter === option.value }"
@click="changeFilter(option.value)"
>
{{ option.label }}
</button>
</section>
<section class="adult-verification__list surface-card">
<div v-if="loading" class="adult-verification__state">{{ $t('general.loading') }}</div>
<div v-else-if="rows.length === 0" class="adult-verification__state">{{ $t('admin.adultVerification.empty') }}</div>
<table v-else>
<thead>
<tr>
<th>{{ $t('admin.adultVerification.username') }}</th>
<th>{{ $t('admin.adultVerification.age') }}</th>
<th>{{ $t('admin.adultVerification.statusLabel') }}</th>
<th>{{ $t('admin.adultVerification.requestLabel') }}</th>
<th>{{ $t('admin.adultVerification.actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :key="row.id">
<td>{{ row.username }}</td>
<td>{{ row.age }}</td>
<td>
<span class="adult-verification__badge" :class="`adult-verification__badge--${row.adultVerificationStatus}`">
{{ $t(`admin.adultVerification.status.${row.adultVerificationStatus}`) }}
</span>
</td>
<td class="adult-verification__request">
<template v-if="row.adultVerificationRequest">
<strong>{{ row.adultVerificationRequest.originalName }}</strong>
<span v-if="row.adultVerificationRequest.note">{{ row.adultVerificationRequest.note }}</span>
<span v-if="!row.adultVerificationDocumentAvailable" class="adult-verification__missing-file">
{{ $t('admin.adultVerification.documentMissing') }}
</span>
<button
type="button"
class="secondary"
:disabled="!row.adultVerificationDocumentAvailable"
@click="openDocument(row)"
>
{{ $t('admin.adultVerification.openDocument') }}
</button>
</template>
<span v-else></span>
</td>
<td class="adult-verification__actions">
<button type="button" @click="setStatus(row, 'approved')">{{ $t('admin.adultVerification.approve') }}</button>
<button type="button" class="secondary" @click="setStatus(row, 'rejected')">{{ $t('admin.adultVerification.reject') }}</button>
<button
v-if="row.adultVerificationStatus !== 'pending'"
type="button"
class="secondary"
@click="setStatus(row, 'pending')"
>
{{ $t('admin.adultVerification.resetPending') }}
</button>
</td>
</tr>
</tbody>
</table>
</section>
<section class="adult-verification__list surface-card">
<div v-if="loading" class="adult-verification__state">{{ $t('general.loading') }}</div>
<div v-else-if="rows.length === 0" class="adult-verification__state">{{ $t('admin.adultVerification.empty') }}</div>
<table v-else>
<thead>
<tr>
<th>{{ $t('admin.adultVerification.username') }}</th>
<th>{{ $t('admin.adultVerification.age') }}</th>
<th>{{ $t('admin.adultVerification.statusLabel') }}</th>
<th>{{ $t('admin.adultVerification.requestLabel') }}</th>
<th>{{ $t('admin.adultVerification.actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :key="row.id">
<td>{{ row.username }}</td>
<td>{{ row.age }}</td>
<td>
<span class="adult-verification__badge" :class="`adult-verification__badge--${row.adultVerificationStatus}`">
{{ $t(`admin.adultVerification.status.${row.adultVerificationStatus}`) }}
</span>
</td>
<td class="adult-verification__request">
<template v-if="row.adultVerificationRequest">
<strong>{{ row.adultVerificationRequest.originalName }}</strong>
<span v-if="row.adultVerificationRequest.note">{{ row.adultVerificationRequest.note }}</span>
<span v-if="!row.adultVerificationDocumentAvailable" class="adult-verification__missing-file">
{{ $t('admin.adultVerification.documentMissing') }}
</span>
<button
type="button"
class="secondary"
:disabled="!row.adultVerificationDocumentAvailable"
@click="openDocument(row)"
>
{{ $t('admin.adultVerification.openDocument') }}
</button>
</template>
<span v-else></span>
</td>
<td class="adult-verification__actions">
<button type="button" @click="setStatus(row, 'approved')">{{ $t('admin.adultVerification.approve') }}</button>
<button type="button" class="secondary" @click="setStatus(row, 'rejected')">{{ $t('admin.adultVerification.reject') }}</button>
<button
v-if="row.adultVerificationStatus !== 'pending'"
type="button"
class="secondary"
@click="setStatus(row, 'pending')"
>
{{ $t('admin.adultVerification.resetPending') }}
</button>
</td>
</tr>
</tbody>
</table>
</section>
<section v-if="previewUrl" class="adult-verification__preview surface-card">
<div class="adult-verification__preview-header">
<div>
<strong>{{ $t('admin.adultVerification.previewTitle') }}</strong>
<span v-if="previewRow">{{ previewRow.username }} · {{ previewRow.adultVerificationRequest?.originalName }}</span>
</div>
<button type="button" class="secondary" @click="clearPreview">
{{ $t('admin.adultVerification.closePreview') }}
</button>
<section v-if="previewUrl" class="adult-verification__preview surface-card">
<div class="adult-verification__preview-header">
<div>
<strong>{{ $t('admin.adultVerification.previewTitle') }}</strong>
<span v-if="previewRow">{{ previewRow.username }} · {{ previewRow.adultVerificationRequest?.originalName }}</span>
</div>
<button type="button" class="secondary" @click="clearPreview">
{{ $t('admin.adultVerification.closePreview') }}
</button>
</div>
<img v-if="isImagePreview" :src="previewUrl" class="adult-verification__preview-image" />
<iframe v-else-if="isPdfPreview" :src="previewUrl" class="adult-verification__preview-pdf" />
<div v-else class="adult-verification__state">
{{ $t('admin.adultVerification.previewUnavailable') }}
</div>
</section>
</div>
<img v-if="isImagePreview" :src="previewUrl" class="adult-verification__preview-image" />
<iframe v-else-if="isPdfPreview" :src="previewUrl" class="adult-verification__preview-pdf" />
<div v-else class="adult-verification__state">
{{ $t('admin.adultVerification.previewUnavailable') }}
</div>
</section>
</div>
</div>
</template>
@@ -195,6 +199,7 @@ export default {
.adult-verification {
display: grid;
gap: 18px;
padding-bottom: 24px;
}
.adult-verification__hero,

View File

@@ -1,78 +1,82 @@
<template>
<div class="adult-verification">
<section class="adult-verification__hero surface-card">
<span class="adult-verification__eyebrow">Administration</span>
<h1>{{ $t('admin.eroticModeration.title') }}</h1>
<p>{{ $t('admin.eroticModeration.intro') }}</p>
</section>
<div class="contenthidden">
<div class="contentscroll">
<div class="adult-verification">
<section class="adult-verification__hero surface-card">
<span class="adult-verification__eyebrow">Administration</span>
<h1>{{ $t('admin.eroticModeration.title') }}</h1>
<p>{{ $t('admin.eroticModeration.intro') }}</p>
</section>
<section class="adult-verification__filters surface-card">
<button
v-for="option in filterOptions"
:key="option.value"
type="button"
:class="{ active: statusFilter === option.value }"
@click="changeFilter(option.value)"
>
{{ option.label }}
</button>
</section>
<section class="adult-verification__filters surface-card">
<button
v-for="option in filterOptions"
:key="option.value"
type="button"
:class="{ active: statusFilter === option.value }"
@click="changeFilter(option.value)"
>
{{ option.label }}
</button>
</section>
<section class="adult-verification__list surface-card">
<div v-if="loading" class="adult-verification__state">{{ $t('general.loading') }}</div>
<div v-else-if="rows.length === 0" class="adult-verification__state">{{ $t('admin.eroticModeration.empty') }}</div>
<table v-else>
<thead>
<tr>
<th>{{ $t('admin.eroticModeration.target') }}</th>
<th>{{ $t('admin.eroticModeration.owner') }}</th>
<th>{{ $t('admin.eroticModeration.reporter') }}</th>
<th>{{ $t('admin.eroticModeration.reason') }}</th>
<th>{{ $t('admin.eroticModeration.statusLabel') }}</th>
<th>{{ $t('admin.eroticModeration.meta') }}</th>
<th>{{ $t('admin.eroticModeration.actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :key="row.id">
<td class="adult-verification__request">
<strong>{{ row.targetType === 'image' ? $t('admin.eroticModeration.image') : $t('admin.eroticModeration.video') }}</strong>
<span>{{ row.target?.title || '—' }}</span>
<span v-if="row.target?.isModeratedHidden" class="adult-verification__badge adult-verification__badge--rejected">
{{ $t('admin.eroticModeration.hidden') }}
</span>
<button v-if="row.target" type="button" class="secondary" @click="previewTarget(row)">
{{ $t('admin.eroticModeration.preview') }}
</button>
</td>
<td>{{ row.owner?.username || '—' }}</td>
<td>{{ row.reporter?.username || '—' }}</td>
<td class="adult-verification__request">
<strong>{{ $t(`socialnetwork.erotic.reportReasons.${row.reason}`) }}</strong>
<span v-if="row.note">{{ row.note }}</span>
</td>
<td>
<span class="adult-verification__badge" :class="`adult-verification__badge--${row.status}`">
{{ $t(`admin.eroticModeration.status.${row.status}`) }}
</span>
</td>
<td class="adult-verification__request">
<span>{{ formatDate(row.createdAt) }}</span>
<span v-if="row.actionTaken">{{ $t(`admin.eroticModeration.actionLabels.${row.actionTaken}`) }}</span>
<span v-if="row.handledAt">{{ formatDate(row.handledAt) }}</span>
</td>
<td class="adult-verification__actions">
<button type="button" @click="applyAction(row, 'dismiss')">{{ $t('admin.eroticModeration.dismiss') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'hide_content')">{{ $t('admin.eroticModeration.hide') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'restore_content')">{{ $t('admin.eroticModeration.restore') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'delete_content')">{{ $t('admin.eroticModeration.delete') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'block_uploads')">{{ $t('admin.eroticModeration.blockUploads') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'revoke_access')">{{ $t('admin.eroticModeration.revokeAccess') }}</button>
</td>
</tr>
</tbody>
</table>
</section>
<section class="adult-verification__list surface-card">
<div v-if="loading" class="adult-verification__state">{{ $t('general.loading') }}</div>
<div v-else-if="rows.length === 0" class="adult-verification__state">{{ $t('admin.eroticModeration.empty') }}</div>
<table v-else>
<thead>
<tr>
<th>{{ $t('admin.eroticModeration.target') }}</th>
<th>{{ $t('admin.eroticModeration.owner') }}</th>
<th>{{ $t('admin.eroticModeration.reporter') }}</th>
<th>{{ $t('admin.eroticModeration.reason') }}</th>
<th>{{ $t('admin.eroticModeration.statusLabel') }}</th>
<th>{{ $t('admin.eroticModeration.meta') }}</th>
<th>{{ $t('admin.eroticModeration.actions') }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :key="row.id">
<td class="adult-verification__request">
<strong>{{ row.targetType === 'image' ? $t('admin.eroticModeration.image') : $t('admin.eroticModeration.video') }}</strong>
<span>{{ row.target?.title || '—' }}</span>
<span v-if="row.target?.isModeratedHidden" class="adult-verification__badge adult-verification__badge--rejected">
{{ $t('admin.eroticModeration.hidden') }}
</span>
<button v-if="row.target" type="button" class="secondary" @click="previewTarget(row)">
{{ $t('admin.eroticModeration.preview') }}
</button>
</td>
<td>{{ row.owner?.username || '—' }}</td>
<td>{{ row.reporter?.username || '—' }}</td>
<td class="adult-verification__request">
<strong>{{ $t(`socialnetwork.erotic.reportReasons.${row.reason}`) }}</strong>
<span v-if="row.note">{{ row.note }}</span>
</td>
<td>
<span class="adult-verification__badge" :class="`adult-verification__badge--${row.status}`">
{{ $t(`admin.eroticModeration.status.${row.status}`) }}
</span>
</td>
<td class="adult-verification__request">
<span>{{ formatDate(row.createdAt) }}</span>
<span v-if="row.actionTaken">{{ $t(`admin.eroticModeration.actionLabels.${row.actionTaken}`) }}</span>
<span v-if="row.handledAt">{{ formatDate(row.handledAt) }}</span>
</td>
<td class="adult-verification__actions">
<button type="button" @click="applyAction(row, 'dismiss')">{{ $t('admin.eroticModeration.dismiss') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'hide_content')">{{ $t('admin.eroticModeration.hide') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'restore_content')">{{ $t('admin.eroticModeration.restore') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'delete_content')">{{ $t('admin.eroticModeration.delete') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'block_uploads')">{{ $t('admin.eroticModeration.blockUploads') }}</button>
<button type="button" class="secondary" @click="applyAction(row, 'revoke_access')">{{ $t('admin.eroticModeration.revokeAccess') }}</button>
</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
</div>
</template>
@@ -158,6 +162,7 @@ export default {
.adult-verification {
display: grid;
gap: 18px;
padding-bottom: 24px;
}
.adult-verification__hero,