@raxonltd/raxon-core 1.1.7 → 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.
- 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 +76 -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/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/view/view.checkout.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -3
- package/tailwind.css +11 -0
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import type { BasketSummaryInterface } from '@/core/interface/basket.interface';
|
|
5
|
+
import { CartPromoCodeSection } from '@/core/feature/cart/component/cart.promo.code.section';
|
|
6
|
+
|
|
7
|
+
const ShieldCheck = (props: React.SVGProps<SVGSVGElement>) => (
|
|
8
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...props}>
|
|
9
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
|
10
|
+
<polyline points="9 12 11 14 15 10" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
type CartOrderSummaryProps = {
|
|
15
|
+
cart: BasketSummaryInterface | null;
|
|
16
|
+
itemCount: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function CartOrderSummary({ cart, itemCount }: CartOrderSummaryProps) {
|
|
20
|
+
return (
|
|
21
|
+
<aside className="lg:col-span-4 lg:sticky lg:top-28 self-start w-full">
|
|
22
|
+
<div className="p-4 bg-white border border-gray-100 rounded-2xl space-y-4">
|
|
23
|
+
<div className="pb-1">
|
|
24
|
+
<h2 className="text-sm font-medium text-gray-900 uppercase tracking-[0.1em]">Sipariş Özeti</h2>
|
|
25
|
+
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-widest mt-0.5">{itemCount} Ürün</p>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div className="space-y-2.5">
|
|
29
|
+
<div className="flex justify-between items-center text-sm">
|
|
30
|
+
<span className="text-gray-500">Ara Toplam</span>
|
|
31
|
+
<span className="font-bold text-gray-900">
|
|
32
|
+
₺{(Number(cart?.info?.payPrice?.total) || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 })}
|
|
33
|
+
</span>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
{Number(cart?.info?.discount?.pay || 0) > 0 && (
|
|
37
|
+
<div className="flex justify-between items-center text-sm">
|
|
38
|
+
<span className="text-gray-500">İndirim</span>
|
|
39
|
+
<span className="font-bold text-green-600">
|
|
40
|
+
-₺{Number(cart?.info?.discount?.pay || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 })}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{Number(cart?.info?.promotion?.pay || 0) > 0 && (
|
|
46
|
+
<div className="flex justify-between items-center text-sm">
|
|
47
|
+
<span className="text-gray-500">Promosyon</span>
|
|
48
|
+
<span className="font-bold text-green-600">
|
|
49
|
+
-₺{Number(cart?.info?.promotion?.pay || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 })}
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
<div className="flex justify-between items-center text-sm">
|
|
55
|
+
<span className="text-gray-500">KDV Dahil</span>
|
|
56
|
+
<span className="font-bold text-gray-900">
|
|
57
|
+
₺{(Number(cart?.info?.payPrice?.tax) || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 })}
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div className="pt-3 border-t border-gray-100">
|
|
63
|
+
<CartPromoCodeSection variant="cart" />
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div className="pt-3 border-t border-gray-100 space-y-4">
|
|
67
|
+
<div className="flex justify-between items-end gap-3">
|
|
68
|
+
<span className="text-sm font-bold text-gray-900">Toplam</span>
|
|
69
|
+
<span className="text-xl font-light text-gray-900">
|
|
70
|
+
₺{(Number(cart?.info?.payPrice?.pay) || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 })}
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<Link
|
|
75
|
+
href="/odeme"
|
|
76
|
+
className="block w-full bg-black text-white py-4 rounded-full text-center text-[11px] font-black uppercase tracking-[0.2em] hover:bg-[#CF0A2C] transition-all duration-300"
|
|
77
|
+
>
|
|
78
|
+
Ödemeye Geç
|
|
79
|
+
</Link>
|
|
80
|
+
|
|
81
|
+
<div className="flex items-center justify-center gap-2 text-[9px] font-bold text-gray-400 uppercase tracking-widest">
|
|
82
|
+
<ShieldCheck className="w-3.5 h-3.5 shrink-0" />
|
|
83
|
+
Güvenli Ödeme
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</aside>
|
|
88
|
+
);
|
|
89
|
+
}
|