first commit
ci / build (push) Failing after 23s
deploy / deploy (push) Failing after 10m12s

This commit is contained in:
soroush.asadi
2026-05-31 12:47:02 +03:30
commit add78d8460
100 changed files with 15221 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
import { notFound } from 'next/navigation';
import Link from 'next/link';
import { AdminShell } from '@/components/admin/AdminShell';
import { PostEditor } from '@/components/admin/PostEditor';
import type { JsonValue } from '@/components/admin/JsonForm';
import { loadContent } from '@/lib/content/load';
import { loadPost, loadPostOverrides, isKnownSlug } from '@/lib/content/posts-store';
// Always render on demand so the editor mirrors current DB state.
export const dynamic = 'force-dynamic';
export default function AdminPostEditorPage({ params }: { params: { slug: string } }) {
const { slug } = params;
if (!isKnownSlug(slug)) notFound();
const post = loadPost(slug);
if (!post) notFound();
const overridden = slug in loadPostOverrides();
const { en } = loadContent();
const title = en.blog.items.find((p) => p.slug === slug)?.title ?? slug;
return (
<AdminShell>
<div className="mx-auto max-w-3xl">
<Link
href="/admin/posts"
className="font-mono text-[0.7rem] uppercase tracking-wider text-slate-500 transition-colors hover:text-electric"
>
Journal articles
</Link>
<h1 className="mb-1 mt-3 text-2xl font-bold text-white">{title}</h1>
<p className="mb-6 text-sm text-slate-400">
Edit the lead and body blocks for both languages, then save. Changes go live immediately.
</p>
<PostEditor
slug={slug}
title={title}
initial={{
date: post.date as JsonValue,
accent: post.accent as JsonValue,
fa: post.fa as unknown as JsonValue,
en: post.en as unknown as JsonValue,
}}
isOverridden={overridden}
/>
</div>
</AdminShell>
);
}
+74
View File
@@ -0,0 +1,74 @@
import Link from 'next/link';
import { AdminShell } from '@/components/admin/AdminShell';
import { loadContent } from '@/lib/content/load';
import { loadAllPosts, loadPostOverrides } from '@/lib/content/posts-store';
// Always reflect live DB state in the editor list.
export const dynamic = 'force-dynamic';
export default function AdminPostsPage() {
const posts = loadAllPosts();
const overrides = loadPostOverrides();
const { en } = loadContent();
const cardBySlug = new Map<string, (typeof en.blog.items)[number]>(
en.blog.items.map((p) => [p.slug, p]),
);
const slugs = Object.keys(posts);
const editedCount = Object.keys(overrides).length;
return (
<AdminShell>
<div className="mx-auto max-w-4xl">
<div className="mb-8">
<h1 className="text-2xl font-bold text-white">Journal articles</h1>
<p className="mt-1 text-sm text-slate-400">
Edit the full bilingual body of each post.{' '}
{editedCount > 0
? `${editedCount} article${editedCount > 1 ? 's' : ''} customized.`
: 'All articles are at their defaults.'}{' '}
Titles, excerpts and read time live under the{' '}
<Link href="/admin/sections/blog" className="text-electric hover:underline">
Journal
</Link>{' '}
section.
</p>
</div>
<div className="grid gap-3 sm:grid-cols-2">
{slugs.map((slug) => {
const card = cardBySlug.get(slug);
const post = posts[slug];
const edited = slug in overrides;
return (
<Link
key={slug}
href={`/admin/posts/${slug}`}
className="group rounded-xl border border-white/8 bg-white/[0.02] p-5 transition-colors hover:border-electric/30 hover:bg-electric/[0.03]"
>
<div className="flex items-start justify-between gap-3">
<h2 className="font-semibold leading-snug text-white group-hover:text-electric">
{card?.title ?? slug}
</h2>
{edited ? (
<span className="shrink-0 rounded-full border border-emerald/30 bg-emerald/5 px-2 py-0.5 font-mono text-[0.6rem] uppercase tracking-wider text-emerald">
edited
</span>
) : (
<span className="shrink-0 rounded-full border border-white/10 px-2 py-0.5 font-mono text-[0.6rem] uppercase tracking-wider text-slate-500">
default
</span>
)}
</div>
<div className="mt-2 flex items-center gap-3 font-mono text-[0.65rem] uppercase tracking-wider text-slate-500">
<span>{card?.category ?? '—'}</span>
<span>·</span>
<span>{post.date}</span>
</div>
</Link>
);
})}
</div>
</div>
</AdminShell>
);
}