@raxonltd/raxon-core 1.1.6 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/core/component/general.image.tsx +86 -0
  2. package/core/context/cart.context.tsx +446 -0
  3. package/core/context/security.context.tsx +151 -0
  4. package/core/feature/address/api/places.api.ts +76 -0
  5. package/core/feature/address/form/address-search-input.tsx +125 -0
  6. package/core/feature/address/hook/use.addres.tsx +63 -0
  7. package/core/feature/address/hook/use.address-autocomplete.ts +116 -0
  8. package/core/feature/address/util/address.types.ts +38 -0
  9. package/core/feature/address/util/parse-google-place.ts +66 -0
  10. package/core/feature/analytic-event/analytic.event.api.ts +27 -0
  11. package/core/feature/analytic-event/analytic.event.context.tsx +180 -0
  12. package/core/feature/analytic-event/analytic.event.util.ts +42 -0
  13. package/core/feature/analytic-event/use.analytic.auto.tsx +114 -0
  14. package/core/feature/article/hook/use.article.tsx +33 -0
  15. package/core/feature/attribute/hook/use.attribute.tsx +24 -0
  16. package/core/feature/auth/hook/use.auth.tsx +141 -0
  17. package/core/feature/auth/modal/modal.auth.tsx +80 -0
  18. package/core/feature/auth/view/view.login.tsx +199 -0
  19. package/core/feature/auth/view/view.register.tsx +333 -0
  20. package/core/feature/bank-account/hook/use.bank.account.tsx +47 -0
  21. package/core/feature/brand/hook/use.brand.tsx +24 -0
  22. package/core/feature/cart/component/cart.order.summary.tsx +89 -0
  23. package/core/feature/cart/component/cart.promo.code.section.tsx +208 -0
  24. package/core/feature/cart/hook/use.cart.tsx +267 -0
  25. package/core/feature/cart/util/basket-pay.response.ts +67 -0
  26. package/core/feature/cart/util/cart-optimistic.ts +425 -0
  27. package/core/feature/cart/util/garanti-payment.ts +27 -0
  28. package/core/feature/collection/hook/use.collection.tsx +32 -0
  29. package/core/feature/delivery-method/hook/use.delivery.method.tsx +40 -0
  30. package/core/feature/delivery-method/util/checkout.delivery.method.ts +11 -0
  31. package/core/feature/faq/hook/use.faq.tsx +23 -0
  32. package/core/feature/favorite/hook/use.favorite.tsx +48 -0
  33. package/core/feature/form-submit/form/form.contact.tsx +118 -0
  34. package/core/feature/form-submit/hook/use.form.submit.tsx +16 -0
  35. package/core/feature/invoice/hook/use.invoice.tsx +51 -0
  36. package/core/feature/newsletter/hook/use.newsletter.tsx +124 -0
  37. package/core/feature/newsletter/modal/modal.newsletter.product.tsx +163 -0
  38. package/core/feature/order/hook/use.order.tsx +31 -0
  39. package/core/feature/payment-method/checkout.payment.options.ts +117 -0
  40. package/core/feature/payment-method/hook/use.payment.method.tsx +44 -0
  41. package/core/feature/product/hook/use.product.tsx +122 -0
  42. package/core/feature/profile/hook/use.profile.tsx +126 -0
  43. package/core/feature/promo-code/hook/use.promo.code.tsx +27 -0
  44. package/core/interface/basket.interface.ts +360 -0
  45. package/core/interface/bootstrap.interface.ts +39 -0
  46. package/core/interface/context.interface.ts +9 -0
  47. package/core/interface/inventory.interface.ts +88 -0
  48. package/core/interface/nexine.interface.ts +4 -0
  49. package/core/interface/prisma.interface.ts +8844 -0
  50. package/core/interface/product.interface.ts +111 -0
  51. package/core/raxon.context.tsx +256 -0
  52. package/core/schema/checkout.schema.ts +103 -0
  53. package/core/util/basket.item.display.ts +19 -0
  54. package/core/util/category.nav.ts +46 -0
  55. package/core/util/client-ip.ts +35 -0
  56. package/core/util/collection.util.ts +433 -0
  57. package/core/util/fetch.bootstrap.ts +21 -0
  58. package/core/util/garanti-payment.ts +5 -0
  59. package/core/util/nexine.axios.tsx +104 -0
  60. package/core/util/no-cache.ts +6 -0
  61. package/core/util/util.ts +191 -0
  62. package/core/view/view.checkout.tsx +1964 -0
  63. package/dist/core/view/view.checkout.js +2 -2
  64. package/dist/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +12 -3
  66. package/tailwind.css +11 -0
@@ -0,0 +1,42 @@
1
+ export function escapeRegExp(value: string) {
2
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
3
+ }
4
+
5
+ export function buildProductPathPattern(productPathPrefix: string) {
6
+ const prefix = productPathPrefix.replace(/\/$/, '');
7
+ return new RegExp(`${escapeRegExp(prefix)}/([^/?#]+)`);
8
+ }
9
+
10
+ export function extractProductIdFromHref(href: string, productPathPrefix: string) {
11
+ if (!href) return null;
12
+
13
+ try {
14
+ const url = href.startsWith('http') ? new URL(href) : new URL(href, window.location.origin);
15
+ const match = url.pathname.match(buildProductPathPattern(productPathPrefix));
16
+ return match?.[1] ?? null;
17
+ } catch {
18
+ const match = href.match(buildProductPathPattern(productPathPrefix));
19
+ return match?.[1] ?? null;
20
+ }
21
+ }
22
+
23
+ export function readProductFromElement(element: Element, productPathPrefix: string) {
24
+ const trackedElement = element.closest('[data-raxon-product-id]');
25
+ if (trackedElement) {
26
+ const productId = trackedElement.getAttribute('data-raxon-product-id');
27
+ if (!productId) return null;
28
+
29
+ return {
30
+ productId,
31
+ variantId: trackedElement.getAttribute('data-raxon-variant-id'),
32
+ };
33
+ }
34
+
35
+ const link = element.closest('a[href]');
36
+ if (!link) return null;
37
+
38
+ const productId = extractProductIdFromHref(link.getAttribute('href') || '', productPathPrefix);
39
+ if (!productId) return null;
40
+
41
+ return { productId, variantId: null };
42
+ }
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import { usePathname } from 'next/navigation';
5
+ import { buildProductPathPattern, extractProductIdFromHref, readProductFromElement } from './analytic.event.util';
6
+
7
+ interface ProductAnalyticEvent {
8
+ productId: string;
9
+ variantId?: string | null;
10
+ }
11
+
12
+ interface UseAnalyticAutoTrackOptions {
13
+ enabled: boolean;
14
+ productPathPrefix: string;
15
+ trackProductView: (event: ProductAnalyticEvent) => void;
16
+ trackProductClick: (event: ProductAnalyticEvent) => void;
17
+ }
18
+
19
+ export function useAnalyticAutoTrack({
20
+ enabled,
21
+ productPathPrefix,
22
+ trackProductView,
23
+ trackProductClick,
24
+ }: UseAnalyticAutoTrackOptions) {
25
+ const pathname = usePathname();
26
+
27
+ useEffect(() => {
28
+ if (!enabled) return;
29
+
30
+ const productId = pathname.match(buildProductPathPattern(productPathPrefix))?.[1];
31
+ if (productId) {
32
+ trackProductView({ productId });
33
+ }
34
+ }, [enabled, pathname, productPathPrefix, trackProductView]);
35
+
36
+ useEffect(() => {
37
+ if (!enabled || typeof window === 'undefined') return;
38
+
39
+ const viewedElements = new WeakSet<Element>();
40
+
41
+ const trackVisibleElement = (element: Element) => {
42
+ if (viewedElements.has(element)) return;
43
+
44
+ const productId = element.getAttribute('data-raxon-product-id');
45
+ if (productId) {
46
+ viewedElements.add(element);
47
+ trackProductView({
48
+ productId,
49
+ variantId: element.getAttribute('data-raxon-variant-id'),
50
+ });
51
+ return;
52
+ }
53
+
54
+ if (element instanceof HTMLAnchorElement) {
55
+ const hrefProductId = extractProductIdFromHref(element.href, productPathPrefix);
56
+ if (hrefProductId) {
57
+ viewedElements.add(element);
58
+ trackProductView({ productId: hrefProductId });
59
+ }
60
+ }
61
+ };
62
+
63
+ const viewObserver = new IntersectionObserver(
64
+ (entries) => {
65
+ entries.forEach((entry) => {
66
+ if (entry.isIntersecting) {
67
+ trackVisibleElement(entry.target);
68
+ }
69
+ });
70
+ },
71
+ { threshold: 0.5 }
72
+ );
73
+
74
+ const observeCandidates = () => {
75
+ document
76
+ .querySelectorAll('[data-raxon-product-id], a[href]')
77
+ .forEach((element) => {
78
+ if (element.hasAttribute('data-raxon-product-id')) {
79
+ viewObserver.observe(element);
80
+ return;
81
+ }
82
+
83
+ if (element instanceof HTMLAnchorElement) {
84
+ const productId = extractProductIdFromHref(element.href, productPathPrefix);
85
+ if (productId) {
86
+ viewObserver.observe(element);
87
+ }
88
+ }
89
+ });
90
+ };
91
+
92
+ const onClick = (event: MouseEvent) => {
93
+ const target = event.target;
94
+ if (!(target instanceof Element)) return;
95
+
96
+ const productEvent = readProductFromElement(target, productPathPrefix);
97
+ if (productEvent) {
98
+ trackProductClick(productEvent);
99
+ }
100
+ };
101
+
102
+ const mutationObserver = new MutationObserver(observeCandidates);
103
+
104
+ document.addEventListener('click', onClick, true);
105
+ mutationObserver.observe(document.body, { childList: true, subtree: true });
106
+ observeCandidates();
107
+
108
+ return () => {
109
+ document.removeEventListener('click', onClick, true);
110
+ viewObserver.disconnect();
111
+ mutationObserver.disconnect();
112
+ };
113
+ }, [enabled, productPathPrefix, trackProductClick, trackProductView]);
114
+ }
@@ -0,0 +1,33 @@
1
+ import { nexineAxios } from "@/core/util/nexine.axios";
2
+ import { IData } from "@/core/interface/nexine.interface";
3
+ import { Article } from "@/core/interface/prisma.interface";
4
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
+
6
+ export const useArticle = () => {
7
+
8
+ const queryClient = useQueryClient();
9
+
10
+ return {
11
+ fetch : (params?:any) => {
12
+ return useQuery({
13
+ queryKey: ['web','article',params],
14
+ queryFn: async () => {
15
+ var response = await nexineAxios.get<IData<Article>>('/customer/article',{
16
+ params
17
+ })
18
+ return response.data;
19
+ },
20
+ })
21
+ },
22
+ detail : (id:string) => {
23
+ return useQuery({
24
+ queryKey: ['web','article',id],
25
+ queryFn: async () => {
26
+ var response = await nexineAxios.get<Article>('/customer/article/'+id)
27
+ return response.data;
28
+ },
29
+ enabled: Boolean(id),
30
+ })
31
+ }
32
+ }
33
+ };
@@ -0,0 +1,24 @@
1
+ import { nexineAxios } from '@/core/util/nexine.axios';
2
+ import { IData } from '@/core/interface/nexine.interface';
3
+ import { Attribute, Brand, Category, Faq } from '@/core/interface/prisma.interface';
4
+ import { useQuery } from '@tanstack/react-query';
5
+
6
+ export const useAttribute = () => {
7
+ return {
8
+ fetch: (params: { categoryId?: string; enabled?: boolean, productId?: string }) => {
9
+ return useQuery({
10
+ queryKey: ['attribute', 'category', params.categoryId, params.productId],
11
+ enabled: (params.enabled ?? true) ,
12
+ queryFn: async () => {
13
+ var response = await nexineAxios.get<IData<Attribute>>('/customer/attribute', {
14
+ params: {
15
+ categoryId: params.categoryId,
16
+ productId: params.productId,
17
+ },
18
+ });
19
+ return response.data;
20
+ },
21
+ });
22
+ },
23
+ };
24
+ };
@@ -0,0 +1,141 @@
1
+ import { nexineAxios } from "@/core/util/nexine.axios";
2
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3
+ import { useEffect, useState } from "react";
4
+ import toast from "react-hot-toast";
5
+
6
+ // Auth event'leri için custom eventler
7
+ export const LOGOUT_EVENT = 'intermarkt-logout';
8
+ export const LOGIN_SUCCESS_EVENT = 'intermarkt-login-success';
9
+
10
+ export const useAuth = () => {
11
+
12
+ const queryClient = useQueryClient();
13
+
14
+
15
+
16
+ return {
17
+
18
+ profile : ({isEnabled = false}) => {
19
+ return useQuery({
20
+ queryKey: ["profile"],
21
+ enabled: isEnabled,
22
+ queryFn: async () => {
23
+ var response = await nexineAxios.get('/customer/profile')
24
+ return response.data.data
25
+ }
26
+ })
27
+ },
28
+ token : () => {
29
+ return useMutation({
30
+ mutationFn: async () => {
31
+ var response = await nexineAxios.get('/auth/token')
32
+ return response.data.data
33
+ }
34
+ })
35
+ },
36
+
37
+ loginEmail : () => {
38
+ return useMutation({
39
+ mutationFn: (data: any) => {
40
+ return nexineAxios.post('/auth/login/email', data)
41
+ },
42
+ onSuccess: (response: any) => {
43
+ localStorage.setItem("koksal-token", response.data.token);
44
+ queryClient.invalidateQueries({ queryKey: ["user"] });
45
+ queryClient.invalidateQueries({ queryKey: ["profile"] });
46
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
47
+
48
+ // Login success event'ini tetikle
49
+ if (typeof window !== 'undefined') {
50
+ window.dispatchEvent(new Event(LOGIN_SUCCESS_EVENT));
51
+ window.dispatchEvent(new Event('koksal-token-changed'));
52
+ }
53
+ },
54
+ })
55
+ },
56
+ loginGuest : () => {
57
+ return useMutation({
58
+ mutationFn: () => {
59
+ return nexineAxios.post('/auth/login/guest')
60
+ },
61
+ onSuccess: (response: any) => {
62
+ localStorage.setItem("koksal-token", response.data.token);
63
+ queryClient.invalidateQueries({ queryKey: ["user"] });
64
+ queryClient.invalidateQueries({ queryKey: ["profile"] });
65
+ queryClient.invalidateQueries({ queryKey: ["cart"] });
66
+
67
+
68
+ // Login success event'ini tetikle
69
+ if (typeof window !== 'undefined') {
70
+ window.dispatchEvent(new Event(LOGIN_SUCCESS_EVENT));
71
+ window.dispatchEvent(new Event('koksal-token-changed'));
72
+ }
73
+ },
74
+ })
75
+ },
76
+ loginSocial : () => {
77
+ return useMutation({
78
+ mutationFn: async (data: { platform: string, returnUrl?: string }) => {
79
+ var response = await nexineAxios.get(`/auth/login/social`, { params : { platform : data.platform,returnUrl : data.returnUrl } })
80
+ return response.data.url
81
+ },
82
+ })
83
+ },
84
+ register : () => {
85
+ return useMutation({
86
+ mutationFn: (data: any) => {
87
+ return nexineAxios.post('/auth/register', data)
88
+ },
89
+ onSuccess: () => {
90
+
91
+ }
92
+ })
93
+ },
94
+ // Şifremi unuttum - E-posta gönderme
95
+ codeSend: () => {
96
+ return useMutation({
97
+ mutationFn: (data: { email: string }) => {
98
+ return nexineAxios.post('/auth/code-send', data)
99
+ },
100
+ onSuccess: () => {
101
+
102
+ }
103
+ })
104
+ },
105
+ // Doğrulama kodu kontrol etme
106
+ verifyCode: () => {
107
+ return useMutation({
108
+ mutationFn: (data: { email: string, code: string }) => {
109
+ return nexineAxios.post('/auth/code-verify', data)
110
+ },
111
+ onSuccess: () => {
112
+
113
+
114
+ }
115
+ })
116
+ },
117
+ // Şifre sıfırlama
118
+ resetPassword: () => {
119
+ return useMutation({
120
+ mutationFn: (data: { email: string, code: string, password: string }) => {
121
+ return nexineAxios.post('/auth/reset-password', data)
122
+ },
123
+ onSuccess: () => {
124
+
125
+ }
126
+ })
127
+ },
128
+ logout: () => {
129
+ localStorage.removeItem("koksal-token");
130
+ queryClient.invalidateQueries({ queryKey: ["user"] });
131
+ queryClient.invalidateQueries({ queryKey: ["profile"] });
132
+
133
+ // Logout event'ini tetikle
134
+ if (typeof window !== 'undefined') {
135
+ window.dispatchEvent(new Event(LOGOUT_EVENT));
136
+ window.dispatchEvent(new Event('koksal-token-changed'));
137
+ window.location.reload();
138
+ }
139
+ }
140
+ }
141
+ };
@@ -0,0 +1,80 @@
1
+ import React, { useImperativeHandle, useMemo, useState } from 'react';
2
+ import { Modal } from 'rizzui';
3
+ import { X } from 'lucide-react';
4
+ import ViewLogin from '@/core/feature/auth/view/view.login';
5
+ import ViewRegister from '@/core/feature/auth/view/view.register';
6
+ import { useRaxon } from '@/core/raxon.context';
7
+ import { GeneralImage } from '@/core/component/general.image';
8
+ import { usePathname } from 'next/navigation';
9
+ import queryString from 'query-string';
10
+
11
+ export interface ModalAuthProps {}
12
+ export interface ModalAuthRef {
13
+ open: (defaultTab?: 'login' | 'register', queries?: any) => void;
14
+ close: () => void;
15
+ }
16
+
17
+ export const ModalAuth = React.forwardRef<ModalAuthRef, ModalAuthProps>((props, ref) => {
18
+ const [isOpen, setIsOpen] = useState(false);
19
+ const [activeTab, setActiveTab] = useState<'login' | 'register'>('login');
20
+ const [queries, setQueries] = useState<any>({});
21
+ const { branch } = useRaxon();
22
+
23
+ const pathname = usePathname();
24
+
25
+ const returnUrl = useMemo(() => {
26
+
27
+
28
+ return queryString.stringify(queries);
29
+ }, [pathname, queries]);
30
+
31
+ useImperativeHandle(ref, () => ({
32
+ open: (defaultTab = 'login', queries?: any) => {
33
+ setActiveTab(defaultTab);
34
+ setQueries(queries ?? {});
35
+ setIsOpen(true);
36
+ },
37
+ close: () => {
38
+ setIsOpen(false);
39
+ },
40
+ }));
41
+
42
+ const handleClose = () => {
43
+ setIsOpen(false);
44
+ };
45
+
46
+ return (
47
+ <Modal isOpen={isOpen} onClose={handleClose} customSize={400} overlayClassName="backdrop-blur-sm">
48
+ <div className="relative bg-white rounded-lg shadow-2xl">
49
+ {/* Close Button */}
50
+ <button onClick={handleClose} className="absolute top-4 right-4 p-2 text-gray-400 hover:text-gray-600 transition-colors z-10">
51
+ <X className="w-5 h-5" />
52
+ </button>
53
+
54
+ {/* Header */}
55
+ <div className="text-center pt-8 pb-6 px-8">
56
+ <div className="flex justify-center mb-4">
57
+ <GeneralImage quality={85} src={branch?.logoMedia?.relativePath ? `${process.env.NEXT_PUBLIC_STORAGE_URL}/${branch?.logoMedia?.relativePath}` : ''} alt="logo" width={180} height={40} className="h-8 w-auto object-contain" />
58
+ </div>
59
+ <p className="text-black/80 text-sm font-light uppercase tracking-wider">{activeTab === 'login' ? 'Hesabınıza giriş yapın' : 'Yeni hesap oluşturun'}</p>
60
+ </div>
61
+
62
+ {/* Tab Navigation */}
63
+ <div className="flex border-b border-black/10 mx-8">
64
+ <button onClick={() => setActiveTab('login')} className={`flex-1 py-3 text-sm font-black uppercase tracking-wider transition-colors border-b-2 ${activeTab === 'login' ? 'border-black text-black' : 'border-transparent text-black/80 hover:text-black'}`}>
65
+ Giriş Yap
66
+ </button>
67
+ <button onClick={() => setActiveTab('register')} className={`flex-1 py-3 text-sm font-black uppercase tracking-wider transition-colors border-b-2 ${activeTab === 'register' ? 'border-black text-black' : 'border-transparent text-black/80 hover:text-black'}`}>
68
+ Kayıt Ol
69
+ </button>
70
+ </div>
71
+
72
+ {/* Content */}
73
+ <div className="p-8">
74
+ {activeTab === 'login' && <ViewLogin onClose={handleClose} returnUrl={returnUrl} />}
75
+ {activeTab === 'register' && <ViewRegister onClose={handleClose} onSwitchToLogin={() => setActiveTab('login')} />}
76
+ </div>
77
+ </div>
78
+ </Modal>
79
+ );
80
+ });
@@ -0,0 +1,199 @@
1
+ 'use client';
2
+ import React, { useState } from 'react';
3
+ import { Eye, EyeOff, Lock, Mail, ArrowRight } from 'lucide-react';
4
+ import Link from 'next/link';
5
+ import { useAuth } from '@/core/feature/auth/hook/use.auth';
6
+ import { useForm } from 'react-hook-form';
7
+ import { useRouter } from 'next/navigation';
8
+ import toast from 'react-hot-toast';
9
+
10
+ interface ViewLoginProps {
11
+ onClose?: () => void;
12
+ returnUrl?: string;
13
+ }
14
+
15
+ export default function ViewLogin({ onClose, returnUrl }: ViewLoginProps) {
16
+ const [showPassword, setShowPassword] = useState(false);
17
+ const { mutate: login } = useAuth().loginEmail();
18
+ const { mutate: loginGuest } = useAuth().loginGuest();
19
+ const { mutate: loginSocial } = useAuth().loginSocial();
20
+ const form = useForm();
21
+
22
+
23
+ const handleSubmitGuest = (e: React.FormEvent) => {
24
+ e.preventDefault();
25
+ loginGuest({} as any, {
26
+ onSuccess: () => {
27
+ onClose?.();
28
+ },
29
+ });
30
+ };
31
+
32
+ const handleLoginSocial = (provider: string) => {
33
+ loginSocial({ platform: provider, returnUrl: returnUrl }, {
34
+ onSuccess: (url) => {
35
+ if (typeof window !== 'undefined') {
36
+ window.location.href = url;
37
+ }
38
+
39
+
40
+
41
+ },
42
+ });
43
+ };
44
+
45
+ const handleSubmit = (e: React.FormEvent) => {
46
+ e.preventDefault();
47
+ form.handleSubmit(data => {
48
+ login(data, {
49
+ onSuccess: () => {
50
+
51
+ onClose?.();
52
+
53
+ },
54
+ onError: (error: any) => {
55
+ toast.error(error?.response?.data?.info?.title || 'Giriş başarısız');
56
+ }
57
+ });
58
+ })();
59
+ };
60
+
61
+ return (
62
+ <form onSubmit={handleSubmit} className="space-y-6">
63
+ {/* Email Field */}
64
+ <div>
65
+ <input
66
+ type="email"
67
+ {...form.register('email')}
68
+ placeholder="E-posta adresinizi girin *"
69
+ className="w-full px-0 py-5 text-lg font-medium border-0 border-b-2 border-gray-300 bg-transparent focus:outline-none focus:border-black transition-colors placeholder-gray-500"
70
+ required
71
+ />
72
+ </div>
73
+
74
+ {/* Password Field */}
75
+ <div className="relative">
76
+ <input
77
+ type={showPassword ? 'text' : 'password'}
78
+ {...form.register('password')}
79
+ placeholder="Şifre *"
80
+ className="w-full px-0 py-5 pr-10 text-lg font-medium border-0 border-b-2 border-gray-300 bg-transparent focus:outline-none focus:border-black transition-colors placeholder-gray-500"
81
+ required
82
+ />
83
+ <button
84
+ type="button"
85
+ className="absolute right-0 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600"
86
+ onClick={() => setShowPassword(!showPassword)}
87
+ >
88
+ {showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
89
+ </button>
90
+ </div>
91
+
92
+ {/* Remember Me & Forgot Password */}
93
+ <div className="flex items-center justify-between py-4">
94
+ <div className="flex items-center">
95
+ <input
96
+ id="remember"
97
+ type="checkbox"
98
+ {...form.register('rememberMe')}
99
+ className="w-4 h-4 text-black border-gray-300 rounded focus:ring-0"
100
+ />
101
+ <label htmlFor="remember" className="ml-2 text-base font-medium text-gray-700">Beni hatırla</label>
102
+ </div>
103
+ <button
104
+ type="button"
105
+ onClick={onClose}
106
+ className="text-base font-medium text-gray-600 hover:text-black transition-colors"
107
+ >
108
+ Şifrenizi mi unuttunuz?
109
+ </button>
110
+ </div>
111
+
112
+
113
+
114
+ {/* Social Login */}
115
+ <div className="py-6">
116
+ <div className="flex items-center mb-6">
117
+ <div className="flex-1 border-t border-gray-200"></div>
118
+ <span className="px-4 text-base font-medium text-gray-500">Veya sosyal hesapla devam edin</span>
119
+ <div className="flex-1 border-t border-gray-200"></div>
120
+ </div>
121
+
122
+ <div className="flex flex-col sm:grid sm:grid-cols-2 gap-4">
123
+ <button
124
+ type="button"
125
+ onClick={() => handleLoginSocial('google')}
126
+ className="py-4 text-base font-medium border border-gray-200 hover:border-black transition-colors flex items-center justify-center space-x-2"
127
+ >
128
+ <svg width="20" height="20" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
129
+ <g clipPath="url(#clip0_278_6045)">
130
+ <path d="M21.6696 9.08832L12.696 9.08789C12.2997 9.08789 11.9785 9.40904 11.9785 9.8053V12.672C11.9785 13.0681 12.2997 13.3894 12.6959 13.3894H17.7493C17.196 14.8254 16.1632 16.0281 14.8455 16.7922L17.0002 20.5223C20.4567 18.5233 22.5002 15.0158 22.5002 11.0894C22.5002 10.5303 22.459 10.1307 22.3766 9.68064C22.314 9.33874 22.0171 9.08832 21.6696 9.08832Z" fill="#167EE6"/>
131
+ <path d="M11.4999 17.6964C9.02689 17.6964 6.86797 16.3452 5.70846 14.3457L1.97852 16.4956C3.87666 19.7854 7.4325 22.0007 11.4999 22.0007C13.4953 22.0007 15.378 21.4635 16.9999 20.5272V20.5221L14.8452 16.792C13.8595 17.3637 12.719 17.6964 11.4999 17.6964Z" fill="#12B347"/>
132
+ <path d="M17 20.5262V20.5211L14.8452 16.791C13.8596 17.3626 12.7192 17.6954 11.5 17.6954V21.9997C13.4953 21.9997 15.3782 21.4625 17 20.5262Z" fill="#0F993E"/>
133
+ <path d="M4.80435 11.0007C4.80435 9.78177 5.13702 8.64133 5.70854 7.65576L1.9786 5.50586C1.0372 7.12264 0.5 9.00029 0.5 11.0007C0.5 13.0012 1.0372 14.8788 1.9786 16.4956L5.70854 14.3457C5.13702 13.3602 4.80435 12.2197 4.80435 11.0007Z" fill="#FFD500"/>
134
+ <path d="M11.4999 4.30435C13.1126 4.30435 14.5939 4.87738 15.7509 5.83056C16.0363 6.06568 16.4512 6.04871 16.7127 5.78725L18.7438 3.75611C19.0405 3.45946 19.0193 2.97387 18.7024 2.69895C16.7639 1.0172 14.2416 0 11.4999 0C7.4325 0 3.87666 2.21534 1.97852 5.50511L5.70846 7.65501C6.86797 5.65555 9.02689 4.30435 11.4999 4.30435Z" fill="#FF4B26"/>
135
+ <path d="M15.751 5.83056C16.0364 6.06568 16.4513 6.04871 16.7128 5.78725L18.7439 3.75611C19.0405 3.45946 19.0194 2.97387 18.7025 2.69895C16.764 1.01716 14.2417 0 11.5 0V4.30435C13.1126 4.30435 14.594 4.87738 15.751 5.83056Z" fill="#D93F21"/>
136
+ </g>
137
+ </svg>
138
+ <span className="text-sm sm:text-base">Google ile giriş yap</span>
139
+ </button>
140
+
141
+ <button
142
+ type="button"
143
+ onClick={() => handleLoginSocial('facebook')}
144
+ className="py-4 text-base font-medium border border-gray-200 hover:border-black transition-colors flex items-center justify-center space-x-2"
145
+ >
146
+ <svg width="20" height="20" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
147
+ <g clipPath="url(#clip0_278_6055)">
148
+ <path d="M22.5 11C22.5 16.4905 18.4773 21.0414 13.2188 21.8664V14.1797H15.7818L16.2695 11H13.2188V8.93664C13.2188 8.06652 13.645 7.21875 15.0114 7.21875H16.3984V4.51172C16.3984 4.51172 15.1395 4.29688 13.9359 4.29688C11.4235 4.29688 9.78125 5.81969 9.78125 8.57656V11H6.98828V14.1797H9.78125V21.8664C4.52273 21.0414 0.5 16.4905 0.5 11C0.5 4.92508 5.42508 0 11.5 0C17.5749 0 22.5 4.92508 22.5 11Z" fill="#1877F2"/>
149
+ <path d="M15.7818 14.1797L16.2695 11H13.2188V8.9366C13.2188 8.0667 13.6449 7.21875 15.0114 7.21875H16.3984V4.51172C16.3984 4.51172 15.1396 4.29688 13.9361 4.29688C11.4235 4.29688 9.78125 5.81969 9.78125 8.57656V11H6.98828V14.1797H9.78125V21.8663C10.3413 21.9542 10.9153 22 11.5 22C12.0847 22 12.6587 21.9542 13.2188 21.8663V14.1797H15.7818Z" fill="white"/>
150
+ </g>
151
+ </svg>
152
+ <span className="text-sm sm:text-base">Facebook ile giriş yap</span>
153
+ </button>
154
+ </div>
155
+ </div>
156
+
157
+ {/* Misafir Login */}
158
+ <button
159
+ onClick={handleSubmitGuest}
160
+ type="button"
161
+ className="w-full py-4 text-base font-medium hover:text-black transition-colors"
162
+ >
163
+ Misafir Olarak Devam Et
164
+ </button>
165
+
166
+ {/* Login Button */}
167
+ <button
168
+ id="btnLogin"
169
+ type="submit"
170
+ className="w-full py-5 text-lg font-bold bg-black text-white hover:bg-gray-800 transition-colors"
171
+ >
172
+ Giriş Yap
173
+ </button>
174
+
175
+ {/* Terms */}
176
+ <div className="text-center">
177
+ <p className="text-sm font-medium text-gray-500 leading-relaxed">
178
+ Devam ederek{' '}
179
+ <button
180
+ type="button"
181
+ onClick={onClose}
182
+ className="hover:underline"
183
+ >
184
+ Kullanım Şartları
185
+ </button>{' '}
186
+ ve{' '}
187
+ <button
188
+ type="button"
189
+ onClick={onClose}
190
+ className="hover:underline"
191
+ >
192
+ Gizlilik Politikası
193
+ </button>
194
+ 'nı kabul etmiş olursunuz.
195
+ </p>
196
+ </div>
197
+ </form>
198
+ );
199
+ }