refactor(EroticVideosView): restructure layout and enhance video statistics display
- Updated the layout of the EroticVideosView component to improve organization and user experience. - Introduced a sidebar for video upload and management, separating it from the video list. - Added statistics for total, visible, and hidden videos to provide users with better insights. - Enhanced form elements and labels for clarity and usability during video uploads.
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="contenthidden">
|
||||||
|
<div class="contentscroll">
|
||||||
<div class="erotic-videos-page">
|
<div class="erotic-videos-page">
|
||||||
<section class="erotic-videos-hero surface-card">
|
<section class="erotic-videos-hero surface-card">
|
||||||
<div>
|
<div>
|
||||||
@@ -6,8 +8,24 @@
|
|||||||
<h2>{{ $t('socialnetwork.erotic.videosTitle') }}</h2>
|
<h2>{{ $t('socialnetwork.erotic.videosTitle') }}</h2>
|
||||||
<p>{{ $t('socialnetwork.erotic.videosIntro') }}</p>
|
<p>{{ $t('socialnetwork.erotic.videosIntro') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="erotic-videos-hero__stats">
|
||||||
|
<div class="erotic-videos-stat">
|
||||||
|
<strong>{{ videos.length }}</strong>
|
||||||
|
<span>{{ $t('socialnetwork.erotic.myVideos') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="erotic-videos-stat">
|
||||||
|
<strong>{{ visibleVideosCount }}</strong>
|
||||||
|
<span>Online</span>
|
||||||
|
</div>
|
||||||
|
<div class="erotic-videos-stat">
|
||||||
|
<strong>{{ hiddenVideosCount }}</strong>
|
||||||
|
<span>{{ $t('socialnetwork.erotic.moderationHidden') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<div class="erotic-videos-workspace">
|
||||||
|
<aside class="erotic-videos-sidebar">
|
||||||
<section class="erotic-videos-upload surface-card">
|
<section class="erotic-videos-upload surface-card">
|
||||||
<div class="erotic-videos-upload__header">
|
<div class="erotic-videos-upload__header">
|
||||||
<h3>{{ $t('socialnetwork.erotic.videoUploadTitle') }}</h3>
|
<h3>{{ $t('socialnetwork.erotic.videoUploadTitle') }}</h3>
|
||||||
@@ -21,28 +39,73 @@
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>{{ $t('socialnetwork.erotic.videoDescription') }}</span>
|
<span>{{ $t('socialnetwork.erotic.videoDescription') }}</span>
|
||||||
<textarea v-model="description" rows="3" />
|
<textarea v-model="description" rows="4" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>{{ $t('socialnetwork.erotic.videoFile') }}</span>
|
<span>{{ $t('socialnetwork.erotic.videoFile') }}</span>
|
||||||
<input type="file" accept="video/mp4,video/webm,video/ogg,video/quicktime" required @change="onFileChange" />
|
<input type="file" accept="video/mp4,video/webm,video/ogg,video/quicktime" required @change="onFileChange" />
|
||||||
</label>
|
</label>
|
||||||
|
<div class="erotic-videos-upload__meta">
|
||||||
|
<span>MP4, WEBM, OGG, MOV</span>
|
||||||
|
<span v-if="fileToUpload">{{ fileToUpload.name }}</span>
|
||||||
|
</div>
|
||||||
<button type="submit">{{ $t('socialnetwork.gallery.upload.upload_button') }}</button>
|
<button type="submit">{{ $t('socialnetwork.gallery.upload.upload_button') }}</button>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="erotic-videos-list surface-card">
|
<section class="erotic-videos-panel surface-card">
|
||||||
<h3>{{ $t('socialnetwork.erotic.myVideos') }}</h3>
|
<h3>Bibliothek</h3>
|
||||||
<div v-if="videos.length === 0" class="erotic-videos-empty">
|
<div class="erotic-videos-panel__list">
|
||||||
{{ $t('socialnetwork.erotic.noVideos') }}
|
<div class="erotic-videos-panel__item">
|
||||||
|
<span>Letzter Upload</span>
|
||||||
|
<strong>{{ latestVideoTitle }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul v-else class="erotic-videos-grid">
|
<div class="erotic-videos-panel__item">
|
||||||
|
<span>Sichtbare Videos</span>
|
||||||
|
<strong>{{ visibleVideosCount }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="erotic-videos-panel__item">
|
||||||
|
<span>Moderationsfälle</span>
|
||||||
|
<strong>{{ hiddenVideosCount }}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="erotic-videos-panel surface-card">
|
||||||
|
<h3>Hinweise</h3>
|
||||||
|
<ul class="erotic-videos-checklist">
|
||||||
|
<li>{{ $t('socialnetwork.erotic.videoUploadHint') }}</li>
|
||||||
|
<li>{{ $t('socialnetwork.erotic.reportAction') }} bei problematischen Inhalten direkt am Eintrag.</li>
|
||||||
|
<li>Die Bibliothek rechts ist eigenstaendig scrollbar, auch wenn viele Videos vorhanden sind.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section class="erotic-videos-library surface-card">
|
||||||
|
<div class="erotic-videos-library__header">
|
||||||
|
<div>
|
||||||
|
<h3>{{ $t('socialnetwork.erotic.myVideos') }}</h3>
|
||||||
|
<p>Eigene Uploads, Moderationsstatus und Meldungen an einem Ort.</p>
|
||||||
|
</div>
|
||||||
|
<span class="erotic-videos-library__count">{{ videos.length }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="videos.length === 0" class="erotic-videos-empty">
|
||||||
|
<strong>{{ $t('socialnetwork.erotic.noVideos') }}</strong>
|
||||||
|
<span>Nutze links den Upload-Bereich, um deine erste Videokarte anzulegen.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="erotic-videos-library__scroll">
|
||||||
|
<ul class="erotic-videos-grid">
|
||||||
<li v-for="video in videos" :key="video.id" class="erotic-videos-card">
|
<li v-for="video in videos" :key="video.id" class="erotic-videos-card">
|
||||||
<video v-if="!video.isModeratedHidden" :src="video.url" controls preload="metadata" />
|
<video v-if="!video.isModeratedHidden" :src="video.url" controls preload="metadata" />
|
||||||
<div v-else class="erotic-videos-card__hidden">
|
<div v-else class="erotic-videos-card__hidden">
|
||||||
{{ $t('socialnetwork.erotic.hiddenByModeration') }}
|
{{ $t('socialnetwork.erotic.hiddenByModeration') }}
|
||||||
</div>
|
</div>
|
||||||
<strong>{{ video.title }}</strong>
|
<div class="erotic-videos-card__meta">
|
||||||
|
<strong>{{ video.title || 'Ohne Titel' }}</strong>
|
||||||
|
<span v-if="video.createdAtLabel">{{ video.createdAtLabel }}</span>
|
||||||
|
</div>
|
||||||
<span v-if="video.isModeratedHidden" class="erotic-videos-card__badge">
|
<span v-if="video.isModeratedHidden" class="erotic-videos-card__badge">
|
||||||
{{ $t('socialnetwork.erotic.moderationHidden') }}
|
{{ $t('socialnetwork.erotic.moderationHidden') }}
|
||||||
</span>
|
</span>
|
||||||
@@ -66,8 +129,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -93,19 +160,40 @@ export default {
|
|||||||
value,
|
value,
|
||||||
label: this.$t(`socialnetwork.erotic.reportReasons.${value}`)
|
label: this.$t(`socialnetwork.erotic.reportReasons.${value}`)
|
||||||
}));
|
}));
|
||||||
|
},
|
||||||
|
visibleVideosCount() {
|
||||||
|
return this.videos.filter((video) => !video.isModeratedHidden).length;
|
||||||
|
},
|
||||||
|
hiddenVideosCount() {
|
||||||
|
return this.videos.filter((video) => video.isModeratedHidden).length;
|
||||||
|
},
|
||||||
|
latestVideoTitle() {
|
||||||
|
return this.videos[0]?.title || 'Noch kein Upload';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
await this.loadVideos();
|
await this.loadVideos();
|
||||||
},
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.releaseVideoUrls();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadVideos() {
|
async loadVideos() {
|
||||||
|
this.releaseVideoUrls();
|
||||||
const response = await apiClient.get('/api/socialnetwork/erotic/videos');
|
const response = await apiClient.get('/api/socialnetwork/erotic/videos');
|
||||||
this.videos = await Promise.all(response.data.map(async (video) => ({
|
this.videos = await Promise.all(response.data.map(async (video) => ({
|
||||||
...video,
|
...video,
|
||||||
url: video.isModeratedHidden ? null : await this.fetchVideoUrl(video.hash),
|
url: video.isModeratedHidden ? null : await this.fetchVideoUrl(video.hash),
|
||||||
|
createdAtLabel: video.createdAt ? new Date(video.createdAt).toLocaleDateString() : '',
|
||||||
})));
|
})));
|
||||||
},
|
},
|
||||||
|
releaseVideoUrls() {
|
||||||
|
this.videos.forEach((video) => {
|
||||||
|
if (video?.url) {
|
||||||
|
URL.revokeObjectURL(video.url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
async fetchVideoUrl(hash) {
|
async fetchVideoUrl(hash) {
|
||||||
const response = await apiClient.get(`/api/socialnetwork/erotic/video/${hash}`, {
|
const response = await apiClient.get(`/api/socialnetwork/erotic/video/${hash}`, {
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
@@ -161,17 +249,23 @@ export default {
|
|||||||
.erotic-videos-page {
|
.erotic-videos-page {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
|
padding-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.erotic-videos-hero,
|
.erotic-videos-hero,
|
||||||
.erotic-videos-upload,
|
.erotic-videos-upload,
|
||||||
.erotic-videos-list {
|
.erotic-videos-library,
|
||||||
|
.erotic-videos-panel {
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
box-shadow: var(--shadow-soft);
|
box-shadow: var(--shadow-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.erotic-videos-hero {
|
.erotic-videos-hero {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: end;
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at top right, rgba(204, 44, 94, 0.18), transparent 38%),
|
radial-gradient(circle at top right, rgba(204, 44, 94, 0.18), transparent 38%),
|
||||||
@@ -195,9 +289,49 @@ export default {
|
|||||||
color: rgba(255, 241, 245, 0.84);
|
color: rgba(255, 241, 245, 0.84);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.erotic-videos-hero__stats {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(90px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-stat {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.15rem;
|
||||||
|
padding: 0.8rem 0.9rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: rgba(255, 241, 245, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-stat strong {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-stat span {
|
||||||
|
color: rgba(255, 241, 245, 0.84);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-workspace {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
|
||||||
|
gap: 1.25rem;
|
||||||
|
min-height: min(72vh, 860px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-sidebar {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
align-content: start;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.erotic-videos-upload,
|
.erotic-videos-upload,
|
||||||
.erotic-videos-list {
|
.erotic-videos-library,
|
||||||
|
.erotic-videos-panel {
|
||||||
padding: 1.25rem;
|
padding: 1.25rem;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.erotic-videos-upload__header,
|
.erotic-videos-upload__header,
|
||||||
@@ -211,6 +345,78 @@ export default {
|
|||||||
gap: 0.35rem;
|
gap: 0.35rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.erotic-videos-upload__meta {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.25rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-panel {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-panel__list,
|
||||||
|
.erotic-videos-checklist {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-panel__item {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.15rem;
|
||||||
|
padding: 0.75rem 0.85rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: rgba(112, 60, 80, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-panel__item span,
|
||||||
|
.erotic-videos-checklist {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-checklist {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
|
gap: 1rem;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library__header p {
|
||||||
|
margin: 0.3rem 0 0;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library__count {
|
||||||
|
display: inline-flex;
|
||||||
|
min-width: 2.2rem;
|
||||||
|
min-height: 2.2rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(112, 60, 80, 0.08);
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library__scroll {
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
padding-right: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.erotic-videos-grid {
|
.erotic-videos-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||||
@@ -228,6 +434,16 @@ export default {
|
|||||||
background: rgba(112, 60, 80, 0.08);
|
background: rgba(112, 60, 80, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.erotic-videos-card__meta {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-card__meta span {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
.erotic-videos-card__hidden {
|
.erotic-videos-card__hidden {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
@@ -264,6 +480,33 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.erotic-videos-empty {
|
.erotic-videos-empty {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.35rem;
|
||||||
|
place-items: center;
|
||||||
|
min-height: 240px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
|
background: rgba(112, 60, 80, 0.05);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 980px) {
|
||||||
|
.erotic-videos-hero,
|
||||||
|
.erotic-videos-workspace {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-workspace {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library {
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.erotic-videos-library__scroll {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user