create-trkit-app projelerindeki src/app/iletisim/ dizini çalışan bir örnektir. Bu sayfa o örneği adım adım açıklar.
1. Şema
src/app/iletisim/schema.ts
import { cepTelefonu } from "@trkit-app-pack/validate";
import { isValidDistrict } from "@trkit-app-pack/address";
import { basHarfleriBuyut } from "@trkit-app-pack/format";
import { z } from "@/lib/zod"; // Türkçe hata mesajları
export const iletisimSchema = z
.object({
adSoyad: z.string().trim().min(3).max(80).transform(basHarfleriBuyut),
eposta: z.email(),
telefon: cepTelefonu, // "0532 123 45 67" → "+905321234567"
il: z.coerce.number().int().min(1, "İl seçiniz").max(81),
ilce: z.string({ message: "İlçe seçiniz" }).min(1, "İlçe seçiniz"),
mesaj: z.string().trim().min(10).max(2000),
ticariIleti: z.boolean().default(false),
})
.refine((v) => isValidDistrict(v.il, v.ilce), { path: ["ilce"], message: "Seçilen ilçe bu ile ait değil" });Hazır mesajlar: "En az 3 karakter olmalıdır", "Geçerli bir e-posta adresi giriniz", "Bu alan zorunludur", "Geçerli bir cep telefonu giriniz (5XX XXX XX XX)".
2. Server action
src/app/iletisim/actions.ts
"use server";
import { iletisimSchema } from "./schema";
export async function iletisimGonder(_prev: FormState, formData: FormData): Promise<FormState> {
const values = Object.fromEntries(formData) as Record<string, string>;
const parsed = iletisimSchema.safeParse({ ...values, ticariIleti: values.ticariIleti === "on" });
if (!parsed.success) {
const errors: Record<string, string> = {};
for (const i of parsed.error.issues) errors[String(i.path[0])] ??= i.message;
return { status: "error", errors, values };
}
// parsed.data.telefon E.164 biçiminde, adSoyad düzgün büyük harfli
return { status: "success", adSoyad: parsed.data.adSoyad };
}3. Form
const [state, action, pending] = useActionState(iletisimGonder, { status: "idle" });
const [konum, setKonum] = useState<IlIlceDegeri>({ il: null, ilce: null });
<form action={action}>
<input name="telefon" type="tel" inputMode="tel" autoComplete="tel-national" placeholder="05XX XXX XX XX" />
<IlIlceSecici value={konum} onChange={setKonum} name={{ il: "il", ilce: "ilce" }} error={{ il: err.il, ilce: err.ilce }} />
</form>Hazır şemalar
| Şema | Kabul eder | Dönüştürür |
|---|---|---|
tckn | 11 hane, algoritma kontrolü | boşlukları temizler |
vkn | 10 hane, algoritma kontrolü | |
vergiNo | TCKN veya VKN (fatura formları) | |
iban | TR IBAN, mod-97 | TR33 0006 … → TR330006… |
telefon | mobil, sabit, 850 | E.164 +90… |
cepTelefonu | yalnızca 5XX | E.164 |
plaka | 34 ABC 123 vb. | 34ABC123 |
postaKodu | 5 hane, il kodu 01–81 | |
mersis | 16 hane |
Özel mesaj için şema fabrikalarını kullanın: tcknSchema({ message: "TC kimlik numaranızı kontrol edin" }), phoneSchema({ mobileOnly: true }).
İpuçları
- Telefon alanında
inputMode="tel"mobilde rakam klavyesini açar. - TCKN algoritma kontrolü numaranın gerçek bir kişiye ait olduğunu göstermez. Kimlik doğrulaması gerekiyorsa NVİ KPS servisi gerekir.
- Loglarda
maskTckn()vemaskPhone()kullanın.