Skip to content

Commit ea4969c

Browse files
committed
feature: Added contact form
1 parent 39f3e1b commit ea4969c

File tree

8 files changed

+593
-0
lines changed

8 files changed

+593
-0
lines changed

app/page.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
"use client"
2+
3+
import { GoogleReCaptchaProvider } from "react-google-recaptcha-v3"
4+
5+
import ContactSection from "@/components/home/contact"
16
import Hero from "@/components/home/hero"
27
import References from "@/components/home/references"
38
import Showcase from "@/components/home/showcase"
@@ -8,6 +13,17 @@ export default function IndexPage() {
813
<Hero />
914
<Showcase />
1015
<References />
16+
<GoogleReCaptchaProvider
17+
reCaptchaKey="6LdX36UeAAAAALStl0KL5co3B-dU6gTnx_osvTWJ"
18+
scriptProps={{
19+
async: false,
20+
defer: false,
21+
appendTo: "head",
22+
nonce: undefined,
23+
}}
24+
>
25+
<ContactSection />
26+
</GoogleReCaptchaProvider>
1127
</>
1228
)
1329
}

components/contact-form.tsx

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
"use client"
2+
3+
import { useCallback, useState } from "react"
4+
import { zodResolver } from "@hookform/resolvers/zod"
5+
import axios from "axios"
6+
import { AlertCircle, ArrowRight, Loader2, MailCheck } from "lucide-react"
7+
import { useGoogleReCaptcha } from "react-google-recaptcha-v3"
8+
import { useForm } from "react-hook-form"
9+
import * as z from "zod"
10+
11+
import { Button } from "@/components/ui/button"
12+
import {
13+
Form,
14+
FormControl,
15+
FormField,
16+
FormItem,
17+
FormLabel,
18+
FormMessage,
19+
} from "@/components/ui/form"
20+
import { Input } from "@/components/ui/input"
21+
22+
import { Alert, AlertDescription, AlertTitle } from "./ui/alert"
23+
import { Textarea } from "./ui/textarea"
24+
25+
const formSchema = z.object({
26+
"kurum-adi": z
27+
.string()
28+
.min(2, {
29+
message: "Kurum adı en az 2 karakter olmalıdır.",
30+
})
31+
.max(255, {
32+
message: "Kurum adı en fazla 255 karakter olmalıdır.",
33+
}),
34+
name: z
35+
.string()
36+
.min(2, {
37+
message: "İsim en az 2 karakter olmalıdır.",
38+
})
39+
.max(255, {
40+
message: "İsim en fazla 255 karakter olmalıdır.",
41+
}),
42+
email: z
43+
.string()
44+
.email({
45+
message: "Geçerli bir e-posta adresi giriniz.",
46+
})
47+
.max(255, {
48+
message: "E-posta adresi en fazla 255 karakter olmalıdır.",
49+
}),
50+
telefon: z
51+
.string()
52+
.min(2, {
53+
message: "Telefon en az 2 karakter olmalıdır.",
54+
})
55+
.max(255, {
56+
message: "Telefon en fazla 255 karakter olmalıdır.",
57+
}),
58+
aciklama: z
59+
.string()
60+
.min(2, {
61+
message: "Açıklama en az 2 karakter olmalıdır.",
62+
})
63+
.max(500, {
64+
message: "Açıklama en fazla 500 karakter olmalıdır.",
65+
}),
66+
})
67+
68+
export function ContactForm() {
69+
const { executeRecaptcha } = useGoogleReCaptcha()
70+
71+
const [error, setError] = useState<boolean>(false)
72+
const [loading, setLoading] = useState<boolean>(false)
73+
const [message, setMessage] = useState<string>("")
74+
75+
const form = useForm<z.infer<typeof formSchema>>({
76+
resolver: zodResolver(formSchema),
77+
defaultValues: {
78+
"kurum-adi": "",
79+
name: "",
80+
email: "",
81+
telefon: "",
82+
aciklama: "",
83+
},
84+
})
85+
86+
const handleOnSubmit = useCallback(
87+
(values: z.infer<typeof formSchema>) => {
88+
setLoading(true)
89+
if (!executeRecaptcha) {
90+
setError(true)
91+
setMessage(
92+
"Bot kontrolü yapılırken bir hata oluştu. Biraz bekledikten sonra tekrar deneyin."
93+
)
94+
95+
return
96+
}
97+
98+
executeRecaptcha("subscribe_newsletter").then((token) => {
99+
submitForm(values, token)
100+
})
101+
},
102+
[executeRecaptcha]
103+
)
104+
105+
function submitForm(values: z.infer<typeof formSchema>, token: string) {
106+
axios
107+
.post<string>(
108+
"https://ws.aciklab.org/contact_liman",
109+
{
110+
...values,
111+
token,
112+
action: "subscribe_newsletter",
113+
},
114+
{
115+
headers: {
116+
"Content-Type": "multipart/form-data",
117+
},
118+
}
119+
)
120+
.then(({ status, data }) => {
121+
if (status != 200) {
122+
setError(true)
123+
} else {
124+
setError(false)
125+
}
126+
127+
setMessage(data)
128+
})
129+
.catch(() => {
130+
setError(true)
131+
setMessage("Bir hata oluştu. Lütfen tekrar deneyin.")
132+
})
133+
.finally(() => {
134+
setLoading(false)
135+
})
136+
}
137+
138+
return (
139+
<Form {...form}>
140+
<form
141+
onSubmit={form.handleSubmit(handleOnSubmit)}
142+
className="w-full space-y-8"
143+
>
144+
{message && (
145+
<Alert variant={!error ? "default" : "destructive"}>
146+
{!error ? (
147+
<MailCheck className="h-4 w-4" />
148+
) : (
149+
<AlertCircle className="h-4 w-4" />
150+
)}
151+
<AlertTitle>{!error ? "Bilgilendirme" : "Hata"}</AlertTitle>
152+
<AlertDescription>{message}</AlertDescription>
153+
</Alert>
154+
)}
155+
156+
<div className="grid grid-cols-2 gap-5">
157+
<FormField
158+
control={form.control}
159+
name="name"
160+
render={({ field }) => (
161+
<FormItem>
162+
<FormLabel>
163+
İsim
164+
<span className="ml-1 font-bold text-red-500">*</span>
165+
</FormLabel>
166+
<FormControl>
167+
<Input placeholder="" {...field} />
168+
</FormControl>
169+
<FormMessage />
170+
</FormItem>
171+
)}
172+
/>
173+
174+
<FormField
175+
control={form.control}
176+
name="email"
177+
render={({ field }) => (
178+
<FormItem>
179+
<FormLabel>
180+
E-posta
181+
<span className="ml-1 font-bold text-red-500">*</span>
182+
</FormLabel>
183+
<FormControl>
184+
<Input placeholder="liman@aciklab.org" {...field} />
185+
</FormControl>
186+
<FormMessage />
187+
</FormItem>
188+
)}
189+
/>
190+
</div>
191+
192+
<div className="grid grid-cols-2 gap-5">
193+
<FormField
194+
control={form.control}
195+
name="kurum-adi"
196+
render={({ field }) => (
197+
<FormItem>
198+
<FormLabel>
199+
Kurum Adı
200+
<span className="ml-1 font-bold text-red-500">*</span>
201+
</FormLabel>
202+
<FormControl>
203+
<Input placeholder="HAVELSAN A.Ş." {...field} />
204+
</FormControl>
205+
<FormMessage />
206+
</FormItem>
207+
)}
208+
/>
209+
210+
<FormField
211+
control={form.control}
212+
name="telefon"
213+
render={({ field }) => (
214+
<FormItem>
215+
<FormLabel>
216+
Telefon
217+
<span className="ml-1 font-bold text-red-500">*</span>
218+
</FormLabel>
219+
<FormControl>
220+
<Input placeholder="+90 500 000 00 00" {...field} />
221+
</FormControl>
222+
<FormMessage />
223+
</FormItem>
224+
)}
225+
/>
226+
</div>
227+
228+
<FormField
229+
control={form.control}
230+
name="aciklama"
231+
render={({ field }) => (
232+
<FormItem>
233+
<FormLabel>
234+
Açıklama<span className="ml-1 font-bold text-red-500">*</span>
235+
</FormLabel>
236+
<FormControl>
237+
<Textarea
238+
placeholder="İhtiyacınızı ve isteklerinizi belirtebilirsiniz..."
239+
{...field}
240+
/>
241+
</FormControl>
242+
<FormMessage />
243+
</FormItem>
244+
)}
245+
/>
246+
247+
<Button type="submit" disabled={loading}>
248+
Gönder
249+
{!loading ? (
250+
<ArrowRight className="ml-2 h-4 w-4" />
251+
) : (
252+
<Loader2 className="ml-2 h-4 w-4 animate-spin" />
253+
)}
254+
</Button>
255+
</form>
256+
<small className="mt-3 block w-full text-muted-foreground/70">
257+
Bu iletişim formu reCAPTCHA from Google ile korunmaktadır.
258+
<br />
259+
<a href="https://policies.google.com/privacy" className="mr-2">
260+
Gizlilik Politikası
261+
</a>
262+
<a href="https://policies.google.com/terms">Hizmet Koşulları</a>
263+
</small>
264+
</Form>
265+
)
266+
}

components/home/contact.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { fontHeading } from "@/lib/fonts"
2+
import { cn } from "@/lib/utils"
3+
4+
import { ContactForm } from "../contact-form"
5+
import { Card, CardContent } from "../ui/card"
6+
7+
export default function ContactSection() {
8+
return (
9+
<section id="contact" className="border-t">
10+
<div className="container py-12">
11+
<div className="grid grid-cols-5 gap-8">
12+
<div className="col-span-2 flex flex-col justify-center">
13+
<h5
14+
className={cn(
15+
"bg-gradient-to-r from-cyan-400 to-blue-500 bg-clip-text font-heading text-4xl text-transparent",
16+
fontHeading.variable
17+
)}
18+
>
19+
Bizimle <br />
20+
iletişime geçin
21+
</h5>
22+
23+
<p className="mt-6 leading-relaxed text-muted-foreground">
24+
Ücretsiz Demo İsteyin! BT süreçlerinizde aksaklığa sebep olmadan
25+
kavram ispat çalışması sunuyoruz. Sistem yönetimi projenizin ilk
26+
adımını risk almadan Liman Merkezi Yönetim Sistemi ile
27+
başlayabilirsiniz. Kavram ispat çalışması sonrası 30 gün ücretsiz
28+
deneme sürümünü sizler de test edebilirsiniz. <br /> <br />
29+
Liman Merkezi Yönetim Sistemi halihazırda bulunan sisteminize
30+
kesinti yaşatmadan entegre olabilmektedir. Bileşen olarak Cihaz
31+
Yöneticisi, Ağ Keşif, Ağ İzleme, Domain Yöneticisi ve
32+
Konfigürasyon Yöneticimizi talep edebilirsiniz. Modüler yapımız
33+
sayesinde ihtiyacınız olan bileşenleri seçerek PoC talep
34+
edebilirsiniz.
35+
</p>
36+
</div>
37+
<div className="col-span-3">
38+
<Card className="shadow shadow-accent">
39+
<CardContent className="p-8">
40+
<div className="mb-5 font-semibold leading-none tracking-tight text-foreground">
41+
Aşağıdaki formu doldurun ve takımımız sizinle iletişime
42+
geçsin.
43+
</div>
44+
<ContactForm />
45+
</CardContent>
46+
</Card>
47+
</div>
48+
</div>
49+
</div>
50+
</section>
51+
)
52+
}

0 commit comments

Comments
 (0)