- Updated navigationController to simplify the eroticChat menu structure. - Enhanced adminService to notify users of adult verification status changes, including previous status. - Improved AppNavigation and related components to register and unregister socket listeners for adult verification updates. - Added localized messages for adult verification notifications in English, German, and Spanish. - Introduced a verification hint in the EroticAccessView to guide users on document submission.
253 lines
7.3 KiB
Vue
253 lines
7.3 KiB
Vue
<template>
|
|
<div class="contenthidden">
|
|
<div class="contentscroll">
|
|
<div class="erotic-access-page">
|
|
<section class="erotic-access-hero surface-card">
|
|
<span class="erotic-access-eyebrow">{{ $t('socialnetwork.erotic.eyebrow') }}</span>
|
|
<h2>{{ $t('socialnetwork.erotic.accessTitle') }}</h2>
|
|
<p>{{ $t('socialnetwork.erotic.accessIntro') }}</p>
|
|
</section>
|
|
|
|
<section class="erotic-access-panel surface-card">
|
|
<div class="erotic-access-status">
|
|
<strong>{{ statusTitle }}</strong>
|
|
<span>{{ statusText }}</span>
|
|
</div>
|
|
|
|
<div v-if="account?.adultVerificationRequest" class="erotic-access-request">
|
|
<strong>{{ $t('socialnetwork.erotic.requestInfoTitle') }}</strong>
|
|
<span>{{ account.adultVerificationRequest.originalName }}</span>
|
|
<span v-if="account.adultVerificationRequest.submittedAt">{{ formattedSubmittedAt }}</span>
|
|
<span v-if="account.adultVerificationRequest.note">{{ account.adultVerificationRequest.note }}</span>
|
|
</div>
|
|
|
|
<div class="erotic-access-actions">
|
|
<router-link to="/settings/account" class="erotic-access-link">
|
|
{{ $t('socialnetwork.erotic.settingsLink') }}
|
|
</router-link>
|
|
</div>
|
|
|
|
<div class="erotic-access-hint">
|
|
<strong>{{ $t('socialnetwork.erotic.verificationHintTitle') }}</strong>
|
|
<span>{{ $t('socialnetwork.erotic.verificationHintBody') }}</span>
|
|
</div>
|
|
|
|
<form v-if="canRequestVerification" class="erotic-access-form" @submit.prevent="requestVerification">
|
|
<label>
|
|
<span>{{ $t('socialnetwork.erotic.documentLabel') }}</span>
|
|
<input type="file" accept=".jpg,.jpeg,.png,.webp,.pdf" @change="handleFileChange" />
|
|
</label>
|
|
<label>
|
|
<span>{{ $t('socialnetwork.erotic.noteLabel') }}</span>
|
|
<textarea v-model="note" rows="4"></textarea>
|
|
</label>
|
|
<button type="submit">{{ $t('socialnetwork.erotic.requestVerification') }}</button>
|
|
</form>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import apiClient from '@/utils/axios.js';
|
|
import { mapGetters } from 'vuex';
|
|
import { showApiError, showInfo, showSuccess } from '@/utils/feedback.js';
|
|
|
|
export default {
|
|
name: 'EroticAccessView',
|
|
data() {
|
|
return {
|
|
account: null,
|
|
note: '',
|
|
documentFile: null
|
|
};
|
|
},
|
|
computed: {
|
|
...mapGetters(['user', 'socket']),
|
|
status() {
|
|
return this.account?.adultVerificationStatus || 'none';
|
|
},
|
|
canRequestVerification() {
|
|
return this.account?.isAdult && ['none', 'rejected'].includes(this.status);
|
|
},
|
|
statusTitle() {
|
|
if (!this.account?.isAdult) {
|
|
return this.$t('settings.account.adultStatus.ineligible.title');
|
|
}
|
|
return this.$t(`socialnetwork.erotic.status.${this.status}.title`);
|
|
},
|
|
statusText() {
|
|
if (!this.account?.isAdult) {
|
|
return this.$t('settings.account.adultStatus.ineligible.body');
|
|
}
|
|
return this.$t(`socialnetwork.erotic.status.${this.status}.body`);
|
|
},
|
|
formattedSubmittedAt() {
|
|
const value = this.account?.adultVerificationRequest?.submittedAt;
|
|
if (!value) return '';
|
|
return new Date(value).toLocaleString();
|
|
}
|
|
},
|
|
methods: {
|
|
async loadAccount() {
|
|
const response = await apiClient.post('/api/settings/account', { userId: this.user.id });
|
|
this.account = response.data;
|
|
},
|
|
registerSocketListeners(sock) {
|
|
if (!sock) return;
|
|
this._adultVerificationChangedHandler = async (payload = {}) => {
|
|
await this.loadAccount();
|
|
if (payload.status === 'approved') {
|
|
showInfo(this, this.$t('socialnetwork.erotic.notifications.approved'));
|
|
this.$router.replace('/socialnetwork/erotic/pictures');
|
|
} else if (payload.status === 'rejected') {
|
|
showInfo(this, this.$t('socialnetwork.erotic.notifications.rejected'));
|
|
}
|
|
};
|
|
sock.on('adultVerificationChanged', this._adultVerificationChangedHandler);
|
|
},
|
|
unregisterSocketListeners() {
|
|
if (this.socket && this._adultVerificationChangedHandler) {
|
|
this.socket.off('adultVerificationChanged', this._adultVerificationChangedHandler);
|
|
}
|
|
this._adultVerificationChangedHandler = null;
|
|
},
|
|
handleFileChange(event) {
|
|
this.documentFile = event.target.files?.[0] || null;
|
|
},
|
|
async requestVerification() {
|
|
try {
|
|
const formData = new FormData();
|
|
if (this.documentFile) {
|
|
formData.append('document', this.documentFile);
|
|
}
|
|
formData.append('note', this.note || '');
|
|
await apiClient.post('/api/settings/adult-verification/request', formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data'
|
|
}
|
|
});
|
|
await this.loadAccount();
|
|
this.note = '';
|
|
this.documentFile = null;
|
|
showSuccess(this, this.$t('socialnetwork.erotic.requestSent'));
|
|
} catch (error) {
|
|
showApiError(this, error, this.$t('socialnetwork.erotic.requestError'));
|
|
}
|
|
}
|
|
},
|
|
async mounted() {
|
|
if (this.socket) {
|
|
this.registerSocketListeners(this.socket);
|
|
}
|
|
await this.loadAccount();
|
|
if (this.account?.adultAccessEnabled) {
|
|
this.$router.replace('/socialnetwork/erotic/pictures');
|
|
}
|
|
},
|
|
watch: {
|
|
socket(newSocket) {
|
|
this.unregisterSocketListeners();
|
|
if (newSocket) {
|
|
this.registerSocketListeners(newSocket);
|
|
}
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
this.unregisterSocketListeners();
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.erotic-access-page {
|
|
display: grid;
|
|
gap: 18px;
|
|
max-width: 920px;
|
|
padding-bottom: 24px;
|
|
}
|
|
|
|
.erotic-access-hero,
|
|
.erotic-access-panel {
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
background: linear-gradient(180deg, rgba(255, 249, 244, 0.98), rgba(245, 237, 229, 0.96));
|
|
box-shadow: var(--shadow-soft);
|
|
}
|
|
|
|
.erotic-access-hero {
|
|
padding: 26px 28px;
|
|
}
|
|
|
|
.erotic-access-eyebrow {
|
|
display: inline-flex;
|
|
margin-bottom: 8px;
|
|
padding: 4px 10px;
|
|
border-radius: var(--radius-pill);
|
|
background: rgba(164, 98, 72, 0.14);
|
|
color: var(--color-text-secondary);
|
|
font-size: 0.78rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.erotic-access-hero p {
|
|
margin: 0;
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.erotic-access-panel {
|
|
display: grid;
|
|
gap: 18px;
|
|
padding: 24px;
|
|
}
|
|
|
|
.erotic-access-status {
|
|
display: grid;
|
|
gap: 6px;
|
|
padding: 14px 16px;
|
|
border-radius: var(--radius-md);
|
|
background: rgba(196, 162, 108, 0.14);
|
|
}
|
|
|
|
.erotic-access-request,
|
|
.erotic-access-form,
|
|
.erotic-access-hint {
|
|
display: grid;
|
|
gap: 10px;
|
|
}
|
|
|
|
.erotic-access-request {
|
|
padding: 14px 16px;
|
|
border-radius: var(--radius-md);
|
|
background: rgba(255, 255, 255, 0.6);
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.erotic-access-hint {
|
|
padding: 14px 16px;
|
|
border-radius: var(--radius-md);
|
|
background: rgba(164, 98, 72, 0.08);
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.erotic-access-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
}
|
|
|
|
.erotic-access-link {
|
|
color: var(--color-primary);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.erotic-access-form label {
|
|
display: grid;
|
|
gap: 6px;
|
|
}
|
|
</style>
|