Skip to content

feat: add system settings admin UI and branding#29

Merged
kemalcalak merged 7 commits into
mainfrom
system-settings
Jun 16, 2026
Merged

feat: add system settings admin UI and branding#29
kemalcalak merged 7 commits into
mainfrom
system-settings

Conversation

@kemalcalak

Copy link
Copy Markdown
Owner

No description provided.

@kemalcalak kemalcalak self-assigned this Jun 16, 2026
@kemalcalak

Copy link
Copy Markdown
Owner Author

/review — 7 bulgu

1. ../ göreli import — @/ alias zorunlu

Dosya: src/app/[locale]/admin/(protected)/system-settings/SystemSettingsContent.tsx#L12
Kategori: code-style · Önem: major

REVIEW.md §3.1 @/ alias'ını zorunlu kılar; ../ göreceli import yasaktır. AdminForbidden @/ ile tam yol verilmeli.

import { AdminForbidden } from "../AdminForbidden";

Öneri: import { AdminForbidden } from "@/app/[locale]/admin/(protected)/AdminForbidden";


2. Inline prop tipi — interface MaintenanceGateProps eksik

Dosya: src/components/common/MaintenanceGate.tsx#L21
Kategori: code-comment · Önem: major

REVIEW.md §5.8 tüm bileşen prop'larının ayrı bir interface XProps ile tanımlanmasını zorunlu kılar (lint yakalamaz, elle denetlenir). Inline { children: React.ReactNode } yerine adlandırılmış interface kullanılmalı.

export function MaintenanceGate({ children }: { children: React.ReactNode }) {

Öneri: Üstüne interface MaintenanceGateProps { children: React.ReactNode } ekleyip imzayı MaintenanceGate({ children }: MaintenanceGateProps) yap.


3. BrandLogo <img> yerine Next.js <Image> kullanmalı

Dosya: src/components/common/BrandLogo.tsx#L24
Kategori: review-md-compliance · Önem: major

REVIEW.md §5.1 <img> yerine Next.js <Image> kullanımını zorunlu kılar. Logo her header/sidebar/drawer render'ında göründüğünden format optimizasyonu (WebP/AVIF), otomatik lazy-load ve LCP etkisi var. res.cloudinary.com zaten next.config.ts remotePatterns'ında tanımlı, <Image> doğrudan çalışır.

<img src={logoUrl} alt={siteName} className="h-full w-full object-contain" />

Öneri: import Image from "next/image" ekleyip <Image src={logoUrl} alt={siteName} fill className="object-contain" /> kullan.


4. as PublicSettingsResponse cast — any üzerine tip zorlaması

Dosya: src/lib/server/site-name.ts#L20
Kategori: code-comment · Önem: major

Response.json() TypeScript'te any döner; doğrudan as PublicSettingsResponse cast etmek tip güvenliğini ortadan kaldırır ve REVIEW.md §5.8 as cast yasağını tetikler. Önce unknown alınıp narrowing uygulanmalı.

const json = (await res.json()) as PublicSettingsResponse;

Öneri: const json: unknown = await res.json(); — sonra typeof/in guard ile site_name alanına eriş.


5. onSave async handler'da try-catch yok — unhandled rejection

Dosya: src/app/[locale]/admin/(protected)/system-settings/SystemSettingsContent.tsx#L37-L40
Kategori: bug · Önem: minor

onSave, Promise.all ile birden fazla mutateAsync çağırıyor; herhangi biri reject ederse Promise.all reddedilir, setDraft({}) çalışmaz ve onClick handler unhandledrejection üretir (Error Boundary yakalamaz). REVIEW.md §5.5 submit handler'ın düzgün hata yönetimine sahip olmasını zorunlu kılar.

const onSave = async () => { await Promise.all(dirtyKeys.map((key) => update.mutateAsync({ key, value: draft[key] }))); setDraft({}); };

Öneri: try { await Promise.all(...); setDraft({}); } catch { /* interceptor toast'ı gösterdi */ } ile sar.


6. defaultVariables.appName kaldırıldı — {{appName}} yükleme sırasında boş render

Dosya: src/i18n/config.ts#L116-L119
Kategori: history · Önem: minor

PR #23 i18n interpolation bloğuna defaultVariables: { appName } eklemişti; bu PR onu kaldırıyor. Ancak home.json hâlâ {{appName}} interpolasyonu kullanıyor. useBranding() veri gelene kadar siteName "" kaldığından /settings/public yanıt vermeden başlık "Find your people at " şeklinde boş render olur.

PR #23 (commit 8f7fc8e): "improve type definitions and enhance security with environment variables" — defaultVariables: { appName: env.NEXT_PUBLIC_APP_NAME }

Öneri: useBranding() hook'una FALLBACK_SITE_NAME sabiti ekleyip siteName boşken bunu döndür.


7. Dil etiketleri (Türkçe/English) hardcode edilmiş

Dosya: src/components/admin/system-settings/SystemSettingsForm.tsx#L84-L85
Kategori: i18n · Önem: minor

default_locale Select'indeki Türkçe/English etiketleri kullanıcıya görünen sabit string. REVIEW.md §3.8/§5.7 hardcoded user-facing string'i yasak sayar; ayrıca UI İngilizce iken İngilizce beklenir.

{ value: "tr", label: "Türkçe" }, { value: "en", label: "English" }

Öneri: systemSettings namespace'ine locales: { tr: "Türkçe", en: "English" } (TR dosyasında İngilizce) ekleyip label: t("locales.tr") / t("locales.en") kullan.


🤖 Generated with Claude Code

@kemalcalak

Copy link
Copy Markdown
Owner Author

/review bulguları çözüldü ✅

Önceki review'daki 6 geçerli bulgu local'de giderildi:

  • BrandLogo — ham <img> yerine next/image <Image fill> kullanılıyor (test güncellendi).
  • MaintenanceGate — inline prop tipi interface MaintenanceGateProps ile adlandırıldı.
  • site-name.tsas PublicSettingsResponse cast'i kaldırıldı; unknown üzerinden tip-güvenli narrowing.
  • SystemSettingsContent.onSavetry/catch eklendi; hata durumunda draft korunuyor (unhandled rejection yok).
  • useBrandingsite_name yüklenene kadar FALLBACK_SITE_NAME = "App" döndürüyor; {{appName}} artık boş render olmuyor.
  • SystemSettingsForm — dil etiketleri t("locales.tr"|"locales.en") ile i18n'e taşındı (her iki dilde anahtarlar mevcut).

Not — ../AdminForbidden import'u (1. bulgu) bilinçli olarak değiştirilmedi. Bu, admin/(protected)/ route group'unun yerleşik konvansiyonu: admins, broadcasts, activities, files, support, users dahil tüm dosyalar shared bileşenleri (AdminForbidden, PermissionGate) tek seviyeli ../ ile import ediyor. False positive.

Doğrulama: tsc --noEmit ✅ · pnpm lint ✅ · BrandLogo.test.tsx 2/2 ✅

🤖 Generated with Claude Code

@kemalcalak kemalcalak merged commit 4666515 into main Jun 16, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant