feat: admin API integration, LogoMark, settings page, i18n, RTL font, docs
- Wire admin API into homepage + templates page (ISR 60s, null fallback) - Add src/lib/admin-api.ts with safeFetch helper - Add adminProjectToTemplateItem + adminProjectToCatalogTemplate mappers - Add LogoMark SVG component, replace Sparkles icon in Navbar/Footer/Sidebar - Add public/favicon.svg (SVG brand mark) - Rewrite opengraph-image.tsx with FlatRender branding - Add RTL/Persian font cascade: unlayered [dir=rtl] block forces Vazirmatn - Dashboard Settings page: Profile, Security, Billing, Notifications sections - Add src/lib/supabase/client.ts browser client - Admin API: GET /me, PATCH /profile, POST /change-password endpoints - Admin API DTOs: AdminUserDto, UpdateProfileRequest, ChangePasswordRequest - Admin UI Settings page with TanStack Query + mutations - Add CLAUDE.md + README.md to both repos for new-machine onboarding - Update PROJECT_MEMORY.md with session log - Add appsettings.Development.json.example template
This commit is contained in:
@@ -1,25 +1,68 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import { SettingsBilling } from "@/components/dashboard/settings/SettingsBilling";
|
||||
import { SettingsNotifications } from "@/components/dashboard/settings/SettingsNotifications";
|
||||
import { SettingsProfile } from "@/components/dashboard/settings/SettingsProfile";
|
||||
import { SettingsSecurity } from "@/components/dashboard/settings/SettingsSecurity";
|
||||
import { createPageMetadata } from "@/lib/metadata";
|
||||
import { getUserProfile } from "@/lib/profiles";
|
||||
import { createClient } from "@/lib/supabase/server";
|
||||
|
||||
export const metadata: Metadata = createPageMetadata({
|
||||
title: "Settings",
|
||||
description: "Manage your CreatorStudio account and workspace preferences.",
|
||||
description: "Manage your FlatRender account and workspace preferences.",
|
||||
path: "/dashboard/settings",
|
||||
});
|
||||
|
||||
export default function DashboardSettingsPage() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function DashboardSettingsPage() {
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
const email = user?.email ?? "";
|
||||
const displayName =
|
||||
typeof user?.user_metadata?.full_name === "string"
|
||||
? user.user_metadata.full_name
|
||||
: null;
|
||||
|
||||
const profile = user ? await getUserProfile(user.id) : null;
|
||||
const plan = profile?.plan ?? "free";
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col">
|
||||
{/* Page header */}
|
||||
<header className="border-b border-gray-100 bg-white px-6 py-4">
|
||||
<h1 className="font-heading text-xl font-bold text-neutral-900">
|
||||
Settings
|
||||
</h1>
|
||||
</header>
|
||||
<div className="flex-1 p-6">
|
||||
<p className="text-sm text-neutral-600">
|
||||
Account and workspace settings will be available here soon.
|
||||
<h1 className="font-heading text-xl font-bold text-neutral-900">Settings</h1>
|
||||
<p className="mt-0.5 text-sm text-neutral-500">
|
||||
Manage your account, security, and notification preferences.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 p-6">
|
||||
<div className="mx-auto max-w-2xl space-y-6">
|
||||
<SettingsProfile email={email} displayName={displayName} />
|
||||
<SettingsSecurity />
|
||||
<SettingsBilling plan={plan} />
|
||||
<SettingsNotifications />
|
||||
|
||||
{/* Danger zone */}
|
||||
<div className="rounded-xl border border-red-100 bg-white p-6">
|
||||
<h2 className="font-heading text-base font-semibold text-red-600">Danger zone</h2>
|
||||
<p className="mt-1 text-sm text-neutral-500">
|
||||
Permanently delete your account and all your projects. This cannot be undone.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-4 rounded-lg border border-red-200 px-4 py-2 text-sm font-semibold text-red-600 transition-colors hover:bg-red-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2"
|
||||
>
|
||||
Delete account
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -96,6 +96,7 @@ export default async function LocaleLayout({
|
||||
className={fontVars}
|
||||
>
|
||||
<head>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link
|
||||
rel="preconnect"
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TemplateGallery } from "@/components/sections/TemplateGallery";
|
||||
import { FAQ } from "@/components/sections/FAQ";
|
||||
import { Testimonials } from "@/components/sections/Testimonials";
|
||||
import { createPageMetadata } from "@/lib/metadata";
|
||||
import { fetchProjects } from "@/lib/admin-api";
|
||||
|
||||
export const metadata: Metadata = createPageMetadata({
|
||||
title: "Create Pro Videos & Images with AI",
|
||||
@@ -16,12 +17,17 @@ export const metadata: Metadata = createPageMetadata({
|
||||
path: "/",
|
||||
});
|
||||
|
||||
export default function Home() {
|
||||
export default async function Home() {
|
||||
// Fetch up to 8 published projects from the admin service.
|
||||
// Returns an empty array when ADMIN_API_URL is not set or the service
|
||||
// is unreachable — TemplateGallery falls back to hardcoded data.
|
||||
const { items: adminProjects } = await fetchProjects({ pageSize: 8 });
|
||||
|
||||
return (
|
||||
<main>
|
||||
<Hero />
|
||||
<ProductsShowcase />
|
||||
<TemplateGallery />
|
||||
<TemplateGallery adminItems={adminProjects} />
|
||||
<HowItWorks />
|
||||
<Pricing />
|
||||
<Testimonials />
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { Metadata } from "next";
|
||||
|
||||
import { TemplatesPageContent } from "@/components/templates/TemplatesPageContent";
|
||||
import { createPageMetadata } from "@/lib/metadata";
|
||||
import { fetchProjects } from "@/lib/admin-api";
|
||||
import { adminProjectToCatalogTemplate } from "@/lib/video-templates-catalog";
|
||||
|
||||
export const metadata: Metadata = createPageMetadata({
|
||||
title: "Video Templates",
|
||||
@@ -10,10 +12,23 @@ export const metadata: Metadata = createPageMetadata({
|
||||
path: "/templates",
|
||||
});
|
||||
|
||||
export default function TemplatesPage() {
|
||||
export default async function TemplatesPage() {
|
||||
// Fetch video projects from the admin service.
|
||||
// When ADMIN_API_URL is not set or the service is unreachable this returns
|
||||
// an empty array → VideoTemplatesPageContent falls back to the demo catalog.
|
||||
const { items: adminProjects } = await fetchProjects({
|
||||
type: "video",
|
||||
pageSize: 100,
|
||||
});
|
||||
|
||||
const initialCatalog =
|
||||
adminProjects.length > 0
|
||||
? adminProjects.map(adminProjectToCatalogTemplate)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-white">
|
||||
<TemplatesPageContent />
|
||||
<TemplatesPageContent initialCatalog={initialCatalog} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user