refactor: organize frontend by feature
Some checks failed
Backend CI/CD / build_and_deploy (push) Has been cancelled
Frontend CI/CD / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 01:05:50 -04:00
parent b6eb692c27
commit 121757546a
60 changed files with 107 additions and 183 deletions

View File

@@ -0,0 +1,146 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
const { t } = useI18n();
const authStore = useAuthStore();
const contentItemsStore = useContentItemsStore();
</script>
<template>
<section class="page-shell">
<div class="header">
<div>
<div class="eyebrow">{{ t('contentItems.eyebrow') }}</div>
<h1>{{ t('contentItems.title') }}</h1>
<p>{{ t('contentItems.description') }}</p>
</div>
<router-link
v-if="authStore.isManager || authStore.isProvider"
:to="{ name: 'content-item-create' }"
class="create-button"
>
{{ t('contentItems.newItem') }}
</router-link>
</div>
<div
v-if="contentItemsStore.isLoading"
class="page-message"
>
{{ t('contentItems.loading') }}
</div>
<div
v-else-if="contentItemsStore.error"
class="page-message error"
>
{{ contentItemsStore.error }}
</div>
<div
v-else-if="contentItemsStore.items.length"
class="item-grid"
>
<router-link
v-for="item in contentItemsStore.items"
:key="item.id"
:to="{ name: 'content-item-detail', params: { id: item.id } }"
class="item-card"
>
<div class="version-chip">{{ item.currentRevisionLabel }}</div>
<strong>{{ item.title }}</strong>
<span>{{ item.publicationTargets }}</span>
<div class="status-row">
<em>{{ item.status }}</em>
<small>{{ item.dueDate ? new Date(item.dueDate).toLocaleDateString() : t('contentItems.noDueDate') }}</small>
</div>
</router-link>
</div>
<div
v-else
class="page-message"
>
{{ t('contentItems.empty') }}
</div>
</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;
}
.header {
@apply flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between;
}
.eyebrow {
@apply text-xs font-bold uppercase tracking-[0.24em];
color: #0f766e;
}
.header h1 {
@apply mt-2 text-4xl font-black;
color: #172033;
}
.header p,
.item-card span,
.status-row em,
.status-row small {
@apply text-sm leading-6 not-italic;
color: #526178;
}
.create-button {
@apply inline-flex items-center justify-center rounded-full px-5 py-3 text-sm font-bold no-underline transition;
background: #172033;
color: #fffaf2;
}
.page-message,
.item-card {
@apply rounded-[1.5rem] border;
background: rgba(255, 255, 255, 0.9);
border-color: rgba(23, 32, 51, 0.08);
}
.page-message {
@apply p-5 text-sm;
color: #526178;
}
.page-message.error {
color: #b91c1c;
}
.item-grid {
@apply grid gap-4 md:grid-cols-2 xl:grid-cols-3;
}
.item-card {
@apply flex flex-col gap-4 p-5 no-underline transition;
}
.item-card:hover {
transform: translateY(-1px);
}
.item-card strong {
color: #172033;
}
.version-chip {
@apply w-fit rounded-full px-4 py-2 text-xs font-bold uppercase tracking-[0.16em];
background: rgba(23, 32, 51, 0.08);
color: #172033;
}
.status-row {
@apply flex items-center justify-between gap-3;
}
</style>