translation added
This commit is contained in:
@@ -11,9 +11,12 @@ import { MotionWrapper } from '../../../../shared/ui/motion';
|
|||||||
import { useLoginForm } from '../lib/useLoginForm';
|
import { useLoginForm } from '../lib/useLoginForm';
|
||||||
import { useLoginModal } from '@/shared/zustand/auth';
|
import { useLoginModal } from '@/shared/zustand/auth';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
export function LoginForm() {
|
export function LoginForm() {
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
const t = useTranslations('Auth.Login');
|
||||||
|
const tCommon = useTranslations('Common');
|
||||||
|
|
||||||
const { phone, setPhone, submit, error, loading } = useLoginForm();
|
const { phone, setPhone, submit, error, loading } = useLoginForm();
|
||||||
const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal);
|
const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal);
|
||||||
@@ -49,14 +52,12 @@ export function LoginForm() {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<p className="mb-1 text-[0.65rem] font-semibold uppercase tracking-[0.16em] text-stone-400">
|
<p className="mb-1 text-[0.65rem] font-semibold uppercase tracking-[0.16em] text-stone-400">
|
||||||
Xush kelibsiz
|
{t('welcome')}
|
||||||
</p>
|
</p>
|
||||||
<h1 className="mb-1 font-serif text-3xl leading-tight text-stone-900">
|
<h1 className="mb-1 font-serif text-3xl leading-tight text-stone-900">
|
||||||
Kirish
|
{t('title')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mb-8 text-sm text-stone-400">
|
<p className="mb-8 text-sm text-stone-400">{t('description')}</p>
|
||||||
Telefon raqamingizni kiriting.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form onSubmit={submit} noValidate className="flex flex-col gap-5">
|
<form onSubmit={submit} noValidate className="flex flex-col gap-5">
|
||||||
{/* Phone field */}
|
{/* Phone field */}
|
||||||
@@ -65,7 +66,7 @@ export function LoginForm() {
|
|||||||
htmlFor="phone"
|
htmlFor="phone"
|
||||||
className="text-[0.7rem] font-medium tracking-widest uppercase text-stone-500"
|
className="text-[0.7rem] font-medium tracking-widest uppercase text-stone-500"
|
||||||
>
|
>
|
||||||
Telefon raqam
|
{t('phoneLabel')}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -75,7 +76,7 @@ export function LoginForm() {
|
|||||||
<input
|
<input
|
||||||
id="phone"
|
id="phone"
|
||||||
type="tel"
|
type="tel"
|
||||||
placeholder="90 123 45 67"
|
placeholder={t('phonePlaceholder')}
|
||||||
value={formatPhone(phone)}
|
value={formatPhone(phone)}
|
||||||
onChange={handlePhoneChange}
|
onChange={handlePhoneChange}
|
||||||
onFocus={() => setIsFocused(true)}
|
onFocus={() => setIsFocused(true)}
|
||||||
@@ -98,11 +99,12 @@ export function LoginForm() {
|
|||||||
{/* Digit counter / complete hint */}
|
{/* Digit counter / complete hint */}
|
||||||
<div className="flex items-center justify-between px-0.5">
|
<div className="flex items-center justify-between px-0.5">
|
||||||
<span className="text-[0.7rem] text-stone-400">
|
<span className="text-[0.7rem] text-stone-400">
|
||||||
{phone.length > 0 && `${phone.length} ta raqam kiritildi`}
|
{phone.length > 0 &&
|
||||||
|
t('digitsEntered', { count: phone.length })}
|
||||||
</span>
|
</span>
|
||||||
{phone.length === 9 && (
|
{phone.length === 9 && (
|
||||||
<span className="text-[0.7rem] font-medium text-emerald-600">
|
<span className="text-[0.7rem] font-medium text-emerald-600">
|
||||||
✓ To‘liq
|
{tCommon('complete')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -130,10 +132,10 @@ export function LoginForm() {
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<span className="flex items-center justify-center gap-2">
|
<span className="flex items-center justify-center gap-2">
|
||||||
<span className="h-3.5 w-3.5 rounded-full border-2 border-stone-500 border-t-stone-100 animate-spin" />
|
<span className="h-3.5 w-3.5 rounded-full border-2 border-stone-500 border-t-stone-100 animate-spin" />
|
||||||
Yuborilmoqda…
|
{t('sending')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
'Kodni yuborish'
|
t('sendCode')
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -141,19 +143,19 @@ export function LoginForm() {
|
|||||||
<div className="relative flex items-center gap-3 py-1">
|
<div className="relative flex items-center gap-3 py-1">
|
||||||
<span className="h-px flex-1 bg-stone-200" />
|
<span className="h-px flex-1 bg-stone-200" />
|
||||||
<span className="text-[0.65rem] font-medium uppercase tracking-widest text-stone-400">
|
<span className="text-[0.65rem] font-medium uppercase tracking-widest text-stone-400">
|
||||||
yoki
|
{tCommon('or')}
|
||||||
</span>
|
</span>
|
||||||
<span className="h-px flex-1 bg-stone-200" />
|
<span className="h-px flex-1 bg-stone-200" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Register hint */}
|
{/* Register hint */}
|
||||||
<p className="text-center text-[0.78rem] text-stone-400">
|
<p className="text-center text-[0.78rem] text-stone-400">
|
||||||
Hisobingiz yo‘qmi?
|
{t('registerPrompt')}
|
||||||
<Link
|
<Link
|
||||||
href="/register"
|
href="/register"
|
||||||
className="text-stone-800 underline underline-offset-2 hover:text-stone-600 transition-colors"
|
className="text-stone-800 underline underline-offset-2 hover:text-stone-600 transition-colors"
|
||||||
>
|
>
|
||||||
Ro‘yxatdan o‘tish
|
{t('registerLink')}
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, { useCallback } from 'react';
|
|||||||
import { useRegisterForm } from '../lib/useRegisterForm';
|
import { useRegisterForm } from '../lib/useRegisterForm';
|
||||||
import { formatPhone, normalizeDigits } from '@/shared/lib/formatPhone';
|
import { formatPhone, normalizeDigits } from '@/shared/lib/formatPhone';
|
||||||
import PhonePrefix from '@/shared/ui/phonePrefix';
|
import PhonePrefix from '@/shared/ui/phonePrefix';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
function Field({
|
function Field({
|
||||||
id,
|
id,
|
||||||
@@ -64,6 +65,8 @@ function Field({
|
|||||||
|
|
||||||
export function RegisterFormUI() {
|
export function RegisterFormUI() {
|
||||||
const [phone, setPhone] = React.useState('');
|
const [phone, setPhone] = React.useState('');
|
||||||
|
const t = useTranslations('Auth.Register');
|
||||||
|
const tCommon = useTranslations('Common');
|
||||||
const {
|
const {
|
||||||
registerData,
|
registerData,
|
||||||
errors,
|
errors,
|
||||||
@@ -97,11 +100,9 @@ export function RegisterFormUI() {
|
|||||||
✓
|
✓
|
||||||
</div>
|
</div>
|
||||||
<h2 className="mb-1 font-serif text-2xl text-stone-900">
|
<h2 className="mb-1 font-serif text-2xl text-stone-900">
|
||||||
You‘re registered
|
{t('successTitle')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-stone-400">
|
<p className="text-sm text-stone-400">{t('successMessage')}</p>
|
||||||
We‘ll be in touch shortly.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -112,14 +113,12 @@ export function RegisterFormUI() {
|
|||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<p className="mb-1 text-[0.65rem] font-semibold uppercase tracking-[0.16em] text-stone-400">
|
<p className="mb-1 text-[0.65rem] font-semibold uppercase tracking-[0.16em] text-stone-400">
|
||||||
Create account
|
{t('headerLabel')}
|
||||||
</p>
|
</p>
|
||||||
<h1 className="mb-1 font-serif text-3xl leading-tight text-stone-900">
|
<h1 className="mb-1 font-serif text-3xl leading-tight text-stone-900">
|
||||||
Register
|
{t('title')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mb-8 text-sm text-stone-400">
|
<p className="mb-8 text-sm text-stone-400">{t('description')}</p>
|
||||||
Fill in your details to get started.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} noValidate className="flex flex-col gap-5">
|
<form onSubmit={handleSubmit} noValidate className="flex flex-col gap-5">
|
||||||
{/* Name + Surname row */}
|
{/* Name + Surname row */}
|
||||||
@@ -128,7 +127,7 @@ export function RegisterFormUI() {
|
|||||||
id="name"
|
id="name"
|
||||||
name="name"
|
name="name"
|
||||||
label="Name"
|
label="Name"
|
||||||
placeholder="Ali"
|
placeholder={t('namePlaceholder')}
|
||||||
value={registerData.name}
|
value={registerData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
error={errors.name}
|
error={errors.name}
|
||||||
@@ -137,7 +136,7 @@ export function RegisterFormUI() {
|
|||||||
id="surname"
|
id="surname"
|
||||||
name="surname"
|
name="surname"
|
||||||
label="Surname"
|
label="Surname"
|
||||||
placeholder="Karimov"
|
placeholder={t('surnamePlaceholder')}
|
||||||
value={registerData.surname}
|
value={registerData.surname}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
error={errors.surname}
|
error={errors.surname}
|
||||||
@@ -150,7 +149,7 @@ export function RegisterFormUI() {
|
|||||||
htmlFor="phone"
|
htmlFor="phone"
|
||||||
className="text-[0.7rem] font-medium tracking-widest uppercase text-stone-500"
|
className="text-[0.7rem] font-medium tracking-widest uppercase text-stone-500"
|
||||||
>
|
>
|
||||||
Telefon raqam
|
{t('phoneLabel')}
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
className={`relative transition-transform duration-200 ${isFocused ? 'scale-[1.01]' : ''}`}
|
className={`relative transition-transform duration-200 ${isFocused ? 'scale-[1.01]' : ''}`}
|
||||||
@@ -159,7 +158,7 @@ export function RegisterFormUI() {
|
|||||||
<input
|
<input
|
||||||
id="phone"
|
id="phone"
|
||||||
type="tel"
|
type="tel"
|
||||||
placeholder="90 123 45 67"
|
placeholder={t('phonePlaceholder')}
|
||||||
value={formatPhone(phone)}
|
value={formatPhone(phone)}
|
||||||
onChange={handlePhoneChange}
|
onChange={handlePhoneChange}
|
||||||
onFocus={() => setIsFocused(true)}
|
onFocus={() => setIsFocused(true)}
|
||||||
@@ -180,11 +179,11 @@ export function RegisterFormUI() {
|
|||||||
{/* Digit counter / complete hint */}
|
{/* Digit counter / complete hint */}
|
||||||
<div className="flex items-center justify-between px-0.5">
|
<div className="flex items-center justify-between px-0.5">
|
||||||
<span className="text-[0.7rem] text-stone-400">
|
<span className="text-[0.7rem] text-stone-400">
|
||||||
{phone.length > 0 && `${phone.length} ta raqam kiritildi`}
|
{phone.length > 0 && t('digitsEntered', { count: phone.length })}
|
||||||
</span>
|
</span>
|
||||||
{phone.length === 9 && (
|
{phone.length === 9 && (
|
||||||
<span className="text-[0.7rem] font-medium text-emerald-600">
|
<span className="text-[0.7rem] font-medium text-emerald-600">
|
||||||
✓ To‘liq
|
{tCommon('complete')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -204,24 +203,7 @@ export function RegisterFormUI() {
|
|||||||
htmlFor="oferta"
|
htmlFor="oferta"
|
||||||
className="cursor-pointer text-[0.78rem] leading-relaxed text-stone-500"
|
className="cursor-pointer text-[0.78rem] leading-relaxed text-stone-500"
|
||||||
>
|
>
|
||||||
I agree to the
|
{t('terms')}
|
||||||
<a
|
|
||||||
href="/terms"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-stone-800 underline underline-offset-2 hover:text-stone-600"
|
|
||||||
>
|
|
||||||
Terms of Service
|
|
||||||
</a>
|
|
||||||
and
|
|
||||||
<a
|
|
||||||
href="/privacy"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-stone-800 underline underline-offset-2 hover:text-stone-600"
|
|
||||||
>
|
|
||||||
Privacy Policy
|
|
||||||
</a>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{errors.oferta && (
|
{errors.oferta && (
|
||||||
@@ -244,10 +226,10 @@ export function RegisterFormUI() {
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<span className="flex items-center justify-center gap-2">
|
<span className="flex items-center justify-center gap-2">
|
||||||
<span className="h-3.5 w-3.5 rounded-full border-2 border-stone-500 border-t-stone-100 animate-spin" />
|
<span className="h-3.5 w-3.5 rounded-full border-2 border-stone-500 border-t-stone-100 animate-spin" />
|
||||||
Submitting…
|
{tCommon('submitting')}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
'Create account'
|
t('submitButton')
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -100,5 +100,108 @@
|
|||||||
"downloadCertificate": "Download Certificate",
|
"downloadCertificate": "Download Certificate",
|
||||||
"unknownError": "Unknown error",
|
"unknownError": "Unknown error",
|
||||||
"words": "words"
|
"words": "words"
|
||||||
|
},
|
||||||
|
"Hero": {
|
||||||
|
"badge": "Academic Integrity Platform",
|
||||||
|
"mainHeading": "Is Your Work",
|
||||||
|
"mainHeadingItalic": "Truly Original?",
|
||||||
|
"description": "Plagiarism is presenting someone else's ideas or words as your own. In academia and professional life, it carries serious consequences. Our platform detects it in seconds — so you can submit with full confidence.",
|
||||||
|
"certificateNote": "Certificate issued within 24h"
|
||||||
|
},
|
||||||
|
"Common": {
|
||||||
|
"startButton": "Start Checking →",
|
||||||
|
"complete": "✓ Complete",
|
||||||
|
"or": "or",
|
||||||
|
"submitting": "Submitting…",
|
||||||
|
"autoFilled": "Auto-filled",
|
||||||
|
"removeFile": "Remove file"
|
||||||
|
},
|
||||||
|
"InfoSection": {
|
||||||
|
"label": "Why It Matters",
|
||||||
|
"heading": "Understanding Plagiarism"
|
||||||
|
},
|
||||||
|
"StepsSection": {
|
||||||
|
"label": "Process",
|
||||||
|
"heading": "How It Works",
|
||||||
|
"description": "Six simple steps from upload to certified report.",
|
||||||
|
"ctaHeading": "Ready to verify your document?",
|
||||||
|
"ctaDescription": "Get your originality certificate in under 24 hours."
|
||||||
|
},
|
||||||
|
"Steps": {
|
||||||
|
"step1Title": "Click Start",
|
||||||
|
"step1Desc": "Begin your plagiarism check journey",
|
||||||
|
"step2Title": "Register or Log In",
|
||||||
|
"step2Desc": "Create an account or sign in to your existing one",
|
||||||
|
"step3Title": "Upload Document",
|
||||||
|
"step3Desc": "Upload your PDF, DOC, DOCX, or TXT file (max 20MB)",
|
||||||
|
"step4Title": "Send for Check",
|
||||||
|
"step4Desc": "Submit your document for comprehensive analysis",
|
||||||
|
"step5Title": "Complete Payment",
|
||||||
|
"step5Desc": "Pay securely for your plagiarism check service",
|
||||||
|
"step6Title": "Get Your Report",
|
||||||
|
"step6Desc": "Receive detailed results and certificate within 24 hours"
|
||||||
|
},
|
||||||
|
"Stats": {
|
||||||
|
"accuracy": "Detection accuracy",
|
||||||
|
"documents": "Documents checked",
|
||||||
|
"formats": "Supported formats",
|
||||||
|
"turnaround": "Report turnaround"
|
||||||
|
},
|
||||||
|
"InfoCards": {
|
||||||
|
"card1Title": "What is Plagiarism?",
|
||||||
|
"card1Desc": "Plagiarism occurs when someone uses another person's ideas, words, or work without proper credit. It can be intentional or unintentional.",
|
||||||
|
"card2Title": "Why Check Your Document?",
|
||||||
|
"card2Desc": "Ensure academic integrity, avoid penalties, and maintain your reputation. Our service provides comprehensive plagiarism detection.",
|
||||||
|
"card3Title": "What You Get",
|
||||||
|
"card3Desc": "Detailed plagiarism report, similarity percentage, matched sources, and an official certificate of originality."
|
||||||
|
},
|
||||||
|
"Ticker": {
|
||||||
|
"item1": "Originality Verified",
|
||||||
|
"item2": "Academic Integrity",
|
||||||
|
"item3": "Trusted Reports",
|
||||||
|
"item4": "Deep Analysis",
|
||||||
|
"item5": "Fast Results",
|
||||||
|
"item6": "Official Certificate"
|
||||||
|
},
|
||||||
|
"Auth": {
|
||||||
|
"Login": {
|
||||||
|
"welcome": "Welcome back",
|
||||||
|
"title": "Login",
|
||||||
|
"description": "Enter your phone number to continue.",
|
||||||
|
"phoneLabel": "Phone number",
|
||||||
|
"phonePlaceholder": "90 123 45 67",
|
||||||
|
"digitsEntered": "{count} digits entered",
|
||||||
|
"sending": "Sending…",
|
||||||
|
"sendCode": "Send code",
|
||||||
|
"registerPrompt": "Don't have an account?",
|
||||||
|
"registerLink": "Register"
|
||||||
|
},
|
||||||
|
"Register": {
|
||||||
|
"successTitle": "You're registered",
|
||||||
|
"successMessage": "We'll be in touch shortly.",
|
||||||
|
"headerLabel": "Create account",
|
||||||
|
"title": "Register",
|
||||||
|
"description": "Fill in your details to get started.",
|
||||||
|
"phoneLabel": "Phone number",
|
||||||
|
"phonePlaceholder": "90 123 45 67",
|
||||||
|
"digitsEntered": "{count} digits entered",
|
||||||
|
"namePlaceholder": "Ali",
|
||||||
|
"surnamePlaceholder": "Karimov",
|
||||||
|
"terms": "I agree to the Terms of Service and Privacy Policy",
|
||||||
|
"submitButton": "Create account"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Payment": {
|
||||||
|
"title": "Payment",
|
||||||
|
"description": "Review your order and pay securely",
|
||||||
|
"orderSummary": "Order Summary",
|
||||||
|
"certificateIncluded": "Certificate of completion included",
|
||||||
|
"paymentMethod": "Payment Method",
|
||||||
|
"security": "Secured by Payme · SSL encrypted",
|
||||||
|
"serviceFee": "Service fee",
|
||||||
|
"certificateLabel": "Certificate",
|
||||||
|
"total": "Total",
|
||||||
|
"connecting": "Connecting to Payme…",
|
||||||
|
"payButton": "Pay with Payme"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,5 +100,108 @@
|
|||||||
"downloadCertificate": "Скачать сертификат",
|
"downloadCertificate": "Скачать сертификат",
|
||||||
"unknownError": "Неизвестная ошибка",
|
"unknownError": "Неизвестная ошибка",
|
||||||
"words": "слов"
|
"words": "слов"
|
||||||
|
},
|
||||||
|
"Hero": {
|
||||||
|
"badge": "Платформа академической честности",
|
||||||
|
"mainHeading": "Ваша работа",
|
||||||
|
"mainHeadingItalic": "Действительно оригинальна?",
|
||||||
|
"description": "Плагиат - это представление идей или слов другого человека как своих собственных. В академической и профессиональной жизни это несет серьезные последствия. Наша платформа обнаруживает его за секунды — чтобы вы могли отправлять с полной уверенностью.",
|
||||||
|
"certificateNote": "Сертификат выдается в течение 24 часов"
|
||||||
|
},
|
||||||
|
"Common": {
|
||||||
|
"startButton": "Начать проверку →",
|
||||||
|
"complete": "✓ Завершено",
|
||||||
|
"or": "или",
|
||||||
|
"submitting": "Отправка…",
|
||||||
|
"autoFilled": "Автозаполнено",
|
||||||
|
"removeFile": "Удалить файл"
|
||||||
|
},
|
||||||
|
"InfoSection": {
|
||||||
|
"label": "Почему это важно",
|
||||||
|
"heading": "Понимание плагиата"
|
||||||
|
},
|
||||||
|
"StepsSection": {
|
||||||
|
"label": "Процесс",
|
||||||
|
"heading": "Как это работает",
|
||||||
|
"description": "Шесть простых шагов от загрузки до сертифицированного отчета.",
|
||||||
|
"ctaHeading": "Готовы проверить ваш документ?",
|
||||||
|
"ctaDescription": "Получите сертификат оригинальности менее чем за 24 часа."
|
||||||
|
},
|
||||||
|
"Steps": {
|
||||||
|
"step1Title": "Нажмите Начать",
|
||||||
|
"step1Desc": "Начните ваше путешествие по проверке на плагиат",
|
||||||
|
"step2Title": "Зарегистрируйтесь или войдите",
|
||||||
|
"step2Desc": "Создайте аккаунт или войдите в существующий",
|
||||||
|
"step3Title": "Загрузите документ",
|
||||||
|
"step3Desc": "Загрузите ваш PDF, DOC, DOCX или TXT файл (макс 20МБ)",
|
||||||
|
"step4Title": "Отправьте на проверку",
|
||||||
|
"step4Desc": "Отправьте ваш документ на комплексный анализ",
|
||||||
|
"step5Title": "Завершите оплату",
|
||||||
|
"step5Desc": "Оплатите безопасно услугу проверки на плагиат",
|
||||||
|
"step6Title": "Получите ваш отчет",
|
||||||
|
"step6Desc": "Получите подробные результаты и сертификат в течение 24 часов"
|
||||||
|
},
|
||||||
|
"Stats": {
|
||||||
|
"accuracy": "Точность обнаружения",
|
||||||
|
"documents": "Проверено документов",
|
||||||
|
"formats": "Поддерживаемые форматы",
|
||||||
|
"turnaround": "Время выполнения отчета"
|
||||||
|
},
|
||||||
|
"InfoCards": {
|
||||||
|
"card1Title": "Что такое плагиат?",
|
||||||
|
"card1Desc": "Плагиат происходит, когда кто-то использует идеи, слова или работу другого человека без надлежащего указания. Это может быть преднамеренным или непреднамеренным.",
|
||||||
|
"card2Title": "Почему проверять ваш документ?",
|
||||||
|
"card2Desc": "Обеспечьте академическую честность, избегайте штрафов и поддерживайте вашу репутацию. Наш сервис предоставляет комплексное обнаружение плагиата.",
|
||||||
|
"card3Title": "Что вы получаете",
|
||||||
|
"card3Desc": "Подробный отчет о плагиате, процент схожести, найденные источники и официальный сертификат оригинальности."
|
||||||
|
},
|
||||||
|
"Ticker": {
|
||||||
|
"item1": "Оригинальность подтверждена",
|
||||||
|
"item2": "Академическая честность",
|
||||||
|
"item3": "Надежные отчеты",
|
||||||
|
"item4": "Глубокий анализ",
|
||||||
|
"item5": "Быстрые результаты",
|
||||||
|
"item6": "Официальный сертификат"
|
||||||
|
},
|
||||||
|
"Auth": {
|
||||||
|
"Login": {
|
||||||
|
"welcome": "Добро пожаловать обратно",
|
||||||
|
"title": "Вход",
|
||||||
|
"description": "Введите ваш номер телефона для продолжения.",
|
||||||
|
"phoneLabel": "Номер телефона",
|
||||||
|
"phonePlaceholder": "90 123 45 67",
|
||||||
|
"digitsEntered": "Введено {count} цифр",
|
||||||
|
"sending": "Отправка…",
|
||||||
|
"sendCode": "Отправить код",
|
||||||
|
"registerPrompt": "Нет аккаунта?",
|
||||||
|
"registerLink": "Зарегистрироваться"
|
||||||
|
},
|
||||||
|
"Register": {
|
||||||
|
"successTitle": "Вы зарегистрированы",
|
||||||
|
"successMessage": "Мы свяжемся с вами в ближайшее время.",
|
||||||
|
"headerLabel": "Создать аккаунт",
|
||||||
|
"title": "Регистрация",
|
||||||
|
"description": "Заполните ваши данные, чтобы начать.",
|
||||||
|
"phoneLabel": "Номер телефона",
|
||||||
|
"phonePlaceholder": "90 123 45 67",
|
||||||
|
"digitsEntered": "Введено {count} цифр",
|
||||||
|
"namePlaceholder": "Али",
|
||||||
|
"surnamePlaceholder": "Каримов",
|
||||||
|
"terms": "Я согласен с Условиями обслуживания и Политикой конфиденциальности",
|
||||||
|
"submitButton": "Создать аккаунт"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Payment": {
|
||||||
|
"title": "Оплата",
|
||||||
|
"description": "Просмотрите ваш заказ и оплатите безопасно",
|
||||||
|
"orderSummary": "Сводка заказа",
|
||||||
|
"certificateIncluded": "Включен сертификат завершения",
|
||||||
|
"paymentMethod": "Способ оплаты",
|
||||||
|
"security": "Защищено Payme · SSL шифрование",
|
||||||
|
"serviceFee": "Стоимость услуги",
|
||||||
|
"certificateLabel": "Сертификат",
|
||||||
|
"total": "Итого",
|
||||||
|
"connecting": "Подключение к Payme…",
|
||||||
|
"payButton": "Оплатить через Payme"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ declare const messages: {
|
|||||||
processedAt: 'Qayta ishlangan';
|
processedAt: 'Qayta ishlangan';
|
||||||
certificate: 'Sertifikat';
|
certificate: 'Sertifikat';
|
||||||
noCertificate: 'Bu tekshiruv uchun sertifikat berilmagan.';
|
noCertificate: 'Bu tekshiruv uchun sertifikat berilmagan.';
|
||||||
noCertificateHighSimilarity: "Yuoqori o'xshashlik natijasida sertifikatlar berilmaydi.";
|
noCertificateHighSimilarity: "Yuqori o'xshashlik natijasida sertifikatlar berilmaydi.";
|
||||||
issued: 'Berilgan';
|
issued: 'Berilgan';
|
||||||
expires: 'Muddati tugaydi';
|
expires: 'Muddati tugaydi';
|
||||||
issuer: 'Beruvchi';
|
issuer: 'Beruvchi';
|
||||||
@@ -104,5 +104,108 @@ declare const messages: {
|
|||||||
unknownError: "Noma'lum xato";
|
unknownError: "Noma'lum xato";
|
||||||
words: "so'z";
|
words: "so'z";
|
||||||
};
|
};
|
||||||
|
Hero: {
|
||||||
|
badge: 'Akademik halollik platformasi';
|
||||||
|
mainHeading: 'Sizning ishingiz';
|
||||||
|
mainHeadingItalic: 'Haqiqatan ham originalmi?';
|
||||||
|
description: "Plagiat - bu boshqa birovning g'oyalarini yoki so'zlarini o'z g'oyalar yoki so'zlari sifatida taqdim etish. Akademik va professional hayotda bu jiddiy oqibatlarga olib keladi. Bizning platformamiz buni soniyalar ichida aniqlaydi — shuning uchun siz to'liq ishonch bilan yuborishingiz mumkin.";
|
||||||
|
certificateNote: 'Sertifikat 24 soat ichida beriladi';
|
||||||
|
};
|
||||||
|
Common: {
|
||||||
|
startButton: 'Tekshirishni boshlash →';
|
||||||
|
complete: "✓ To'liq";
|
||||||
|
or: 'yoki';
|
||||||
|
submitting: 'Yuborilmoqda…';
|
||||||
|
autoFilled: "Avto-to'ldirilgan";
|
||||||
|
removeFile: 'Faylni olib tashlash';
|
||||||
|
};
|
||||||
|
InfoSection: {
|
||||||
|
label: 'Nima uchun bu muhim';
|
||||||
|
heading: 'Plagiatni tushunish';
|
||||||
|
};
|
||||||
|
StepsSection: {
|
||||||
|
label: 'Jarayon';
|
||||||
|
heading: 'Bu qanday ishlaydi';
|
||||||
|
description: 'Yuklashdan sertifikatlangan hisobotgacha oltita oddiy qadam.';
|
||||||
|
ctaHeading: 'Hujjatingizni tekshirishga tayyormisiz?';
|
||||||
|
ctaDescription: '24 soat ichida orijinallik sertifikatini oling.';
|
||||||
|
};
|
||||||
|
Steps: {
|
||||||
|
step1Title: 'Boshlash tugmasini bosing';
|
||||||
|
step1Desc: 'Plagiat tekshiruvi sayohatini boshlang';
|
||||||
|
step2Title: "Ro'yxatdan o'ting yoki kiring";
|
||||||
|
step2Desc: 'Hisob yarating yoki mavjud hisobingizga kiring';
|
||||||
|
step3Title: 'Hujjatni yuklang';
|
||||||
|
step3Desc: 'PDF, DOC, DOCX yoki TXT faylingizni yuklang (maks 20MB)';
|
||||||
|
step4Title: 'Tekshirish uchun yuboring';
|
||||||
|
step4Desc: 'Hujjatingizni kompleks tahlil uchun yuboring';
|
||||||
|
step5Title: "To'lovni amalga oshiring";
|
||||||
|
step5Desc: "Plagiat tekshiruvi xizmatini xavfsiz to'lang";
|
||||||
|
step6Title: 'Hisobotingizni oling';
|
||||||
|
step6Desc: '24 soat ichida batafsil natijalar va sertifikatni oling';
|
||||||
|
};
|
||||||
|
Stats: {
|
||||||
|
accuracy: 'Aniqlash aniqligi';
|
||||||
|
documents: 'Tekshirilgan hujjatlar';
|
||||||
|
formats: "Qo'llab-quvvatlangan formatlar";
|
||||||
|
turnaround: 'Hisobot tayyorlanish vaqti';
|
||||||
|
};
|
||||||
|
InfoCards: {
|
||||||
|
card1Title: 'Plagiat nima?';
|
||||||
|
card1Desc: "Plagiat - bu kimdir boshqa birovning g'oyalarini, so'zlarini yoki ishini tegishli kredit bermay ishlatganda yuzaga keladi. Bu qasddan yoki qasdsiz bo'lishi mumkin.";
|
||||||
|
card2Title: 'Nima uchun hujjatingizni tekshirish kerak?';
|
||||||
|
card2Desc: "Akademik halollikni ta'minlang, jarimalardan qoching va obro'ingizni saqlang. Bizning xizmatimiz kompleks plagiat aniqlashni taqdim etadi.";
|
||||||
|
card3Title: 'Siz nima olasiz';
|
||||||
|
card3Desc: "Batafsil plagiat hisoboti, o'xshashlik foizi, topilgan manbalar va orijinallikning rasmiy sertifikati.";
|
||||||
|
};
|
||||||
|
Ticker: {
|
||||||
|
item1: 'Orijinallik tasdiqlangan';
|
||||||
|
item2: 'Akademik halollik';
|
||||||
|
item3: 'Ishonchli hisobotlar';
|
||||||
|
item4: 'Chuqur tahlil';
|
||||||
|
item5: 'Tez natijalar';
|
||||||
|
item6: 'Rasmiy sertifikat';
|
||||||
|
};
|
||||||
|
Auth: {
|
||||||
|
Login: {
|
||||||
|
welcome: 'Xush kelibsiz';
|
||||||
|
title: 'Kirish';
|
||||||
|
description: 'Davomini davom ettirish uchun telefon raqamingizni kiriting.';
|
||||||
|
phoneLabel: 'Telefon raqami';
|
||||||
|
phonePlaceholder: '90 123 45 67';
|
||||||
|
digitsEntered: '{count} ta raqam kiritildi';
|
||||||
|
sending: 'Yuborilmoqda…';
|
||||||
|
sendCode: 'Kodni yuborish';
|
||||||
|
registerPrompt: "Hisobingiz yo'qmi?";
|
||||||
|
registerLink: "Ro'yxatdan o'tish";
|
||||||
|
};
|
||||||
|
Register: {
|
||||||
|
successTitle: "Siz ro'yxatdan o'tdingiz";
|
||||||
|
successMessage: "Tez orada siz bilan bog'lanamiz.";
|
||||||
|
headerLabel: 'Hisob yaratish';
|
||||||
|
title: "Ro'yxatdan o'tish";
|
||||||
|
description: "Boshlash uchun ma'lumotlaringizni to'ldiring.";
|
||||||
|
phoneLabel: 'Telefon raqami';
|
||||||
|
phonePlaceholder: '90 123 45 67';
|
||||||
|
digitsEntered: '{count} ta raqam kiritildi';
|
||||||
|
namePlaceholder: 'Ali';
|
||||||
|
surnamePlaceholder: 'Karimov';
|
||||||
|
terms: "Men Xizmat ko'rsatish shartlari va Maxfiylik siyosatiga roziman";
|
||||||
|
submitButton: 'Hisob yaratish';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Payment: {
|
||||||
|
title: "To'lov";
|
||||||
|
description: "Buyurtmangizni ko'rib chiqing va xavfsiz to'lang";
|
||||||
|
orderSummary: "Buyurtma umumiy ko'rinishi";
|
||||||
|
certificateIncluded: 'Tugallanish sertifikati kiritilgan';
|
||||||
|
paymentMethod: "To'lov usuli";
|
||||||
|
security: 'Payme tomonidan himoyalangan · SSL shifrlash';
|
||||||
|
serviceFee: "Xizmat to'lovi";
|
||||||
|
certificateLabel: 'Sertifikat';
|
||||||
|
total: 'Jami';
|
||||||
|
connecting: 'Paymega ulanmoqda…';
|
||||||
|
payButton: "Payme orqali to'lash";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
export default messages;
|
export default messages;
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
"processedAt": "Qayta ishlangan",
|
"processedAt": "Qayta ishlangan",
|
||||||
"certificate": "Sertifikat",
|
"certificate": "Sertifikat",
|
||||||
"noCertificate": "Bu tekshiruv uchun sertifikat berilmagan.",
|
"noCertificate": "Bu tekshiruv uchun sertifikat berilmagan.",
|
||||||
"noCertificateHighSimilarity": "Yuoqori o'xshashlik natijasida sertifikatlar berilmaydi.",
|
"noCertificateHighSimilarity": "Yuqori o'xshashlik natijasida sertifikatlar berilmaydi.",
|
||||||
"issued": "Berilgan",
|
"issued": "Berilgan",
|
||||||
"expires": "Muddati tugaydi",
|
"expires": "Muddati tugaydi",
|
||||||
"issuer": "Beruvchi",
|
"issuer": "Beruvchi",
|
||||||
@@ -100,5 +100,108 @@
|
|||||||
"downloadCertificate": "Sertifikatni yuklab olish",
|
"downloadCertificate": "Sertifikatni yuklab olish",
|
||||||
"unknownError": "Noma'lum xato",
|
"unknownError": "Noma'lum xato",
|
||||||
"words": "so'z"
|
"words": "so'z"
|
||||||
|
},
|
||||||
|
"Hero": {
|
||||||
|
"badge": "Akademik halollik platformasi",
|
||||||
|
"mainHeading": "Sizning ishingiz",
|
||||||
|
"mainHeadingItalic": "Haqiqatan ham originalmi?",
|
||||||
|
"description": "Plagiat - bu boshqa birovning g'oyalarini yoki so'zlarini o'z g'oyalar yoki so'zlari sifatida taqdim etish. Akademik va professional hayotda bu jiddiy oqibatlarga olib keladi. Bizning platformamiz buni soniyalar ichida aniqlaydi — shuning uchun siz to'liq ishonch bilan yuborishingiz mumkin.",
|
||||||
|
"certificateNote": "Sertifikat 24 soat ichida beriladi"
|
||||||
|
},
|
||||||
|
"Common": {
|
||||||
|
"startButton": "Tekshirishni boshlash →",
|
||||||
|
"complete": "✓ To'liq",
|
||||||
|
"or": "yoki",
|
||||||
|
"submitting": "Yuborilmoqda…",
|
||||||
|
"autoFilled": "Avto-to'ldirilgan",
|
||||||
|
"removeFile": "Faylni olib tashlash"
|
||||||
|
},
|
||||||
|
"InfoSection": {
|
||||||
|
"label": "Nima uchun bu muhim",
|
||||||
|
"heading": "Plagiatni tushunish"
|
||||||
|
},
|
||||||
|
"StepsSection": {
|
||||||
|
"label": "Jarayon",
|
||||||
|
"heading": "Bu qanday ishlaydi",
|
||||||
|
"description": "Yuklashdan sertifikatlangan hisobotgacha oltita oddiy qadam.",
|
||||||
|
"ctaHeading": "Hujjatingizni tekshirishga tayyormisiz?",
|
||||||
|
"ctaDescription": "24 soat ichida orijinallik sertifikatini oling."
|
||||||
|
},
|
||||||
|
"Steps": {
|
||||||
|
"step1Title": "Boshlash tugmasini bosing",
|
||||||
|
"step1Desc": "Plagiat tekshiruvi sayohatini boshlang",
|
||||||
|
"step2Title": "Ro'yxatdan o'ting yoki kiring",
|
||||||
|
"step2Desc": "Hisob yarating yoki mavjud hisobingizga kiring",
|
||||||
|
"step3Title": "Hujjatni yuklang",
|
||||||
|
"step3Desc": "PDF, DOC, DOCX yoki TXT faylingizni yuklang (maks 20MB)",
|
||||||
|
"step4Title": "Tekshirish uchun yuboring",
|
||||||
|
"step4Desc": "Hujjatingizni kompleks tahlil uchun yuboring",
|
||||||
|
"step5Title": "To'lovni amalga oshiring",
|
||||||
|
"step5Desc": "Plagiat tekshiruvi xizmatini xavfsiz to'lang",
|
||||||
|
"step6Title": "Hisobotingizni oling",
|
||||||
|
"step6Desc": "24 soat ichida batafsil natijalar va sertifikatni oling"
|
||||||
|
},
|
||||||
|
"Stats": {
|
||||||
|
"accuracy": "Aniqlash aniqligi",
|
||||||
|
"documents": "Tekshirilgan hujjatlar",
|
||||||
|
"formats": "Qo'llab-quvvatlangan formatlar",
|
||||||
|
"turnaround": "Hisobot tayyorlanish vaqti"
|
||||||
|
},
|
||||||
|
"InfoCards": {
|
||||||
|
"card1Title": "Plagiat nima?",
|
||||||
|
"card1Desc": "Plagiat - bu kimdir boshqa birovning g'oyalarini, so'zlarini yoki ishini tegishli kredit bermay ishlatganda yuzaga keladi. Bu qasddan yoki qasdsiz bo'lishi mumkin.",
|
||||||
|
"card2Title": "Nima uchun hujjatingizni tekshirish kerak?",
|
||||||
|
"card2Desc": "Akademik halollikni ta'minlang, jarimalardan qoching va obro'ingizni saqlang. Bizning xizmatimiz kompleks plagiat aniqlashni taqdim etadi.",
|
||||||
|
"card3Title": "Siz nima olasiz",
|
||||||
|
"card3Desc": "Batafsil plagiat hisoboti, o'xshashlik foizi, topilgan manbalar va orijinallikning rasmiy sertifikati."
|
||||||
|
},
|
||||||
|
"Ticker": {
|
||||||
|
"item1": "Orijinallik tasdiqlangan",
|
||||||
|
"item2": "Akademik halollik",
|
||||||
|
"item3": "Ishonchli hisobotlar",
|
||||||
|
"item4": "Chuqur tahlil",
|
||||||
|
"item5": "Tez natijalar",
|
||||||
|
"item6": "Rasmiy sertifikat"
|
||||||
|
},
|
||||||
|
"Auth": {
|
||||||
|
"Login": {
|
||||||
|
"welcome": "Xush kelibsiz",
|
||||||
|
"title": "Kirish",
|
||||||
|
"description": "Davomini davom ettirish uchun telefon raqamingizni kiriting.",
|
||||||
|
"phoneLabel": "Telefon raqami",
|
||||||
|
"phonePlaceholder": "90 123 45 67",
|
||||||
|
"digitsEntered": "{count} ta raqam kiritildi",
|
||||||
|
"sending": "Yuborilmoqda…",
|
||||||
|
"sendCode": "Kodni yuborish",
|
||||||
|
"registerPrompt": "Hisobingiz yo'qmi?",
|
||||||
|
"registerLink": "Ro'yxatdan o'tish"
|
||||||
|
},
|
||||||
|
"Register": {
|
||||||
|
"successTitle": "Siz ro'yxatdan o'tdingiz",
|
||||||
|
"successMessage": "Tez orada siz bilan bog'lanamiz.",
|
||||||
|
"headerLabel": "Hisob yaratish",
|
||||||
|
"title": "Ro'yxatdan o'tish",
|
||||||
|
"description": "Boshlash uchun ma'lumotlaringizni to'ldiring.",
|
||||||
|
"phoneLabel": "Telefon raqami",
|
||||||
|
"phonePlaceholder": "90 123 45 67",
|
||||||
|
"digitsEntered": "{count} ta raqam kiritildi",
|
||||||
|
"namePlaceholder": "Ali",
|
||||||
|
"surnamePlaceholder": "Karimov",
|
||||||
|
"terms": "Men Xizmat ko'rsatish shartlari va Maxfiylik siyosatiga roziman",
|
||||||
|
"submitButton": "Hisob yaratish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Payment": {
|
||||||
|
"title": "To'lov",
|
||||||
|
"description": "Buyurtmangizni ko'rib chiqing va xavfsiz to'lang",
|
||||||
|
"orderSummary": "Buyurtma umumiy ko'rinishi",
|
||||||
|
"certificateIncluded": "Tugallanish sertifikati kiritilgan",
|
||||||
|
"paymentMethod": "To'lov usuli",
|
||||||
|
"security": "Payme tomonidan himoyalangan · SSL shifrlash",
|
||||||
|
"serviceFee": "Xizmat to'lovi",
|
||||||
|
"certificateLabel": "Sertifikat",
|
||||||
|
"total": "Jami",
|
||||||
|
"connecting": "Paymega ulanmoqda…",
|
||||||
|
"payButton": "Payme orqali to'lash"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import DocIllustration from './DocIllustration';
|
|||||||
import Section from './Section';
|
import Section from './Section';
|
||||||
import Stat from './Stat';
|
import Stat from './Stat';
|
||||||
import StartButton from './StartButton';
|
import StartButton from './StartButton';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
interface HeroProps {
|
interface HeroProps {
|
||||||
onStart: () => void;
|
onStart: () => void;
|
||||||
@@ -17,6 +18,23 @@ interface HeroProps {
|
|||||||
|
|
||||||
const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const t = useTranslations('Hero');
|
||||||
|
const tStats = useTranslations('Stats');
|
||||||
|
|
||||||
|
const getTranslatedLabel = (label: string) => {
|
||||||
|
switch (label) {
|
||||||
|
case 'Detection accuracy':
|
||||||
|
return tStats('accuracy');
|
||||||
|
case 'Documents checked':
|
||||||
|
return tStats('documents');
|
||||||
|
case 'Supported formats':
|
||||||
|
return tStats('formats');
|
||||||
|
case 'Report turnaround':
|
||||||
|
return tStats('turnaround');
|
||||||
|
default:
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: 'relative', overflow: 'hidden', background: C.bg }}>
|
<div style={{ position: 'relative', overflow: 'hidden', background: C.bg }}>
|
||||||
@@ -71,7 +89,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
|||||||
>
|
>
|
||||||
{/* Text block */}
|
{/* Text block */}
|
||||||
<div style={{ maxWidth: 600, flex: 1 }}>
|
<div style={{ maxWidth: 600, flex: 1 }}>
|
||||||
<Badge>Academic Integrity Platform</Badge>
|
<Badge>{t('badge')}</Badge>
|
||||||
|
|
||||||
<motion.div variants={stagger} initial="hidden" animate="visible">
|
<motion.div variants={stagger} initial="hidden" animate="visible">
|
||||||
<motion.h1
|
<motion.h1
|
||||||
@@ -86,7 +104,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
|||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Is Your Work
|
{t('mainHeading')}
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
variants={fadeUp(0.1)}
|
variants={fadeUp(0.1)}
|
||||||
@@ -101,7 +119,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
|||||||
marginBottom: 28,
|
marginBottom: 28,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Truly Original?
|
{t('mainHeadingItalic')}
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -117,10 +135,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
|||||||
marginBottom: 40,
|
marginBottom: 40,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Plagiarism is presenting someone else‘s ideas or words as
|
{t('description')}
|
||||||
your own. In academia and professional life, it carries serious
|
|
||||||
consequences. Our platform detects it in seconds — so you can
|
|
||||||
submit with full confidence.
|
|
||||||
</motion.p>
|
</motion.p>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -142,7 +157,7 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
|||||||
fontFamily: "'DM Mono', monospace",
|
fontFamily: "'DM Mono', monospace",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Certificate issued within 24h
|
{t('certificateNote')}
|
||||||
</span>
|
</span>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,9 +180,15 @@ const Hero: FC<HeroProps> = ({ onStart, blobY }) => {
|
|||||||
borderTop: `1px solid ${C.border}`,
|
borderTop: `1px solid ${C.border}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{STATS.map((s) => (
|
{STATS.map((s) => {
|
||||||
<Stat key={s.label} value={s.value} label={s.label} />
|
return (
|
||||||
))}
|
<Stat
|
||||||
|
key={s.label}
|
||||||
|
value={s.value}
|
||||||
|
label={getTranslatedLabel(s.label)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,8 +5,38 @@ import { C } from '../tokens';
|
|||||||
import { INFO_CARDS } from '../constants';
|
import { INFO_CARDS } from '../constants';
|
||||||
import InfoCard from './InfoCard';
|
import InfoCard from './InfoCard';
|
||||||
import Section from './Section';
|
import Section from './Section';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
const InfoSection: FC = () => (
|
const InfoSection: FC = () => {
|
||||||
|
const t = useTranslations('InfoSection');
|
||||||
|
const tCards = useTranslations('InfoCards');
|
||||||
|
|
||||||
|
const getTranslatedCard = (card: (typeof INFO_CARDS)[0]) => {
|
||||||
|
switch (card.title) {
|
||||||
|
case 'What is Plagiarism?':
|
||||||
|
return {
|
||||||
|
...card,
|
||||||
|
title: tCards('card1Title'),
|
||||||
|
text: tCards('card1Desc'),
|
||||||
|
};
|
||||||
|
case 'Why Check Your Document?':
|
||||||
|
return {
|
||||||
|
...card,
|
||||||
|
title: tCards('card2Title'),
|
||||||
|
text: tCards('card2Desc'),
|
||||||
|
};
|
||||||
|
case 'What You Get':
|
||||||
|
return {
|
||||||
|
...card,
|
||||||
|
title: tCards('card3Title'),
|
||||||
|
text: tCards('card3Desc'),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
<div style={{ background: C.surfaceWarm }}>
|
<div style={{ background: C.surfaceWarm }}>
|
||||||
<Section style={{ paddingTop: 96, paddingBottom: 96 }}>
|
<Section style={{ paddingTop: 96, paddingBottom: 96 }}>
|
||||||
{/* Heading */}
|
{/* Heading */}
|
||||||
@@ -27,7 +57,7 @@ const InfoSection: FC = () => (
|
|||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Why It Matters
|
{t('label')}
|
||||||
</p>
|
</p>
|
||||||
<h2
|
<h2
|
||||||
style={{
|
style={{
|
||||||
@@ -38,18 +68,19 @@ const InfoSection: FC = () => (
|
|||||||
lineHeight: 1.2,
|
lineHeight: 1.2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Understanding Plagiarism
|
{t('heading')}
|
||||||
</h2>
|
</h2>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Cards */}
|
{/* Cards */}
|
||||||
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: 20, flexWrap: 'wrap' }}>
|
||||||
{INFO_CARDS.map((card) => (
|
{INFO_CARDS.map((card) => (
|
||||||
<InfoCard key={card.title} {...card} />
|
<InfoCard key={card.title} {...getTranslatedCard(card)} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default InfoSection;
|
export default InfoSection;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { useState, type FC } from 'react';
|
import { useState, type FC } from 'react';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { C } from '../tokens';
|
import { C } from '../tokens';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
interface StartButtonProps {
|
interface StartButtonProps {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
@@ -10,6 +11,7 @@ interface StartButtonProps {
|
|||||||
|
|
||||||
const StartButton: FC<StartButtonProps> = ({ onClick, small = false }) => {
|
const StartButton: FC<StartButtonProps> = ({ onClick, small = false }) => {
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
|
const t = useTranslations('Common');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.button
|
<motion.button
|
||||||
@@ -35,7 +37,7 @@ const StartButton: FC<StartButtonProps> = ({ onClick, small = false }) => {
|
|||||||
: `0 2px 8px ${C.accent}22`,
|
: `0 2px 8px ${C.accent}22`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Start Checking →
|
{t('startButton')}
|
||||||
</motion.button>
|
</motion.button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { fadeUp } from '../animations';
|
|||||||
import { C } from '../tokens';
|
import { C } from '../tokens';
|
||||||
import { STEPS } from '../constants';
|
import { STEPS } from '../constants';
|
||||||
import type { Step } from '../types';
|
import type { Step } from '../types';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
interface StepCardProps {
|
interface StepCardProps {
|
||||||
step: Step;
|
step: Step;
|
||||||
@@ -14,6 +15,45 @@ const StepCard: FC<StepCardProps> = ({ step, index }) => {
|
|||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const inView = useInView(ref, { once: true, margin: '-50px' });
|
const inView = useInView(ref, { once: true, margin: '-50px' });
|
||||||
const isLast = index === STEPS.length - 1;
|
const isLast = index === STEPS.length - 1;
|
||||||
|
const t = useTranslations('Steps');
|
||||||
|
|
||||||
|
const getStepTitle = (num: string) => {
|
||||||
|
switch (num) {
|
||||||
|
case '01':
|
||||||
|
return t('step1Title');
|
||||||
|
case '02':
|
||||||
|
return t('step2Title');
|
||||||
|
case '03':
|
||||||
|
return t('step3Title');
|
||||||
|
case '04':
|
||||||
|
return t('step4Title');
|
||||||
|
case '05':
|
||||||
|
return t('step5Title');
|
||||||
|
case '06':
|
||||||
|
return t('step6Title');
|
||||||
|
default:
|
||||||
|
return step.title;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStepDesc = (num: string) => {
|
||||||
|
switch (num) {
|
||||||
|
case '01':
|
||||||
|
return t('step1Desc');
|
||||||
|
case '02':
|
||||||
|
return t('step2Desc');
|
||||||
|
case '03':
|
||||||
|
return t('step3Desc');
|
||||||
|
case '04':
|
||||||
|
return t('step4Desc');
|
||||||
|
case '05':
|
||||||
|
return t('step5Desc');
|
||||||
|
case '06':
|
||||||
|
return t('step6Desc');
|
||||||
|
default:
|
||||||
|
return step.desc;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -87,7 +127,7 @@ const StepCard: FC<StepCardProps> = ({ step, index }) => {
|
|||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{step.title}
|
{getStepTitle(step.num)}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
@@ -98,7 +138,7 @@ const StepCard: FC<StepCardProps> = ({ step, index }) => {
|
|||||||
margin: 0,
|
margin: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{step.desc}
|
{getStepDesc(step.num)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useIsMobile } from '../hooks/useIsMobile';
|
|||||||
import Section from './Section';
|
import Section from './Section';
|
||||||
import StartButton from './StartButton';
|
import StartButton from './StartButton';
|
||||||
import StepCard from './StepCard';
|
import StepCard from './StepCard';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
interface StepsSectionProps {
|
interface StepsSectionProps {
|
||||||
stepsRef: React.RefObject<HTMLDivElement | null>;
|
stepsRef: React.RefObject<HTMLDivElement | null>;
|
||||||
@@ -15,6 +16,7 @@ interface StepsSectionProps {
|
|||||||
|
|
||||||
const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const t = useTranslations('StepsSection');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -44,7 +46,7 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
|||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Process
|
{t('label')}
|
||||||
</p>
|
</p>
|
||||||
<h2
|
<h2
|
||||||
style={{
|
style={{
|
||||||
@@ -55,7 +57,7 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
|||||||
lineHeight: 1.2,
|
lineHeight: 1.2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
How It Works
|
{t('heading')}
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
@@ -65,7 +67,7 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
|||||||
lineHeight: 1.75,
|
lineHeight: 1.75,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Six simple steps from upload to certified report.
|
{t('description')}
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -112,10 +114,10 @@ const StepsSection: FC<StepsSectionProps> = ({ stepsRef, onScrollTop }) => {
|
|||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Ready to verify your document?
|
{t('ctaHeading')}
|
||||||
</h3>
|
</h3>
|
||||||
<p style={{ color: C.textMuted, fontSize: 13 }}>
|
<p style={{ color: C.textMuted, fontSize: 13 }}>
|
||||||
Get your originality certificate in under 24 hours.
|
{t('ctaDescription')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<StartButton onClick={onScrollTop} />
|
<StartButton onClick={onScrollTop} />
|
||||||
|
|||||||
@@ -2,10 +2,32 @@ import type { FC } from 'react';
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { C } from '../tokens';
|
import { C } from '../tokens';
|
||||||
import { TICKER_ITEMS } from '../constants';
|
import { TICKER_ITEMS } from '../constants';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
const Ticker: FC = () => {
|
const Ticker: FC = () => {
|
||||||
|
const t = useTranslations('Ticker');
|
||||||
const doubled = [...TICKER_ITEMS, ...TICKER_ITEMS];
|
const doubled = [...TICKER_ITEMS, ...TICKER_ITEMS];
|
||||||
|
|
||||||
|
const getTranslatedItem = (item: string, index: number) => {
|
||||||
|
const itemIndex = index % TICKER_ITEMS.length;
|
||||||
|
switch (itemIndex) {
|
||||||
|
case 0:
|
||||||
|
return t('item1');
|
||||||
|
case 1:
|
||||||
|
return t('item2');
|
||||||
|
case 2:
|
||||||
|
return t('item3');
|
||||||
|
case 3:
|
||||||
|
return t('item4');
|
||||||
|
case 4:
|
||||||
|
return t('item5');
|
||||||
|
case 5:
|
||||||
|
return t('item6');
|
||||||
|
default:
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -37,7 +59,7 @@ const Ticker: FC = () => {
|
|||||||
color: i % 3 === 0 ? C.textMid : C.textMuted,
|
color: i % 3 === 0 ? C.textMid : C.textMuted,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item}
|
{getTranslatedItem(item, i)}
|
||||||
<span style={{ marginLeft: 56, color: C.accent, opacity: 0.3 }}>
|
<span style={{ marginLeft: 56, color: C.accent, opacity: 0.3 }}>
|
||||||
✦
|
✦
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { PaymentStatus } from '../lib/types';
|
import { PaymentStatus } from '../lib/types';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
// ─── Payme Logo SVG ────────────────────────────────────────────────────────────
|
// ─── Payme Logo SVG ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -64,13 +65,14 @@ export const PaymeButton: React.FC<PaymeButtonProps> = ({
|
|||||||
status,
|
status,
|
||||||
}) => {
|
}) => {
|
||||||
const isLoading = status === 'loading';
|
const isLoading = status === 'loading';
|
||||||
|
const t = useTranslations('Payment');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
aria-busy={isLoading}
|
aria-busy={isLoading}
|
||||||
aria-label="Pay with Payme"
|
aria-label={t('payButton')}
|
||||||
className={`
|
className={`
|
||||||
w-full flex items-center justify-center gap-3
|
w-full flex items-center justify-center gap-3
|
||||||
rounded-xl px-6 py-4
|
rounded-xl px-6 py-4
|
||||||
@@ -87,7 +89,7 @@ export const PaymeButton: React.FC<PaymeButtonProps> = ({
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span>Connecting to Payme…</span>
|
<span>{t('connecting')}</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { PaymentModalProps } from '../lib/types';
|
|||||||
import { getPricing } from '../lib/utils';
|
import { getPricing } from '../lib/utils';
|
||||||
import { PriceSummary } from './Pricesummary';
|
import { PriceSummary } from './Pricesummary';
|
||||||
import { PaymeButton } from './Paymebutton';
|
import { PaymeButton } from './Paymebutton';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
// ─── Close Button ──────────────────────────────────────────────────────────────
|
// ─── Close Button ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -68,12 +69,14 @@ const CloseButton: React.FC<{ onClick: () => void }> = ({ onClick }) => (
|
|||||||
|
|
||||||
// ─── Security Badge ────────────────────────────────────────────────────────────
|
// ─── Security Badge ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const SecurityBadge: React.FC = () => (
|
const SecurityBadge: React.FC<{ securityText: string }> = ({
|
||||||
|
securityText,
|
||||||
|
}) => (
|
||||||
<div className="flex items-center justify-center gap-1.5 text-xs text-slate-400">
|
<div className="flex items-center justify-center gap-1.5 text-xs text-slate-400">
|
||||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z" />
|
<path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>Secured by Payme · SSL encrypted</span>
|
<span>{securityText}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -89,6 +92,7 @@ export const PaymentModal: React.FC<PaymentModalProps> = ({
|
|||||||
const dialogRef = useRef<HTMLDivElement>(null);
|
const dialogRef = useRef<HTMLDivElement>(null);
|
||||||
const pricing = getPricing();
|
const pricing = getPricing();
|
||||||
const status = isLoading ? 'loading' : 'idle';
|
const status = isLoading ? 'loading' : 'idle';
|
||||||
|
const t = useTranslations('Payment');
|
||||||
|
|
||||||
// ── Close on Escape ──────────────────────────────────────────────────────────
|
// ── Close on Escape ──────────────────────────────────────────────────────────
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -157,11 +161,9 @@ export const PaymentModal: React.FC<PaymentModalProps> = ({
|
|||||||
id="payment-modal-title"
|
id="payment-modal-title"
|
||||||
className="text-lg font-semibold text-slate-900"
|
className="text-lg font-semibold text-slate-900"
|
||||||
>
|
>
|
||||||
Payment
|
{t('title')}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-0.5 text-sm text-slate-500">
|
<p className="mt-0.5 text-sm text-slate-500">{t('description')}</p>
|
||||||
Review your order and pay securely
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -170,7 +172,7 @@ export const PaymentModal: React.FC<PaymentModalProps> = ({
|
|||||||
{/* Order details */}
|
{/* Order details */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-400 mb-3">
|
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-400 mb-3">
|
||||||
Order Summary
|
{t('orderSummary')}
|
||||||
</h3>
|
</h3>
|
||||||
<PriceSummary hasCertificate={hasCertificate} pricing={pricing} />
|
<PriceSummary hasCertificate={hasCertificate} pricing={pricing} />
|
||||||
</div>
|
</div>
|
||||||
@@ -187,20 +189,20 @@ export const PaymentModal: React.FC<PaymentModalProps> = ({
|
|||||||
>
|
>
|
||||||
<path d="M12 1l2.753 5.527 6.247.907-4.5 4.385 1.063 6.181L12 15.027l-5.563 2.973 1.063-6.181L3 7.434l6.247-.907z" />
|
<path d="M12 1l2.753 5.527 6.247.907-4.5 4.385 1.063 6.181L12 15.027l-5.563 2.973 1.063-6.181L3 7.434l6.247-.907z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>Certificate of completion included</span>
|
<span>{t('certificateIncluded')}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Payment method label */}
|
{/* Payment method label */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-400 mb-3">
|
<h3 className="text-xs font-semibold uppercase tracking-widest text-slate-400 mb-3">
|
||||||
Payment Method
|
{t('paymentMethod')}
|
||||||
</h3>
|
</h3>
|
||||||
<PaymeButton onClick={onConfirmPayment} status={status} />
|
<PaymeButton onClick={onConfirmPayment} status={status} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Security note */}
|
{/* Security note */}
|
||||||
<SecurityBadge />
|
<SecurityBadge securityText={t('security')} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { formatPrice } from '../lib/utils';
|
import { formatPrice } from '../lib/utils';
|
||||||
import { PriceSummaryProps } from '../lib/types';
|
import { PriceSummaryProps } from '../lib/types';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
// ─── Price Row ─────────────────────────────────────────────────────────────────
|
// ─── Price Row ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -53,25 +54,26 @@ export const PriceSummary: React.FC<PriceSummaryProps> = ({
|
|||||||
const total = hasCertificate
|
const total = hasCertificate
|
||||||
? pricing.serviceFee + pricing.certificateFee
|
? pricing.serviceFee + pricing.certificateFee
|
||||||
: pricing.serviceFee;
|
: pricing.serviceFee;
|
||||||
|
const t = useTranslations('Payment');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-xl bg-slate-50 border border-slate-100 px-5 py-2 space-y-0">
|
<div className="rounded-xl bg-slate-50 border border-slate-100 px-5 py-2 space-y-0">
|
||||||
<PriceRow
|
<PriceRow
|
||||||
label="Service fee"
|
label={t('serviceFee')}
|
||||||
amount={pricing.serviceFee}
|
amount={pricing.serviceFee}
|
||||||
currency={pricing.currency}
|
currency={pricing.currency}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{hasCertificate && (
|
{hasCertificate && (
|
||||||
<PriceRow
|
<PriceRow
|
||||||
label="Certificate"
|
label={t('certificateLabel')}
|
||||||
amount={pricing.certificateFee}
|
amount={pricing.certificateFee}
|
||||||
currency={pricing.currency}
|
currency={pricing.currency}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PriceRow
|
<PriceRow
|
||||||
label="Total"
|
label={t('total')}
|
||||||
amount={total}
|
amount={total}
|
||||||
currency={pricing.currency}
|
currency={pricing.currency}
|
||||||
highlight
|
highlight
|
||||||
|
|||||||
Reference in New Issue
Block a user