Files
yourpart3/frontend/src/views/admin/AdultVerificationView.vue

259 lines
7.3 KiB
Vue

<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>
<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>
<button type="button" class="secondary" @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>
</div>
</template>
<script>
import apiClient from '@/utils/axios.js';
import { showApiError, showSuccess } from '@/utils/feedback.js';
export default {
name: 'AdminAdultVerificationView',
data() {
return {
loading: false,
statusFilter: 'pending',
rows: []
};
},
computed: {
filterOptions() {
return [
{ value: 'pending', label: this.$t('admin.adultVerification.filters.pending') },
{ value: 'approved', label: this.$t('admin.adultVerification.filters.approved') },
{ value: 'rejected', label: this.$t('admin.adultVerification.filters.rejected') },
{ value: 'all', label: this.$t('admin.adultVerification.filters.all') }
];
}
},
methods: {
async load() {
this.loading = true;
try {
const response = await apiClient.get('/api/admin/users/adult-verification', {
params: { status: this.statusFilter }
});
this.rows = response.data || [];
} catch (error) {
this.rows = [];
showApiError(this, error, this.$t('admin.adultVerification.loadError'));
} finally {
this.loading = false;
}
},
async changeFilter(filter) {
this.statusFilter = filter;
await this.load();
},
async setStatus(row, status) {
try {
await apiClient.put(`/api/admin/users/${row.id}/adult-verification`, { status });
showSuccess(this, this.$t(`admin.adultVerification.messages.${status}`));
await this.load();
} catch (error) {
showApiError(this, error, this.$t('admin.adultVerification.updateError'));
}
},
async openDocument(row) {
try {
const response = await apiClient.get(`/api/admin/users/${row.id}/adult-verification/document`, {
responseType: 'blob'
});
const url = window.URL.createObjectURL(response.data);
window.open(url, '_blank', 'noopener');
window.setTimeout(() => window.URL.revokeObjectURL(url), 10000);
} catch (error) {
showApiError(this, error, this.$t('admin.adultVerification.documentError'));
}
}
},
async mounted() {
await this.load();
}
};
</script>
<style scoped>
.adult-verification {
display: grid;
gap: 18px;
}
.adult-verification__hero,
.adult-verification__filters,
.adult-verification__list {
background: linear-gradient(180deg, rgba(255, 252, 247, 0.97), rgba(250, 244, 235, 0.95));
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-soft);
padding: 22px 24px;
}
.adult-verification__eyebrow {
display: inline-flex;
margin-bottom: 8px;
padding: 4px 10px;
border-radius: var(--radius-pill);
background: var(--color-secondary-soft);
color: var(--color-text-secondary);
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.adult-verification__hero p,
.adult-verification__state {
margin: 0;
color: var(--color-text-secondary);
}
.adult-verification__filters {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.adult-verification__filters button.active {
background: var(--color-primary);
color: #fff;
}
.adult-verification table {
width: 100%;
border-collapse: collapse;
}
.adult-verification th,
.adult-verification td {
padding: 12px 10px;
border-bottom: 1px solid rgba(93, 64, 55, 0.08);
text-align: left;
}
.adult-verification__badge {
display: inline-flex;
padding: 4px 10px;
border-radius: var(--radius-pill);
font-weight: 700;
font-size: 0.82rem;
}
.adult-verification__badge--pending {
background: rgba(216, 167, 65, 0.16);
}
.adult-verification__badge--approved {
background: rgba(92, 156, 106, 0.18);
}
.adult-verification__badge--rejected {
background: rgba(176, 88, 88, 0.16);
}
.adult-verification__actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.adult-verification__request {
display: grid;
gap: 4px;
}
.adult-verification__actions .secondary {
background: transparent;
}
@media (max-width: 760px) {
.adult-verification__hero,
.adult-verification__filters,
.adult-verification__list {
padding: 18px;
}
.adult-verification table,
.adult-verification thead,
.adult-verification tbody,
.adult-verification tr,
.adult-verification td {
display: block;
}
.adult-verification thead {
display: none;
}
.adult-verification td {
padding: 8px 0;
}
}
</style>