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:
@@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user