Make admin section editor form-based and localize Montech site to Persian.

Operators can edit landing copy as plain fields instead of JSON/HTML; default CMS seed and UI chrome are now Farsi/RTL.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-30 20:33:37 +03:30
co-authored by Cursor
parent 0f4363174c
commit 3410f3e5ed
22 changed files with 462 additions and 218 deletions
+16 -7
View File
@@ -1,15 +1,15 @@
<template>
<article v-if="post" class="min-h-screen px-6 pb-24 pt-28 md:px-10">
<SiteNavbar brand="Montech" />
<SiteNavbar brand="مانتک" />
<div class="mx-auto max-w-[760px]">
<NuxtLink to="/blog" class="font-mono text-[11px] tracking-[0.22em] text-ash hover:text-volt">← BACK</NuxtLink>
<h1 class="mt-8 font-display text-4xl font-extrabold uppercase tracking-[-0.03em] md:text-6xl">
<NuxtLink to="/blog" class="text-sm text-ash hover:text-volt">→ بازگشت به بلاگ</NuxtLink>
<h1 class="mt-8 font-display text-3xl font-extrabold leading-tight md:text-5xl">
{{ post.title }}
</h1>
<p class="mt-4 font-mono text-[11px] tracking-[0.2em] text-ash">
{{ post.publishedAt ? new Date(post.publishedAt).toLocaleDateString("en-GB") : "" }}
<p class="mt-4 text-sm text-ash">
{{ post.publishedAt ? new Date(post.publishedAt).toLocaleDateString("fa-IR") : "" }}
</p>
<div class="prose-montech mt-10 space-y-4 text-base leading-relaxed text-bone/90" v-html="post.content" />
<div class="mt-10 whitespace-pre-line text-base leading-relaxed text-bone/90">{{ plainContent }}</div>
</div>
</article>
</template>
@@ -20,9 +20,18 @@ const slug = String(route.params.slug);
const { data: post } = await useAsyncData(`blog-${slug}`, () => fetchPublicPost(slug));
if (!post.value) {
throw createError({ statusCode: 404, statusMessage: "Post not found" });
throw createError({ statusCode: 404, statusMessage: "پست پیدا نشد" });
}
const plainContent = computed(() => {
const raw = post.value?.content || "";
return raw
.replace(/<\/p>/gi, "\n\n")
.replace(/<br\s*\/?>/gi, "\n")
.replace(/<[^>]+>/g, "")
.trim();
});
useSeoMeta({
title: post.value.seoTitle || post.value.title,
description: post.value.seoDesc || post.value.excerpt,
+7 -7
View File
@@ -1,23 +1,23 @@
<template>
<div class="min-h-screen px-6 pb-24 pt-28 md:px-10">
<SiteNavbar :brand="'Montech'" />
<SiteNavbar brand="مانتک" />
<div class="mx-auto max-w-[1100px]">
<h1 class="font-display text-5xl font-extrabold uppercase tracking-[-0.03em] md:text-7xl">Blog</h1>
<p class="mt-4 max-w-xl text-ash">Notes on product, engineering and shipping software.</p>
<h1 class="font-display text-4xl font-extrabold md:text-6xl">بلاگ</h1>
<p class="mt-4 max-w-xl text-ash">یادداشت‌هایی درباره محصول، مهندسی و ساخت نرم‌افزار.</p>
<ul class="mt-16 divide-y divide-white/[0.07] border-y border-white/[0.07]">
<li v-for="post in posts" :key="post.id" class="py-8">
<NuxtLink :to="`/blog/${post.slug}`" class="group block">
<p class="font-mono text-[10px] tracking-[0.22em] text-ash">
{{ post.publishedAt ? new Date(post.publishedAt).toLocaleDateString("en-GB") : "" }}
<p class="text-sm text-ash">
{{ post.publishedAt ? new Date(post.publishedAt).toLocaleDateString("fa-IR") : "" }}
</p>
<h2 class="mt-2 font-display text-2xl font-bold uppercase tracking-[-0.02em] group-hover:text-volt md:text-3xl">
<h2 class="mt-2 font-display text-2xl font-bold group-hover:text-volt md:text-3xl">
{{ post.title }}
</h2>
<p class="mt-3 max-w-2xl text-sm text-ash">{{ post.excerpt }}</p>
</NuxtLink>
</li>
</ul>
<p v-if="!posts?.length" class="mt-10 text-ash">No posts yet.</p>
<p v-if="!posts?.length" class="mt-10 text-ash">هنوز پستی منتشر نشده است.</p>
</div>
</div>
</template>
+6 -6
View File
@@ -1,9 +1,9 @@
<template>
<div class="min-h-screen px-6 pb-24 pt-28 md:px-10">
<SiteNavbar brand="Montech" />
<SiteNavbar brand="مانتک" />
<div class="mx-auto max-w-[1500px]">
<h1 class="font-display text-5xl font-extrabold uppercase tracking-[-0.03em] md:text-7xl">Gallery</h1>
<p class="mt-4 max-w-xl text-ash">Selected visuals from projects and studio life.</p>
<h1 class="font-display text-4xl font-extrabold md:text-6xl">گالری</h1>
<p class="mt-4 max-w-xl text-ash">تصاویر منتخب از پروژه‌ها و فضای استودیو.</p>
<div class="mt-16 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<figure v-for="item in items" :key="item.id" class="overflow-hidden border border-white/[0.07] bg-coal">
<div
@@ -11,12 +11,12 @@
:style="item.imageUrl ? { backgroundImage: `url(${item.imageUrl})` } : {}"
/>
<figcaption class="p-4">
<p class="font-display text-lg font-bold uppercase">{{ item.title }}</p>
<p v-if="item.category" class="mt-1 font-mono text-[10px] tracking-[0.2em] text-ash">{{ item.category }}</p>
<p class="font-display text-lg font-bold">{{ item.title }}</p>
<p v-if="item.category" class="mt-1 text-xs text-ash">{{ item.category }}</p>
</figcaption>
</figure>
</div>
<p v-if="!items?.length" class="mt-10 text-ash">Gallery is empty.</p>
<p v-if="!items?.length" class="mt-10 text-ash">گالری خالی است.</p>
</div>
</div>
</template>
+1 -1
View File
@@ -8,7 +8,7 @@
<SiteMarquee
:items="site.sections.marquee.items"
:duration="22"
class-name="font-display text-xl font-bold uppercase tracking-[-0.01em] md:text-3xl"
class-name="font-display text-xl font-bold md:text-3xl"
item-class-name="mx-4 md:mx-6"
separator-class-name="text-ink/50"
/>