cf46e30a7e
Rakip analizi (kocaelidijital.com, promedyanet.com, lf.com.tr) sonrası
satış blokerleri tespit edildi ve aşağıdaki bölümler eklendi:
1. ANASAYFADA SSS (8 hazır soru, admin'den düzenlenebilir)
- Fiyat, süre, ödeme, garanti, hosting, SEO, mevcut site yenileme,
sadece tasarım hizmeti gibi en sık sorulan sorular
- HomepageFaq component (sticky sol + accordion sağ)
- site_settings.homepage_faq[] (JSON {q,a})
2. RISK REVERSAL bölümü (Guarantee component)
- 'İlk taslak ücretsiz, memnun değilseniz devam etmiyoruz'
- 4 garanti maddesi checklist
- site_settings.guarantee_title/description/items
3. PROJE METRİKLERİ (vaka çalışması güçlendirme)
- projects.metrics[] (JSON {value,label})
- Detay sayfada büyük metric kartları
- Admin formda 'değer | etiket' satır formatı
4. HERO COPY GÜNCELLEMESİ (admin'den düzenlenebilir)
- 'Kocaeli'de 2-3 hafta içinde yayında olan, satan kurumsal web siteleri'
- 'İlk tasarım taslakı ücretsiz' vurgusu
- Trust band: 30 dk yanıt + ücretsiz taslak + 4.9 memnuniyet
5. /SITE-ANALIZI LEAD MAGNET SAYFASI
- URL + ad + email + telefon formu
- 6 analiz başlığı (CWV, mobil, SEO, güvenlik, içerik, rakip)
- contact_messages'a source=quick-site-audit ile yazılır
- 'subject' alanı ile inbox'ta ayırt edilebilir
6. EKİP BÖLÜMÜ (Hakkımızda sayfasında)
- Yeni team_members tablosu (name, role, bio, photo, linkedin)
- /admin/ekip CRUD sayfası
- TeamGrid component
7. SEKTÖR LANDING SAYFALARI (/sektor/[slug])
- Yeni industries tablosu (slug, title, content, features, faq, SEO)
- /admin/sektorler CRUD sayfası
- SEO + ad-targeted landing template
- Hero + trust + features + content + garanti + projeler + hizmetler + FAQ + JSON-LD
Admin /admin/site formuna yeni bölümler:
- 'Risk reversal / Garanti' (title + description + items)
- 'Anasayfa SSS' (---' bloklarla)
App sidebar'a 'Sektörler' ve 'Ekip' linkleri eklendi.
Footer'a 'Ücretsiz Site Analizi' linki eklendi.
36 route üretiliyor (önceki 31'den +5: /site-analizi, /sektor/[slug],
/admin/ekip + alt, /admin/sektorler + alt).
185 lines
4.6 KiB
TypeScript
185 lines
4.6 KiB
TypeScript
import "server-only";
|
|
import { DATABASE_ID, Q, TABLES, tablesDB } from "@/lib/appwrite-rest";
|
|
import { getSessionSecret } from "@/lib/auth";
|
|
import type {
|
|
BlogPostRow,
|
|
ContactMessageRow,
|
|
IndustryRow,
|
|
ProjectRow,
|
|
ServiceRow,
|
|
SeoPageRow,
|
|
SeoSettingsRow,
|
|
SiteSettingsRow,
|
|
TeamMemberRow,
|
|
TestimonialRow,
|
|
} from "@/lib/types";
|
|
|
|
async function safeList<T>(tableId: string, queries: string[]): Promise<T[]> {
|
|
try {
|
|
const res = await tablesDB.listRows<T>(DATABASE_ID, tableId, queries);
|
|
return res.rows;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
async function safeListAuth<T>(tableId: string, queries: string[]): Promise<T[]> {
|
|
try {
|
|
const secret = await getSessionSecret();
|
|
const res = await tablesDB.listRows<T>(
|
|
DATABASE_ID,
|
|
tableId,
|
|
queries,
|
|
secret ?? undefined,
|
|
);
|
|
return res.rows;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function listServices(opts?: { featured?: boolean }) {
|
|
const q = [Q.orderAsc("order"), Q.limit(50)];
|
|
if (opts?.featured) q.unshift(Q.equal("featured", true));
|
|
return safeList<ServiceRow>(TABLES.services, q);
|
|
}
|
|
|
|
export async function listProjects(opts?: {
|
|
featured?: boolean;
|
|
limit?: number;
|
|
serviceSlug?: string;
|
|
}) {
|
|
const q = [Q.orderDesc("year"), Q.limit(opts?.limit ?? 50)];
|
|
if (opts?.featured) q.unshift(Q.equal("featured", true));
|
|
if (opts?.serviceSlug) q.unshift(Q.equal("service_slug", opts.serviceSlug));
|
|
return safeList<ProjectRow>(TABLES.projects, q);
|
|
}
|
|
|
|
export async function getServiceBySlug(slug: string): Promise<ServiceRow | null> {
|
|
const res = await safeList<ServiceRow>(TABLES.services, [
|
|
Q.equal("slug", slug),
|
|
Q.limit(1),
|
|
]);
|
|
return res[0] ?? null;
|
|
}
|
|
|
|
export async function getProjectBySlug(slug: string): Promise<ProjectRow | null> {
|
|
const res = await safeList<ProjectRow>(TABLES.projects, [
|
|
Q.equal("slug", slug),
|
|
Q.limit(1),
|
|
]);
|
|
return res[0] ?? null;
|
|
}
|
|
|
|
export async function listPublishedPosts(opts?: { limit?: number }) {
|
|
return safeList<BlogPostRow>(TABLES.blogPosts, [
|
|
Q.equal("status", "published"),
|
|
Q.orderDesc("published_at"),
|
|
Q.limit(opts?.limit ?? 50),
|
|
]);
|
|
}
|
|
|
|
export async function listAllPosts() {
|
|
return safeListAuth<BlogPostRow>(TABLES.blogPosts, [
|
|
Q.orderDesc("$createdAt"),
|
|
Q.limit(200),
|
|
]);
|
|
}
|
|
|
|
export async function getPostBySlug(slug: string): Promise<BlogPostRow | null> {
|
|
const res = await safeList<BlogPostRow>(TABLES.blogPosts, [
|
|
Q.equal("slug", slug),
|
|
Q.limit(1),
|
|
]);
|
|
return res[0] ?? null;
|
|
}
|
|
|
|
export async function listTestimonials(opts?: { featured?: boolean }) {
|
|
const q = [Q.orderAsc("order"), Q.limit(50)];
|
|
if (opts?.featured) q.unshift(Q.equal("featured", true));
|
|
return safeList<TestimonialRow>(TABLES.testimonials, q);
|
|
}
|
|
|
|
export async function listMessages(status?: ContactMessageRow["status"]) {
|
|
const q = [Q.orderDesc("$createdAt"), Q.limit(200)];
|
|
if (status) q.unshift(Q.equal("status", status));
|
|
return safeListAuth<ContactMessageRow>(TABLES.contactMessages, q);
|
|
}
|
|
|
|
export async function getSeoPage(path: string): Promise<SeoPageRow | null> {
|
|
const res = await safeList<SeoPageRow>(TABLES.seoPages, [
|
|
Q.equal("path", path),
|
|
Q.limit(1),
|
|
]);
|
|
return res[0] ?? null;
|
|
}
|
|
|
|
export async function listSeoPages() {
|
|
return safeListAuth<SeoPageRow>(TABLES.seoPages, [
|
|
Q.orderAsc("path"),
|
|
Q.limit(200),
|
|
]);
|
|
}
|
|
|
|
export async function listTeamMembers() {
|
|
return safeList<TeamMemberRow>(TABLES.teamMembers, [
|
|
Q.orderAsc("order"),
|
|
Q.limit(50),
|
|
]);
|
|
}
|
|
|
|
export async function listIndustries(opts?: { featured?: boolean }) {
|
|
const q = [Q.orderAsc("order"), Q.limit(100)];
|
|
if (opts?.featured) q.unshift(Q.equal("featured", true));
|
|
return safeList<IndustryRow>(TABLES.industries, q);
|
|
}
|
|
|
|
export async function getIndustryBySlug(slug: string): Promise<IndustryRow | null> {
|
|
const res = await safeList<IndustryRow>(TABLES.industries, [
|
|
Q.equal("slug", slug),
|
|
Q.limit(1),
|
|
]);
|
|
return res[0] ?? null;
|
|
}
|
|
|
|
export async function getSiteSettings(): Promise<SiteSettingsRow | null> {
|
|
try {
|
|
return await tablesDB.getRow<SiteSettingsRow>(
|
|
DATABASE_ID,
|
|
TABLES.siteSettings,
|
|
"homepage",
|
|
);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function getSeoSettings(): Promise<SeoSettingsRow | null> {
|
|
try {
|
|
return await tablesDB.getRow<SeoSettingsRow>(
|
|
DATABASE_ID,
|
|
TABLES.seoSettings,
|
|
"global",
|
|
);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function getRow<T>(
|
|
tableId: string,
|
|
rowId: string,
|
|
): Promise<T | null> {
|
|
try {
|
|
const secret = await getSessionSecret();
|
|
return await tablesDB.getRow<T>(
|
|
DATABASE_ID,
|
|
tableId,
|
|
rowId,
|
|
secret ?? undefined,
|
|
);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|