feat: all core modules — properties, customers, searches, matches, presentations, activities, investors + public sunum page
- Server actions: property/customer/search/presentation/activity/investor CRUD - Matching engine: matchPropertyToSearches + syncMatchesForSearch on search save - UI: form sheets + table clients for all modules - Public /sunum/[token] page (no auth) with property card grid + expiry check - All pages force-dynamic for auth guard compatibility
This commit is contained in:
@@ -1,8 +1,41 @@
|
||||
export default function Page() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { Query } from "node-appwrite";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { listCustomers } from "@/lib/appwrite/customer-queries";
|
||||
import { listProperties } from "@/lib/appwrite/property-queries";
|
||||
import { DATABASE_ID, TABLES, type Activity } from "@/lib/appwrite/schema";
|
||||
import { createAdminClient } from "@/lib/appwrite/server";
|
||||
import { ActivitiesClient } from "@/components/activities/activities-client";
|
||||
|
||||
export default async function ActivitiesPage() {
|
||||
const ctx = await requireTenant();
|
||||
const { tablesDB } = createAdminClient();
|
||||
|
||||
const [customers, properties, activitiesResult] = await Promise.all([
|
||||
listCustomers(ctx.tenantId),
|
||||
listProperties(ctx.tenantId),
|
||||
tablesDB.listRows({
|
||||
databaseId: DATABASE_ID,
|
||||
tableId: TABLES.activities,
|
||||
queries: [
|
||||
Query.equal("tenantId", ctx.tenantId),
|
||||
Query.orderDesc("$createdAt"),
|
||||
Query.limit(300),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
const activities = activitiesResult.rows as unknown as Activity[];
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold capitalize">activities</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<ActivitiesClient
|
||||
initialActivities={activities}
|
||||
customers={customers}
|
||||
properties={properties}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,81 @@
|
||||
export default function MatchesPage() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { Query } from "node-appwrite";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { listCustomers } from "@/lib/appwrite/customer-queries";
|
||||
import { listProperties } from "@/lib/appwrite/property-queries";
|
||||
import { DATABASE_ID, TABLES, type PropertyMatch } from "@/lib/appwrite/schema";
|
||||
import { createAdminClient } from "@/lib/appwrite/server";
|
||||
|
||||
export default async function MatchesPage() {
|
||||
const ctx = await requireTenant();
|
||||
const { tablesDB } = createAdminClient();
|
||||
|
||||
const [customers, properties, matchesResult] = await Promise.all([
|
||||
listCustomers(ctx.tenantId),
|
||||
listProperties(ctx.tenantId),
|
||||
tablesDB.listRows({
|
||||
databaseId: DATABASE_ID,
|
||||
tableId: TABLES.propertyMatches,
|
||||
queries: [
|
||||
Query.equal("tenantId", ctx.tenantId),
|
||||
Query.orderDesc("$createdAt"),
|
||||
Query.limit(500),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
const matches = matchesResult.rows as unknown as PropertyMatch[];
|
||||
const customerMap = Object.fromEntries(customers.map((c) => [c.$id, c.name]));
|
||||
const propertyMap = Object.fromEntries(properties.map((p) => [p.$id, p.title]));
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold">Eşleşmeler</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Eşleşmeler</h1>
|
||||
<span className="text-muted-foreground text-sm">{matches.length} eşleşme</span>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b">
|
||||
<th className="text-left p-3 font-medium">Müşteri</th>
|
||||
<th className="text-left p-3 font-medium">İlan</th>
|
||||
<th className="text-left p-3 font-medium">Tarih</th>
|
||||
<th className="text-left p-3 font-medium">Görüntülendi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{matches.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={4} className="text-muted-foreground text-center py-10">
|
||||
Henüz eşleşme yok.
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{matches.map((m) => (
|
||||
<tr key={m.$id} className="border-b last:border-0 hover:bg-muted/30">
|
||||
<td className="p-3">{customerMap[m.customerId] ?? m.customerId}</td>
|
||||
<td className="p-3">{propertyMap[m.propertyId] ?? m.propertyId}</td>
|
||||
<td className="p-3 text-muted-foreground">
|
||||
{new Date(m.$createdAt).toLocaleDateString("tr-TR")}
|
||||
</td>
|
||||
<td className="p-3">
|
||||
{m.viewedAt ? (
|
||||
<span className="text-green-600 text-xs">
|
||||
{new Date(m.viewedAt).toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-muted-foreground text-xs">Hayır</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
export default function CustomersPage() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { listCustomers } from "@/lib/appwrite/customer-queries";
|
||||
import { CustomersClient } from "@/components/customers/customers-client";
|
||||
|
||||
export default async function CustomersPage() {
|
||||
const ctx = await requireTenant();
|
||||
const customers = await listCustomers(ctx.tenantId);
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold">Müşteriler</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<CustomersClient initialCustomers={customers} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,35 @@
|
||||
export default function SearchesPage() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { Query } from "node-appwrite";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { listCustomers } from "@/lib/appwrite/customer-queries";
|
||||
import { DATABASE_ID, TABLES, type CustomerSearch } from "@/lib/appwrite/schema";
|
||||
import { createAdminClient } from "@/lib/appwrite/server";
|
||||
import { SearchesClient } from "@/components/customers/searches-client";
|
||||
|
||||
export default async function SearchesPage() {
|
||||
const ctx = await requireTenant();
|
||||
const { tablesDB } = createAdminClient();
|
||||
|
||||
const [customers, searchesResult] = await Promise.all([
|
||||
listCustomers(ctx.tenantId),
|
||||
tablesDB.listRows({
|
||||
databaseId: DATABASE_ID,
|
||||
tableId: TABLES.customerSearches,
|
||||
queries: [
|
||||
Query.equal("tenantId", ctx.tenantId),
|
||||
Query.orderDesc("$createdAt"),
|
||||
Query.limit(500),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
const searches = searchesResult.rows as unknown as CustomerSearch[];
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold">Arama Kriterleri</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<SearchesClient initialSearches={searches} customers={customers} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
export default function Page() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { Query } from "node-appwrite";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { DATABASE_ID, TABLES, type Investor } from "@/lib/appwrite/schema";
|
||||
import { createAdminClient } from "@/lib/appwrite/server";
|
||||
import { InvestorsClient } from "@/components/investors/investors-client";
|
||||
|
||||
export default async function InvestorsPage() {
|
||||
const ctx = await requireTenant();
|
||||
const { tablesDB } = createAdminClient();
|
||||
|
||||
const result = await tablesDB.listRows({
|
||||
databaseId: DATABASE_ID,
|
||||
tableId: TABLES.investors,
|
||||
queries: [
|
||||
Query.equal("tenantId", ctx.tenantId),
|
||||
Query.orderDesc("$createdAt"),
|
||||
Query.limit(200),
|
||||
],
|
||||
});
|
||||
|
||||
const investors = result.rows as unknown as Investor[];
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold capitalize">investors</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<InvestorsClient initialInvestors={investors} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
export default function Page() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { Query } from "node-appwrite";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { listCustomers } from "@/lib/appwrite/customer-queries";
|
||||
import { listProperties } from "@/lib/appwrite/property-queries";
|
||||
import { DATABASE_ID, TABLES, type Presentation } from "@/lib/appwrite/schema";
|
||||
import { createAdminClient } from "@/lib/appwrite/server";
|
||||
import { PresentationsClient } from "@/components/presentations/presentations-client";
|
||||
|
||||
export default async function PresentationsPage() {
|
||||
const ctx = await requireTenant();
|
||||
const { tablesDB } = createAdminClient();
|
||||
|
||||
const [customers, properties, presResult] = await Promise.all([
|
||||
listCustomers(ctx.tenantId),
|
||||
listProperties(ctx.tenantId),
|
||||
tablesDB.listRows({
|
||||
databaseId: DATABASE_ID,
|
||||
tableId: TABLES.presentations,
|
||||
queries: [
|
||||
Query.equal("tenantId", ctx.tenantId),
|
||||
Query.orderDesc("$createdAt"),
|
||||
Query.limit(200),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
|
||||
const presentations = presResult.rows as unknown as Presentation[];
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold capitalize">presentations</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<PresentationsClient
|
||||
initialPresentations={presentations}
|
||||
customers={customers}
|
||||
properties={properties}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
export default function Page() {
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { requireTenant } from "@/lib/appwrite/tenant-guard";
|
||||
import { listProperties } from "@/lib/appwrite/property-queries";
|
||||
import { PropertiesClient } from "@/components/properties/properties-client";
|
||||
|
||||
export default async function PropertiesPage() {
|
||||
const ctx = await requireTenant();
|
||||
const properties = await listProperties(ctx.tenantId);
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 flex-col gap-4 p-4">
|
||||
<h1 className="text-2xl font-bold capitalize">properties</h1>
|
||||
<p className="text-muted-foreground">Yakında...</p>
|
||||
<div className="flex flex-1 flex-col gap-4 p-4 md:p-6">
|
||||
<PropertiesClient initialProperties={properties} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user