Files
flatrender/src/components/dashboard/DashboardSidebar.tsx
T
Soroush.Asadi 36e264f3e3 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
2026-05-27 09:06:51 +03:30

88 lines
2.9 KiB
TypeScript

import Link from "next/link";
import { Suspense } from "react";
import { LogoMark } from "@/components/ui/LogoMark";
import {
DashboardPlanBadge,
DashboardPlanBadgeSkeleton,
} from "@/components/dashboard/DashboardPlanBadge";
import { DashboardSidebarNav } from "@/components/dashboard/DashboardSidebarNav";
interface DashboardSidebarProps {
userEmail: string;
userName?: string | null;
userId: string;
}
function getInitials(email: string, name?: string | null): string {
if (name?.trim()) {
const parts = name.trim().split(/\s+/);
return parts
.slice(0, 2)
.map((part) => part[0]?.toUpperCase() ?? "")
.join("");
}
return email.slice(0, 2).toUpperCase();
}
export function DashboardSidebar({
userEmail,
userName,
userId,
}: DashboardSidebarProps) {
const initials = getInitials(userEmail, userName);
return (
<aside className="flex h-full w-60 shrink-0 flex-col border-r border-gray-100 bg-white">
<div className="border-b border-gray-100 px-4 py-5">
<Link
href="/"
className="flex items-center gap-2 rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2"
>
<LogoMark size={36} />
<span className="font-heading text-lg font-bold text-neutral-900">
FlatRender
</span>
</Link>
</div>
<DashboardSidebarNav />
<div className="border-t border-gray-100 p-4">
<div className="mb-3 rounded-lg border border-gray-100 bg-neutral-50 p-3">
<p className="text-xs font-medium uppercase tracking-wide text-neutral-500">
Current plan
</p>
<Suspense fallback={<DashboardPlanBadgeSkeleton />}>
<DashboardPlanBadge userId={userId} />
</Suspense>
</div>
<div className="flex items-center gap-3 rounded-lg px-2 py-2">
<div
className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary-100 font-heading text-sm font-semibold text-primary-700"
aria-hidden
>
{initials}
</div>
<div className="min-w-0">
<p className="truncate text-sm font-medium text-neutral-900">
{userName ?? userEmail.split("@")[0]}
</p>
<p className="truncate text-xs text-neutral-500">{userEmail}</p>
</div>
</div>
<form action="/auth/sign-out" method="post" className="mt-3">
<button
type="submit"
className="w-full rounded-lg px-3 py-2 text-left text-sm text-neutral-600 transition-colors hover:bg-neutral-50 hover:text-neutral-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-600 focus-visible:ring-offset-2"
>
Sign out
</button>
</form>
</div>
</aside>
);
}