Replace the landing with the artifact design and rebuild the admin in Ant Design.
Adds consultation leads, section editors, and the one-time content reset for this release. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,44 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>محتوای لندینگ</h1>
|
||||
<p class="muted">متنها را مثل فرم معمولی ویرایش کنید — نیازی به کدنویسی نیست.</p>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>بخش</th><th>آخرین ویرایش</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="s in sections" :key="s.id">
|
||||
<td>{{ label(s.key) }}</td>
|
||||
<td class="muted">{{ new Date(s.updatedAt).toLocaleString("fa-IR") }}</td>
|
||||
<td><RouterLink :to="`/sections/${s.key}`">ویرایش</RouterLink></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1>محتوای لندینگ</h1>
|
||||
<p>هر بخش صفحه اصلی را مثل یک فرم ساده ویرایش کنید.</p>
|
||||
</div>
|
||||
<a-button href="/" target="_blank">
|
||||
<template #icon><GlobalOutlined /></template>
|
||||
مشاهده لندینگ
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<a-row :gutter="[16, 16]">
|
||||
<a-col v-for="s in rows" :key="s.key" :xs="24" :md="12" :xl="8">
|
||||
<a-card hoverable class="section-card" @click="router.push(`/sections/${s.key}`)">
|
||||
<a-card-meta :title="SECTION_TITLES_FA[s.key]" :description="SECTION_SCHEMAS[s.key].description" />
|
||||
<div class="meta">
|
||||
<a-tag v-if="s.isDefault" color="default">محتوای پیشفرض</a-tag>
|
||||
<a-tag v-else color="blue">سفارشیشده</a-tag>
|
||||
<a-typography-text v-if="s.updatedAt && !s.isDefault" type="secondary">
|
||||
{{ new Date(s.updatedAt).toLocaleString("fa-IR") }}
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-skeleton v-if="loading" active />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { api } from "../lib/api";
|
||||
import { useRouter } from "vue-router";
|
||||
import { message } from "ant-design-vue";
|
||||
import { GlobalOutlined } from "@ant-design/icons-vue";
|
||||
import { SECTION_KEYS, SECTION_TITLES_FA, type SectionKey } from "@montech/shared";
|
||||
import { SECTION_SCHEMAS } from "../sections/schema";
|
||||
import { api, apiErrorMessage } from "../lib/api";
|
||||
|
||||
const TITLES: Record<string, string> = {
|
||||
hero: "صفحه اول (هیرو)",
|
||||
marquee: "نوار متحرک",
|
||||
manifesto: "معرفی",
|
||||
services: "خدمات",
|
||||
work: "نمونه کارها",
|
||||
process: "فرآیند کار",
|
||||
stats: "آمار",
|
||||
team: "تیم",
|
||||
footer: "تماس و فوتر",
|
||||
};
|
||||
|
||||
const sections = ref<any[]>([]);
|
||||
function label(key: string) {
|
||||
return TITLES[key] || key;
|
||||
interface SectionRow {
|
||||
key: SectionKey;
|
||||
updatedAt: string | null;
|
||||
isDefault: boolean;
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
const rows = ref<SectionRow[]>([]);
|
||||
const loading = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
const { data } = await api.get("/admin/sections");
|
||||
sections.value = data;
|
||||
try {
|
||||
const { data } = await api.get<SectionRow[]>("/admin/sections");
|
||||
const byKey = new Map(data.map((s) => [s.key, s]));
|
||||
rows.value = SECTION_KEYS.map((key) => byKey.get(key) ?? { key, updatedAt: null, isDefault: true });
|
||||
} catch (e) {
|
||||
message.error(apiErrorMessage(e));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.section-card {
|
||||
height: 100%;
|
||||
}
|
||||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user