refactor: organize frontend by feature
This commit is contained in:
222
frontend/src/features/content/views/MediaLibraryView.vue
Normal file
222
frontend/src/features/content/views/MediaLibraryView.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {
|
||||
mdiCheckCircleOutline,
|
||||
mdiCloudSyncOutline,
|
||||
mdiFolderGoogleDrive,
|
||||
mdiImageMultipleOutline,
|
||||
mdiVideoOutline,
|
||||
} from '@mdi/js';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const mediaTypes = [
|
||||
{ label: t('mediaLibrary.mediaTypes.images'), icon: mdiImageMultipleOutline },
|
||||
{ label: t('mediaLibrary.mediaTypes.videos'), icon: mdiVideoOutline },
|
||||
];
|
||||
|
||||
const workflowSteps = [
|
||||
t('mediaLibrary.workflow.connectDrive'),
|
||||
t('mediaLibrary.workflow.syncAssets'),
|
||||
t('mediaLibrary.workflow.organizeLibrary'),
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="page-shell">
|
||||
<div class="hero">
|
||||
<div class="hero-copy">
|
||||
<div class="eyebrow">{{ t('mediaLibrary.eyebrow') }}</div>
|
||||
<h1>{{ t('mediaLibrary.title') }}</h1>
|
||||
<p>{{ t('mediaLibrary.description') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="hero-card">
|
||||
<div class="hero-card-icon">
|
||||
<v-icon :icon="mdiFolderGoogleDrive" />
|
||||
</div>
|
||||
<strong>{{ t('mediaLibrary.syncCard.title') }}</strong>
|
||||
<span>{{ t('mediaLibrary.syncCard.description') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-grid">
|
||||
<article class="panel">
|
||||
<div class="panel-header">
|
||||
<strong>{{ t('mediaLibrary.mediaTypesTitle') }}</strong>
|
||||
<span>{{ t('mediaLibrary.mediaTypesDescription') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="media-type-list">
|
||||
<div
|
||||
v-for="type in mediaTypes"
|
||||
:key="type.label"
|
||||
class="media-type-item"
|
||||
>
|
||||
<v-icon :icon="type.icon" />
|
||||
<span>{{ type.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="panel-header">
|
||||
<strong>{{ t('mediaLibrary.workflowTitle') }}</strong>
|
||||
<span>{{ t('mediaLibrary.workflowDescription') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="workflow-list">
|
||||
<div
|
||||
v-for="step in workflowSteps"
|
||||
:key="step"
|
||||
class="workflow-item"
|
||||
>
|
||||
<v-icon
|
||||
:icon="mdiCheckCircleOutline"
|
||||
class="workflow-icon"
|
||||
/>
|
||||
<span>{{ step }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<article class="status-panel">
|
||||
<div class="status-copy">
|
||||
<div class="status-label">
|
||||
<v-icon :icon="mdiCloudSyncOutline" />
|
||||
<span>{{ t('mediaLibrary.statusLabel') }}</span>
|
||||
</div>
|
||||
<strong>{{ t('mediaLibrary.pendingTitle') }}</strong>
|
||||
<p>{{ t('mediaLibrary.pendingDescription') }}</p>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-shell {
|
||||
@apply mx-auto flex w-full max-w-7xl flex-col gap-6 px-5 py-8 md:px-8;
|
||||
}
|
||||
|
||||
.hero {
|
||||
@apply grid gap-4 lg:grid-cols-[minmax(0,1.45fr)_minmax(18rem,0.8fr)];
|
||||
}
|
||||
|
||||
.hero-copy,
|
||||
.hero-card,
|
||||
.panel,
|
||||
.status-panel {
|
||||
@apply rounded-[1.75rem] border;
|
||||
border-color: rgba(23, 32, 51, 0.08);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.hero-copy {
|
||||
@apply p-6 md:p-8;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(14, 165, 164, 0.18), transparent 45%),
|
||||
linear-gradient(135deg, rgba(255, 255, 255, 0.98), rgba(240, 249, 255, 0.92));
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
@apply text-xs font-bold uppercase tracking-[0.24em];
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.hero-copy h1 {
|
||||
@apply mt-3 text-4xl font-black;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.hero-copy p,
|
||||
.hero-card span,
|
||||
.panel-header span,
|
||||
.media-type-item span,
|
||||
.workflow-item span,
|
||||
.status-copy p,
|
||||
.status-label span {
|
||||
@apply text-sm leading-6;
|
||||
color: #526178;
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
@apply flex flex-col justify-between gap-5 p-6;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 250, 242, 0.96), rgba(255, 255, 255, 0.96));
|
||||
}
|
||||
|
||||
.hero-card-icon,
|
||||
.media-type-item,
|
||||
.workflow-item,
|
||||
.status-label {
|
||||
@apply inline-flex items-center gap-3;
|
||||
}
|
||||
|
||||
.hero-card-icon,
|
||||
.media-type-item {
|
||||
@apply w-fit rounded-full px-3 py-2;
|
||||
background: rgba(15, 118, 110, 0.08);
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.hero-card strong,
|
||||
.panel-header strong,
|
||||
.status-copy strong {
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.hero-card strong {
|
||||
@apply text-2xl font-black;
|
||||
}
|
||||
|
||||
.content-grid {
|
||||
@apply grid gap-6 lg:grid-cols-2;
|
||||
}
|
||||
|
||||
.panel,
|
||||
.status-panel {
|
||||
@apply flex flex-col gap-5 p-6;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
@apply flex flex-col gap-2;
|
||||
}
|
||||
|
||||
.panel-header strong {
|
||||
@apply text-xl font-black;
|
||||
}
|
||||
|
||||
.media-type-list,
|
||||
.workflow-list {
|
||||
@apply flex flex-col gap-3;
|
||||
}
|
||||
|
||||
.media-type-item,
|
||||
.workflow-item {
|
||||
@apply rounded-[1.1rem] border px-4 py-3;
|
||||
border-color: rgba(23, 32, 51, 0.08);
|
||||
background: rgba(248, 250, 252, 0.9);
|
||||
}
|
||||
|
||||
.workflow-icon {
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.status-panel {
|
||||
background: linear-gradient(135deg, rgba(255, 247, 237, 0.95), rgba(255, 255, 255, 0.98));
|
||||
}
|
||||
|
||||
.status-copy {
|
||||
@apply flex flex-col gap-3;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
@apply text-xs font-bold uppercase tracking-[0.2em];
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.status-copy strong {
|
||||
@apply text-2xl font-black;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user