feat(dashboard): personalized header + reuse dashboard-2 components

- /dashboard now a server component:
  * fetches active user + active tenant settings via getActiveContext()
  * redirects to /onboarding if user has no tenant yet
  * header shows companyName + 'Hoş geldiniz, {firstName}' + Turkish description
- Body reuses dashboard-2 components (Metrics/Sales/Revenue/Transactions/
  TopProducts/CustomerInsights/QuickActions). Mock data for now; will be
  swapped for live Appwrite queries as modules ship.
- New lib/appwrite/active-context.ts: getActiveContext() returns
  { user, tenantId, settings } for any server component / action.
- Made schema.ts SDK-agnostic by replacing Models.Document import with a
  local SystemRow type ({ $id, $createdAt, $updatedAt, $permissions, ... }).
  Avoids type clashes between appwrite (browser) and node-appwrite (server).
This commit is contained in:
kovakmedya
2026-04-30 03:33:53 +03:00
parent 19a0e2b11f
commit 8a7742af1b
3 changed files with 98 additions and 30 deletions
+42 -26
View File
@@ -1,35 +1,51 @@
import { ChartAreaInteractive } from "./components/chart-area-interactive" import { redirect } from "next/navigation";
import { DataTable } from "./components/data-table"
import { SectionCards } from "./components/section-cards"
import data from "./data/data.json" import { getActiveContext } from "@/lib/appwrite/active-context";
import pastPerformanceData from "./data/past-performance-data.json" import { CustomerInsights } from "../dashboard-2/components/customer-insights";
import keyPersonnelData from "./data/key-personnel-data.json" import { MetricsOverview } from "../dashboard-2/components/metrics-overview";
import focusDocumentsData from "./data/focus-documents-data.json" import { QuickActions } from "../dashboard-2/components/quick-actions";
import { RecentTransactions } from "../dashboard-2/components/recent-transactions";
import { RevenueBreakdown } from "../dashboard-2/components/revenue-breakdown";
import { SalesChart } from "../dashboard-2/components/sales-chart";
import { TopProducts } from "../dashboard-2/components/top-products";
export default async function DashboardPage() {
const ctx = await getActiveContext();
if (!ctx) redirect("/onboarding");
const firstName = ctx.user.name?.split(" ")[0] ?? "";
const companyName = ctx.settings?.companyName ?? "Çalışma alanı";
export default function Page() {
return ( return (
<> <div className="flex-1 space-y-6 px-6 pt-0">
{/* Page Title and Description */} <div className="flex flex-col justify-between gap-4 md:flex-row md:items-center md:gap-6">
<div className="px-4 lg:px-6"> <div className="flex flex-col gap-1">
<div className="flex flex-col gap-2"> <p className="text-muted-foreground text-sm">{companyName}</p>
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1> <h1 className="text-2xl font-bold tracking-tight">
<p className="text-muted-foreground">Welcome to your admin dashboard</p> {firstName ? `Hoş geldiniz, ${firstName}` : "Genel bakış"}
</h1>
<p className="text-muted-foreground text-sm">
İşletmenizin temel metriklerini ve son hareketleri buradan takip edin.
</p>
</div> </div>
<QuickActions />
</div> </div>
<div className="@container/main px-4 lg:px-6 space-y-6"> <div className="@container/main space-y-6">
<SectionCards /> <MetricsOverview />
<ChartAreaInteractive />
<div className="grid grid-cols-1 gap-6 @5xl:grid-cols-2">
<SalesChart />
<RevenueBreakdown />
</div> </div>
<div className="@container/main">
<DataTable <div className="grid grid-cols-1 gap-6 @5xl:grid-cols-2">
data={data} <RecentTransactions />
pastPerformanceData={pastPerformanceData} <TopProducts />
keyPersonnelData={keyPersonnelData}
focusDocumentsData={focusDocumentsData}
/>
</div> </div>
</>
) <CustomerInsights />
</div>
</div>
);
} }
+44
View File
@@ -0,0 +1,44 @@
import "server-only";
import { Query } from "node-appwrite";
import { createAdminClient, getCurrentUser } from "./server";
import { DATABASE_ID, TABLES, type TenantSettings } from "./schema";
import { getActiveTenantId, getUserTeams } from "./tenant";
export type ActiveContext = {
user: { id: string; name: string; email: string };
tenantId: string;
settings: TenantSettings | null;
};
export async function getActiveContext(): Promise<ActiveContext | null> {
const user = await getCurrentUser();
if (!user) return null;
let tenantId = await getActiveTenantId();
if (!tenantId) {
const teams = await getUserTeams();
tenantId = teams?.teams[0]?.$id ?? null;
}
if (!tenantId) return null;
let settings: TenantSettings | null = null;
try {
const { tablesDB } = createAdminClient();
const result = await tablesDB.listRows({
databaseId: DATABASE_ID,
tableId: TABLES.tenantSettings,
queries: [Query.equal("tenantId", tenantId), Query.limit(1)],
});
settings = (result.rows[0] as unknown as TenantSettings) ?? null;
} catch {
settings = null;
}
return {
user: { id: user.$id, name: user.name, email: user.email },
tenantId,
settings,
};
}
+11 -3
View File
@@ -1,5 +1,3 @@
import type { Models } from "appwrite";
export const DATABASE_ID = "isletmem"; export const DATABASE_ID = "isletmem";
export const TABLES = { export const TABLES = {
@@ -18,7 +16,17 @@ export const TABLES = {
export type TableId = (typeof TABLES)[keyof typeof TABLES]; export type TableId = (typeof TABLES)[keyof typeof TABLES];
type Row = Models.Document; export type SystemRow = {
$id: string;
$createdAt: string;
$updatedAt: string;
$permissions: string[];
$databaseId?: string;
$tableId?: string;
$sequence?: number;
};
type Row = SystemRow;
export type TenantRole = "owner" | "admin" | "member"; export type TenantRole = "owner" | "admin" | "member";