@raxonltd/raxon-core 1.1.7 → 1.1.13
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.
- package/core/component/general.image.tsx +86 -0
- package/core/context/cart.context.tsx +446 -0
- package/core/context/security.context.tsx +151 -0
- package/core/feature/address/api/places.api.ts +92 -0
- package/core/feature/address/form/address-search-input.tsx +125 -0
- package/core/feature/address/hook/use.addres.tsx +63 -0
- package/core/feature/address/hook/use.address-autocomplete.ts +116 -0
- package/core/feature/address/util/address.types.ts +38 -0
- package/core/feature/address/util/parse-google-place.ts +66 -0
- package/core/feature/analytic-event/analytic.event.api.ts +27 -0
- package/core/feature/analytic-event/analytic.event.context.tsx +180 -0
- package/core/feature/analytic-event/analytic.event.util.ts +42 -0
- package/core/feature/analytic-event/use.analytic.auto.tsx +114 -0
- package/core/feature/article/hook/use.article.tsx +33 -0
- package/core/feature/attribute/hook/use.attribute.tsx +24 -0
- package/core/feature/auth/hook/use.auth.tsx +141 -0
- package/core/feature/auth/modal/modal.auth.tsx +80 -0
- package/core/feature/auth/view/view.login.tsx +199 -0
- package/core/feature/auth/view/view.register.tsx +333 -0
- package/core/feature/bank-account/hook/use.bank.account.tsx +47 -0
- package/core/feature/brand/hook/use.brand.tsx +24 -0
- package/core/feature/cart/component/cart.order.summary.tsx +89 -0
- package/core/feature/cart/component/cart.promo.code.section.tsx +208 -0
- package/core/feature/cart/hook/use.cart.tsx +267 -0
- package/core/feature/cart/util/basket-pay.response.ts +67 -0
- package/core/feature/cart/util/cart-optimistic.ts +425 -0
- package/core/feature/cart/util/garanti-payment.ts +27 -0
- package/core/feature/collection/hook/use.collection.tsx +32 -0
- package/core/feature/delivery-method/hook/use.delivery.method.tsx +40 -0
- package/core/feature/delivery-method/util/checkout.delivery.method.ts +11 -0
- package/core/feature/faq/hook/use.faq.tsx +23 -0
- package/core/feature/favorite/hook/use.favorite.tsx +48 -0
- package/core/feature/form-submit/form/form.contact.tsx +118 -0
- package/core/feature/form-submit/hook/use.form.submit.tsx +16 -0
- package/core/feature/invoice/hook/use.invoice.tsx +51 -0
- package/core/feature/newsletter/hook/use.newsletter.tsx +124 -0
- package/core/feature/newsletter/modal/modal.newsletter.product.tsx +163 -0
- package/core/feature/order/hook/use.order.tsx +31 -0
- package/core/feature/payment-method/checkout.payment.options.ts +117 -0
- package/core/feature/payment-method/hook/use.payment.method.tsx +44 -0
- package/core/feature/product/hook/use.product.tsx +122 -0
- package/core/feature/profile/hook/use.profile.tsx +126 -0
- package/core/feature/promo-code/hook/use.promo.code.tsx +27 -0
- package/core/interface/basket.interface.ts +360 -0
- package/core/interface/bootstrap.interface.ts +39 -0
- package/core/interface/context.interface.ts +9 -0
- package/core/interface/inventory.interface.ts +88 -0
- package/core/interface/nexine.interface.ts +4 -0
- package/core/interface/prisma.interface.ts +8844 -0
- package/core/interface/product.interface.ts +111 -0
- package/core/raxon.context.tsx +256 -0
- package/core/schema/checkout.schema.ts +103 -0
- package/core/server/places.proxy.ts +35 -0
- package/core/server/raxon.bootstrap.route.ts +39 -0
- package/core/server/raxon.server.ts +80 -0
- package/core/util/basket.item.display.ts +19 -0
- package/core/util/category.nav.ts +46 -0
- package/core/util/client-ip.ts +35 -0
- package/core/util/collection.util.ts +433 -0
- package/core/util/fetch.bootstrap.ts +21 -0
- package/core/util/garanti-payment.ts +5 -0
- package/core/util/nexine.axios.tsx +104 -0
- package/core/util/no-cache.ts +6 -0
- package/core/util/util.ts +191 -0
- package/core/view/view.checkout.tsx +1964 -0
- package/dist/core/feature/address/api/places.api.d.ts.map +1 -1
- package/dist/core/feature/address/api/places.api.js +18 -4
- package/dist/core/server/places.proxy.d.ts +10 -0
- package/dist/core/server/places.proxy.d.ts.map +1 -0
- package/dist/core/server/places.proxy.js +24 -0
- package/dist/core/server/raxon.bootstrap.route.d.ts +7 -0
- package/dist/core/server/raxon.bootstrap.route.d.ts.map +1 -0
- package/dist/core/server/raxon.bootstrap.route.js +27 -0
- package/dist/core/server/raxon.server.d.ts +24 -0
- package/dist/core/server/raxon.server.d.ts.map +1 -0
- package/dist/core/server/raxon.server.js +59 -0
- package/dist/core/view/view.checkout.js +2 -2
- package/dist/middleware.d.ts +6 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +5 -0
- package/dist/server-bootstrap.d.ts +2 -0
- package/dist/server-bootstrap.d.ts.map +1 -0
- package/dist/server-bootstrap.js +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -3
- package/tailwind.css +11 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Eye, EyeOff, Lock, Mail, User, Phone, ArrowRight } from 'lucide-react';
|
|
4
|
+
import { useAuth } from '@/core/feature/auth/hook/use.auth';
|
|
5
|
+
import { useRouter } from 'next/navigation';
|
|
6
|
+
import { useForm } from 'react-hook-form';
|
|
7
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import toast from 'react-hot-toast';
|
|
10
|
+
|
|
11
|
+
// Türk telefon numarası regex'i - 0 ile başlayan 11 haneli veya 5 ile başlayan 10 haneli
|
|
12
|
+
const turkishPhoneRegex = /^(\+90[5-9][0-9]{9}|0[5-9][0-9]{9}|[5-9][0-9]{8})$/;
|
|
13
|
+
|
|
14
|
+
// Zod validation şeması
|
|
15
|
+
const registerSchema = z.object({
|
|
16
|
+
firstName: z.string()
|
|
17
|
+
.min(1, 'Ad alanı zorunludur')
|
|
18
|
+
.min(2, 'Ad en az 2 karakter olmalıdır')
|
|
19
|
+
.max(50, 'Ad en fazla 50 karakter olabilir')
|
|
20
|
+
.regex(/^[a-zA-ZçğıöşüÇĞIİÖŞÜ\s]+$/, 'Ad sadece harf içerebilir'),
|
|
21
|
+
|
|
22
|
+
lastName: z.string()
|
|
23
|
+
.min(1, 'Soyad alanı zorunludur')
|
|
24
|
+
.min(2, 'Soyad en az 2 karakter olmalıdır')
|
|
25
|
+
.max(50, 'Soyad en fazla 50 karakter olabilir')
|
|
26
|
+
.regex(/^[a-zA-ZçğıöşüÇĞIİÖŞÜ\s]+$/, 'Soyad sadece harf içerebilir'),
|
|
27
|
+
|
|
28
|
+
email: z.string()
|
|
29
|
+
.min(1, 'E-posta alanı zorunludur')
|
|
30
|
+
.email('Geçerli bir e-posta adresi giriniz')
|
|
31
|
+
.max(100, 'E-posta adresi çok uzun'),
|
|
32
|
+
|
|
33
|
+
phone: z.string()
|
|
34
|
+
.min(1, 'Telefon numarası zorunludur')
|
|
35
|
+
.regex(turkishPhoneRegex, 'Geçerli bir Türk telefon numarası giriniz (örn: +905551234567)')
|
|
36
|
+
.transform((val) => val.replace(/\s/g, '')), // Boşlukları temizle
|
|
37
|
+
|
|
38
|
+
password: z.string()
|
|
39
|
+
.min(1, 'Şifre alanı zorunludur')
|
|
40
|
+
.min(6, 'Şifre en az 6 karakter olmalıdır')
|
|
41
|
+
.max(100, 'Şifre çok uzun'),
|
|
42
|
+
|
|
43
|
+
confirmPassword: z.string()
|
|
44
|
+
.min(1, 'Şifre tekrarı zorunludur'),
|
|
45
|
+
|
|
46
|
+
acceptTerms: z.boolean()
|
|
47
|
+
.refine((val) => val === true, 'Kullanım şartlarını kabul etmelisiniz'),
|
|
48
|
+
|
|
49
|
+
acceptMarketing: z.boolean().optional()
|
|
50
|
+
}).refine((data) => data.password === data.confirmPassword, {
|
|
51
|
+
message: 'Şifreler eşleşmiyor',
|
|
52
|
+
path: ['confirmPassword']
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
type RegisterFormData = z.infer<typeof registerSchema>;
|
|
56
|
+
|
|
57
|
+
interface ViewRegisterProps {
|
|
58
|
+
onClose?: () => void;
|
|
59
|
+
onSwitchToLogin?: () => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default function ViewRegister({ onClose, onSwitchToLogin }: ViewRegisterProps) {
|
|
63
|
+
const router = useRouter();
|
|
64
|
+
const { register: authRegister } = useAuth();
|
|
65
|
+
const registerMutation = authRegister();
|
|
66
|
+
|
|
67
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
68
|
+
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
register,
|
|
72
|
+
handleSubmit,
|
|
73
|
+
formState: { errors, isSubmitting },
|
|
74
|
+
} = useForm<RegisterFormData>({
|
|
75
|
+
resolver: zodResolver(registerSchema),
|
|
76
|
+
defaultValues: {
|
|
77
|
+
firstName: '',
|
|
78
|
+
lastName: '',
|
|
79
|
+
email: '',
|
|
80
|
+
phone: '',
|
|
81
|
+
password: '',
|
|
82
|
+
confirmPassword: '',
|
|
83
|
+
acceptTerms: false,
|
|
84
|
+
acceptMarketing: false
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const onSubmit = async (data: RegisterFormData) => {
|
|
89
|
+
registerMutation.mutate({
|
|
90
|
+
firstName: data.firstName,
|
|
91
|
+
lastName: data.lastName,
|
|
92
|
+
email: data.email,
|
|
93
|
+
phone: data.phone,
|
|
94
|
+
password: data.password,
|
|
95
|
+
acceptMarketing: data.acceptMarketing || false
|
|
96
|
+
}, {
|
|
97
|
+
onSuccess: () => {
|
|
98
|
+
|
|
99
|
+
onSwitchToLogin?.();
|
|
100
|
+
},
|
|
101
|
+
onError: (e: any) => {
|
|
102
|
+
toast.error(e.response?.data?.info?.title || 'Kayıt başarısız');
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
|
109
|
+
{/* Name Fields */}
|
|
110
|
+
<div className="grid grid-cols-2 gap-4">
|
|
111
|
+
<div className="relative">
|
|
112
|
+
<div className="relative">
|
|
113
|
+
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
114
|
+
<input
|
|
115
|
+
type="text"
|
|
116
|
+
{...register('firstName')}
|
|
117
|
+
placeholder="Adınız"
|
|
118
|
+
className={`w-full pl-10 pr-4 py-3 text-sm border rounded-sm focus:outline-none transition-colors ${
|
|
119
|
+
errors.firstName ? 'border-red-500' : 'border-gray-200 focus:border-black'
|
|
120
|
+
}`}
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
{errors.firstName && (
|
|
124
|
+
<p className="mt-1 text-xs text-red-500">{errors.firstName.message}</p>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="relative">
|
|
129
|
+
<div className="relative">
|
|
130
|
+
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
131
|
+
<input
|
|
132
|
+
type="text"
|
|
133
|
+
{...register('lastName')}
|
|
134
|
+
placeholder="Soyadınız"
|
|
135
|
+
className={`w-full pl-10 pr-4 py-3 text-sm border rounded-sm focus:outline-none transition-colors ${
|
|
136
|
+
errors.lastName ? 'border-red-500' : 'border-gray-200 focus:border-black'
|
|
137
|
+
}`}
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
{errors.lastName && (
|
|
141
|
+
<p className="mt-1 text-xs text-red-500">{errors.lastName.message}</p>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
{/* Email Field */}
|
|
147
|
+
<div className="relative">
|
|
148
|
+
<div className="relative">
|
|
149
|
+
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
150
|
+
<input
|
|
151
|
+
type="email"
|
|
152
|
+
{...register('email')}
|
|
153
|
+
placeholder="E-posta adresiniz"
|
|
154
|
+
className={`w-full pl-10 pr-4 py-3 text-sm border rounded-sm focus:outline-none transition-colors ${
|
|
155
|
+
errors.email ? 'border-red-500' : 'border-gray-200 focus:border-black'
|
|
156
|
+
}`}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
{errors.email && (
|
|
160
|
+
<p className="mt-1 text-xs text-red-500">{errors.email.message}</p>
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Phone Field */}
|
|
165
|
+
<div className="relative">
|
|
166
|
+
<div className="relative">
|
|
167
|
+
<Phone className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
168
|
+
<input
|
|
169
|
+
type="tel"
|
|
170
|
+
{...register('phone')}
|
|
171
|
+
placeholder="Telefon numaranız (örn: 05551234567)"
|
|
172
|
+
className={`w-full pl-10 pr-4 py-3 text-sm border rounded-sm focus:outline-none transition-colors ${
|
|
173
|
+
errors.phone ? 'border-red-500' : 'border-gray-200 focus:border-black'
|
|
174
|
+
}`}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
{errors.phone && (
|
|
178
|
+
<p className="mt-1 text-xs text-red-500">{errors.phone.message}</p>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
{/* Password Field */}
|
|
183
|
+
<div className="relative">
|
|
184
|
+
<div className="relative">
|
|
185
|
+
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
186
|
+
<input
|
|
187
|
+
type={showPassword ? "text" : "password"}
|
|
188
|
+
{...register('password')}
|
|
189
|
+
placeholder="Şifreniz (en az 6 karakter, büyük/küçük harf ve rakam)"
|
|
190
|
+
className={`w-full pl-10 pr-12 py-3 text-sm border rounded-sm focus:outline-none transition-colors ${
|
|
191
|
+
errors.password ? 'border-red-500' : 'border-gray-200 focus:border-black'
|
|
192
|
+
}`}
|
|
193
|
+
/>
|
|
194
|
+
<button
|
|
195
|
+
type="button"
|
|
196
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
197
|
+
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
|
|
198
|
+
>
|
|
199
|
+
{showPassword ? (
|
|
200
|
+
<EyeOff className="w-4 h-4" />
|
|
201
|
+
) : (
|
|
202
|
+
<Eye className="w-4 h-4" />
|
|
203
|
+
)}
|
|
204
|
+
</button>
|
|
205
|
+
</div>
|
|
206
|
+
{errors.password && (
|
|
207
|
+
<p className="mt-1 text-xs text-red-500">{errors.password.message}</p>
|
|
208
|
+
)}
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
{/* Confirm Password Field */}
|
|
212
|
+
<div className="relative">
|
|
213
|
+
<div className="relative">
|
|
214
|
+
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
|
|
215
|
+
<input
|
|
216
|
+
type={showConfirmPassword ? "text" : "password"}
|
|
217
|
+
{...register('confirmPassword')}
|
|
218
|
+
placeholder="Şifrenizi tekrar giriniz"
|
|
219
|
+
className={`w-full pl-10 pr-12 py-3 text-sm border rounded-sm focus:outline-none transition-colors ${
|
|
220
|
+
errors.confirmPassword ? 'border-red-500' : 'border-gray-200 focus:border-black'
|
|
221
|
+
}`}
|
|
222
|
+
/>
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
|
226
|
+
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
|
|
227
|
+
>
|
|
228
|
+
{showConfirmPassword ? (
|
|
229
|
+
<EyeOff className="w-4 h-4" />
|
|
230
|
+
) : (
|
|
231
|
+
<Eye className="w-4 h-4" />
|
|
232
|
+
)}
|
|
233
|
+
</button>
|
|
234
|
+
</div>
|
|
235
|
+
{errors.confirmPassword && (
|
|
236
|
+
<p className="mt-1 text-xs text-red-500">{errors.confirmPassword.message}</p>
|
|
237
|
+
)}
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{/* Terms & Marketing Checkboxes */}
|
|
241
|
+
<div className="space-y-3">
|
|
242
|
+
<div className="flex items-start">
|
|
243
|
+
<input
|
|
244
|
+
type="checkbox"
|
|
245
|
+
{...register('acceptTerms')}
|
|
246
|
+
className="w-4 h-4 text-black border-gray-300 rounded focus:ring-0 focus:ring-offset-0 mt-0.5"
|
|
247
|
+
/>
|
|
248
|
+
<label className="ml-2 text-sm text-gray-600 font-light">
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
onClick={onClose}
|
|
252
|
+
className="text-black hover:underline"
|
|
253
|
+
>
|
|
254
|
+
Kullanım Şartları
|
|
255
|
+
</button>
|
|
256
|
+
{' '}ve{' '}
|
|
257
|
+
<button
|
|
258
|
+
type="button"
|
|
259
|
+
onClick={onClose}
|
|
260
|
+
className="text-black hover:underline"
|
|
261
|
+
>
|
|
262
|
+
Gizlilik Politikası
|
|
263
|
+
</button>
|
|
264
|
+
'nı okudum ve kabul ediyorum. *
|
|
265
|
+
</label>
|
|
266
|
+
</div>
|
|
267
|
+
{errors.acceptTerms && (
|
|
268
|
+
<p className="text-xs text-red-500 ml-6">{errors.acceptTerms.message}</p>
|
|
269
|
+
)}
|
|
270
|
+
|
|
271
|
+
<div className="flex items-start">
|
|
272
|
+
<input
|
|
273
|
+
type="checkbox"
|
|
274
|
+
{...register('acceptMarketing')}
|
|
275
|
+
className="w-4 h-4 text-black border-gray-300 rounded focus:ring-0 focus:ring-offset-0 mt-0.5"
|
|
276
|
+
/>
|
|
277
|
+
<label className="ml-2 text-sm text-gray-600 font-light">
|
|
278
|
+
Kampanya ve promosyon bilgilerini e-posta ile almak istiyorum.
|
|
279
|
+
</label>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
{/* Register Button */}
|
|
284
|
+
<button
|
|
285
|
+
type="submit"
|
|
286
|
+
disabled={isSubmitting || registerMutation.isPending}
|
|
287
|
+
className="w-full bg-black text-white py-3 rounded-sm hover:bg-gray-800 transition-colors duration-200 flex items-center justify-center space-x-2 group disabled:opacity-50 disabled:cursor-not-allowed"
|
|
288
|
+
>
|
|
289
|
+
<span className="text-sm font-light uppercase tracking-wider">
|
|
290
|
+
{isSubmitting || registerMutation.isPending ? 'Kayıt Oluşturuluyor...' : 'Kayıt Ol'}
|
|
291
|
+
</span>
|
|
292
|
+
{!isSubmitting && !registerMutation.isPending && (
|
|
293
|
+
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
|
294
|
+
)}
|
|
295
|
+
</button>
|
|
296
|
+
|
|
297
|
+
{/* Divider */}
|
|
298
|
+
<div className="my-8 flex items-center">
|
|
299
|
+
<div className="flex-1 border-t border-gray-200"></div>
|
|
300
|
+
<span className="px-4 text-xs text-gray-500 uppercase tracking-wider">
|
|
301
|
+
veya
|
|
302
|
+
</span>
|
|
303
|
+
<div className="flex-1 border-t border-gray-200"></div>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
{/* Social Register */}
|
|
307
|
+
<div className="space-y-3 hidden">
|
|
308
|
+
<button
|
|
309
|
+
type="button"
|
|
310
|
+
className="w-full border border-gray-200 py-3 rounded-sm hover:bg-gray-50 transition-colors duration-200 flex items-center justify-center space-x-2"
|
|
311
|
+
>
|
|
312
|
+
<svg className="w-5 h-5" viewBox="0 0 24 24">
|
|
313
|
+
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
|
|
314
|
+
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
|
|
315
|
+
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
|
316
|
+
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
|
317
|
+
</svg>
|
|
318
|
+
<span className="text-sm font-light">Google ile kayıt ol</span>
|
|
319
|
+
</button>
|
|
320
|
+
|
|
321
|
+
<button
|
|
322
|
+
type="button"
|
|
323
|
+
className="w-full border border-gray-200 py-3 rounded-sm hover:bg-gray-50 transition-colors duration-200 flex items-center justify-center space-x-2"
|
|
324
|
+
>
|
|
325
|
+
<svg className="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
|
|
326
|
+
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
|
|
327
|
+
</svg>
|
|
328
|
+
<span className="text-sm font-light">Facebook ile kayıt ol</span>
|
|
329
|
+
</button>
|
|
330
|
+
</div>
|
|
331
|
+
</form>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { nexineAxios } from '@/core/util/nexine.axios';
|
|
2
|
+
import { IData } from '@/core/interface/nexine.interface';
|
|
3
|
+
import { BankAccount, BankTransferCode } from '@/core/interface/prisma.interface';
|
|
4
|
+
import { useMutation, useQuery } from '@tanstack/react-query';
|
|
5
|
+
|
|
6
|
+
async function fetchBankAccounts() {
|
|
7
|
+
const withTags = await nexineAxios.get<IData<BankAccount>>('/customer/bank-account', {
|
|
8
|
+
params: {
|
|
9
|
+
isCompanySpecificVisible: true,
|
|
10
|
+
tags: ['WEB'],
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const tagged = withTags.data?.data ?? [];
|
|
15
|
+
if (tagged.length > 0) return withTags.data;
|
|
16
|
+
|
|
17
|
+
const fallback = await nexineAxios.get<IData<BankAccount>>('/customer/bank-account', {
|
|
18
|
+
params: {
|
|
19
|
+
isCompanySpecificVisible: true,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return fallback.data;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const useBankAccount = () => {
|
|
27
|
+
return {
|
|
28
|
+
fetch: (opts?: { enabled?: boolean }) => {
|
|
29
|
+
return useQuery({
|
|
30
|
+
queryKey: ['bank-account', 'web'],
|
|
31
|
+
queryFn: fetchBankAccounts,
|
|
32
|
+
enabled: opts?.enabled !== false,
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
createTransferCode: () => {
|
|
36
|
+
return useMutation({
|
|
37
|
+
mutationFn: async (data: { amount: number; bankAccountId: string }) => {
|
|
38
|
+
const response = await nexineAxios.post<{ bankTransferCode: BankTransferCode }>(
|
|
39
|
+
'/customer/payment/bank-transfer',
|
|
40
|
+
data
|
|
41
|
+
);
|
|
42
|
+
return response.data.bankTransferCode;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { nexineAxios } from '@/core/util/nexine.axios';
|
|
2
|
+
import { IData } from '@/core/interface/nexine.interface';
|
|
3
|
+
import { Brand } from '@/core/interface/prisma.interface';
|
|
4
|
+
import { useQuery } from '@tanstack/react-query';
|
|
5
|
+
|
|
6
|
+
export const useBrand = () => {
|
|
7
|
+
return {
|
|
8
|
+
fetch: (params?: { enabled?: boolean; page?: number; amount?: number }) => {
|
|
9
|
+
return useQuery({
|
|
10
|
+
queryKey: ['brand', params?.page, params?.amount],
|
|
11
|
+
enabled: params?.enabled ?? true,
|
|
12
|
+
queryFn: async () => {
|
|
13
|
+
const response = await nexineAxios.get<IData<Brand>>('/customer/brand', {
|
|
14
|
+
params: {
|
|
15
|
+
page: params?.page,
|
|
16
|
+
amount: params?.amount,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return response.data;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
};
|