add service and sertificate prices ,
This commit is contained in:
@@ -53,7 +53,9 @@
|
|||||||
"certificateDescription": "An official certificate will be attached to your originality report.",
|
"certificateDescription": "An official certificate will be attached to your originality report.",
|
||||||
"submitting": "Submitting…",
|
"submitting": "Submitting…",
|
||||||
"submitButton": "Submit for Originality Check",
|
"submitButton": "Submit for Originality Check",
|
||||||
"dismiss": "Dismiss"
|
"dismiss": "Dismiss",
|
||||||
|
"service_price": "Service price {PLAGIAT_SERVICE_FEE} UZS",
|
||||||
|
"sertificate_price": "Certificate price {SERTIFICATE_PRICE} UZS"
|
||||||
},
|
},
|
||||||
"HistoryPage": {
|
"HistoryPage": {
|
||||||
"title": "Check History",
|
"title": "Check History",
|
||||||
@@ -63,8 +65,8 @@
|
|||||||
"date": "Date",
|
"date": "Date",
|
||||||
"amount": "Amount",
|
"amount": "Amount",
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
"fileName":"File name",
|
"fileName": "File name",
|
||||||
"count":"N_",
|
"count": "N_",
|
||||||
"actions": "",
|
"actions": "",
|
||||||
"state": "Payment status",
|
"state": "Payment status",
|
||||||
"emptyMessage": "No plagiarism checks found.",
|
"emptyMessage": "No plagiarism checks found.",
|
||||||
@@ -239,7 +241,7 @@
|
|||||||
"security": "Secured by Payme · SSL encrypted",
|
"security": "Secured by Payme · SSL encrypted",
|
||||||
"serviceFee": "Service fee",
|
"serviceFee": "Service fee",
|
||||||
"discountLabel": "Discount",
|
"discountLabel": "Discount",
|
||||||
"sertificateLabel":"Certificate",
|
"sertificateLabel": "Certificate",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"paymentRequired": "Payment not completed",
|
"paymentRequired": "Payment not completed",
|
||||||
"connecting": "Connecting to Payme…",
|
"connecting": "Connecting to Payme…",
|
||||||
|
|||||||
@@ -53,7 +53,9 @@
|
|||||||
"certificateDescription": "Официальный сертификат будет прикреплен к вашему отчету об оригинальности.",
|
"certificateDescription": "Официальный сертификат будет прикреплен к вашему отчету об оригинальности.",
|
||||||
"submitting": "Отправка…",
|
"submitting": "Отправка…",
|
||||||
"submitButton": "Отправить на проверку оригинальности",
|
"submitButton": "Отправить на проверку оригинальности",
|
||||||
"dismiss": "Закрыть"
|
"dismiss": "Закрыть",
|
||||||
|
"service_price": "Стоимость услуги {PLAGIAT_SERVICE_FEE} сум",
|
||||||
|
"sertificate_price": "Стоимость сертификата {SERTIFICATE_PRICE} сум"
|
||||||
},
|
},
|
||||||
"HistoryPage": {
|
"HistoryPage": {
|
||||||
"title": "История проверок",
|
"title": "История проверок",
|
||||||
@@ -63,7 +65,7 @@
|
|||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"amount": "Сумма",
|
"amount": "Сумма",
|
||||||
"result": "Результат",
|
"result": "Результат",
|
||||||
"count":"H_",
|
"count": "H_",
|
||||||
"actions": "",
|
"actions": "",
|
||||||
"state": "Статус оплаты",
|
"state": "Статус оплаты",
|
||||||
"emptyMessage": "Проверки на плагиат не найдены.",
|
"emptyMessage": "Проверки на плагиат не найдены.",
|
||||||
@@ -238,7 +240,7 @@
|
|||||||
"security": "Защищено Payme · SSL шифрование",
|
"security": "Защищено Payme · SSL шифрование",
|
||||||
"serviceFee": "Стоимость услуги",
|
"serviceFee": "Стоимость услуги",
|
||||||
"discountLabel": "Скидка",
|
"discountLabel": "Скидка",
|
||||||
"sertificateLabel":"Сертификат",
|
"sertificateLabel": "Сертификат",
|
||||||
"total": "Итого",
|
"total": "Итого",
|
||||||
"paymentRequired": "Оплата не произведена",
|
"paymentRequired": "Оплата не произведена",
|
||||||
"connecting": "Подключение к Payme…",
|
"connecting": "Подключение к Payme…",
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ declare const messages: {
|
|||||||
submitting: 'Yuborilmoqda…';
|
submitting: 'Yuborilmoqda…';
|
||||||
submitButton: 'Orijinallik tekshiruvi uchun yuborish';
|
submitButton: 'Orijinallik tekshiruvi uchun yuborish';
|
||||||
dismiss: 'Yopish';
|
dismiss: 'Yopish';
|
||||||
|
service_price: "Xizmat narxi {PLAGIAT_SERVICE_FEE} so'm";
|
||||||
|
sertificate_price: "Sertifikat narxi {SERTIFICATE_PRICE} so'm";
|
||||||
};
|
};
|
||||||
HistoryPage: {
|
HistoryPage: {
|
||||||
title: 'Tekshiruv tarixi';
|
title: 'Tekshiruv tarixi';
|
||||||
|
|||||||
@@ -53,15 +53,17 @@
|
|||||||
"certificateDescription": "Rasmiy sertifikat sizning orijinallik hisobotingizga ilova qilinadi.",
|
"certificateDescription": "Rasmiy sertifikat sizning orijinallik hisobotingizga ilova qilinadi.",
|
||||||
"submitting": "Yuborilmoqda…",
|
"submitting": "Yuborilmoqda…",
|
||||||
"submitButton": "Orijinallik tekshiruvi uchun yuborish",
|
"submitButton": "Orijinallik tekshiruvi uchun yuborish",
|
||||||
"dismiss": "Yopish"
|
"dismiss": "Yopish",
|
||||||
|
"service_price": "Xizmat narxi {PLAGIAT_SERVICE_FEE} so'm",
|
||||||
|
"sertificate_price": "Sertifikat narxi {SERTIFICATE_PRICE} so'm"
|
||||||
},
|
},
|
||||||
"HistoryPage": {
|
"HistoryPage": {
|
||||||
"title": "Tekshiruv tarixi",
|
"title": "Tekshiruv tarixi",
|
||||||
"description": "Siz tomonidan yuborilgan barcha plagiat tekshiruvlari",
|
"description": "Siz tomonidan yuborilgan barcha plagiat tekshiruvlari",
|
||||||
"sender": "Yuboruvchi",
|
"sender": "Yuboruvchi",
|
||||||
"file": "Fayl",
|
"file": "Fayl",
|
||||||
"fileName":"Fayl nomi",
|
"fileName": "Fayl nomi",
|
||||||
"count":"N_",
|
"count": "N_",
|
||||||
"date": "Sana",
|
"date": "Sana",
|
||||||
"amount": "Summa",
|
"amount": "Summa",
|
||||||
"result": "Natija",
|
"result": "Natija",
|
||||||
@@ -128,8 +130,8 @@
|
|||||||
"downloadCertificate": "Sertifikatni yuklab olish",
|
"downloadCertificate": "Sertifikatni yuklab olish",
|
||||||
"unknownError": "Noma'lum xato",
|
"unknownError": "Noma'lum xato",
|
||||||
"words": "so'z",
|
"words": "so'z",
|
||||||
"aiProbabilityText":"Ai yordamida yaratilganlik ehtimoli aniqlandi",
|
"aiProbabilityText": "Ai yordamida yaratilganlik ehtimoli aniqlandi",
|
||||||
"documentNumber":"Dokument mavzusi",
|
"documentNumber": "Dokument mavzusi",
|
||||||
"scoreAiContent": "O'zidan iqtibos keltirish",
|
"scoreAiContent": "O'zidan iqtibos keltirish",
|
||||||
"scoreOriginality": "Originallik",
|
"scoreOriginality": "Originallik",
|
||||||
"scorePlagiarism": "Plagiat",
|
"scorePlagiarism": "Plagiat",
|
||||||
@@ -239,9 +241,9 @@
|
|||||||
"security": "Payme tomonidan himoyalangan · SSL shifrlash",
|
"security": "Payme tomonidan himoyalangan · SSL shifrlash",
|
||||||
"serviceFee": "Xizmat to'lovi",
|
"serviceFee": "Xizmat to'lovi",
|
||||||
"discountLabel": "Chegirma",
|
"discountLabel": "Chegirma",
|
||||||
"sertificateLabel":"Sertifikat",
|
"sertificateLabel": "Sertifikat",
|
||||||
"total": "Jami",
|
"total": "Jami",
|
||||||
"paymentRequired":"To'lov qilinmagan",
|
"paymentRequired": "To'lov qilinmagan",
|
||||||
"connecting": "Paymega ulanmoqda…",
|
"connecting": "Paymega ulanmoqda…",
|
||||||
"payButton": "Payme orqali to'lash"
|
"payButton": "Payme orqali to'lash"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -143,11 +143,19 @@ api.interceptors.response.use(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const status = error.response?.status;
|
const status = error.response?.status;
|
||||||
|
// const responseData = error.response?.data as Record<string, unknown> | undefined;
|
||||||
const requestUrl = originalRequest.url ?? '';
|
const requestUrl = originalRequest.url ?? '';
|
||||||
const isAuthEndpoint =
|
const isAuthEndpoint =
|
||||||
requestUrl.includes('/users/login/') ||
|
requestUrl.includes('/users/login/') ||
|
||||||
requestUrl.includes('/users/register/');
|
requestUrl.includes('/users/register/');
|
||||||
|
|
||||||
|
// 403 with token_not_valid means the token is expired — clear and redirect
|
||||||
|
if (status === 403) {
|
||||||
|
TokenStorage.clear();
|
||||||
|
redirectToMain();
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
// For auth endpoints, 401 means wrong credentials — show error, don't refresh
|
// For auth endpoints, 401 means wrong credentials — show error, don't refresh
|
||||||
if (isAuthEndpoint || status !== 401 || originalRequest._retry) {
|
if (isAuthEndpoint || status !== 401 || originalRequest._retry) {
|
||||||
toast.error(extractErrorMessage(error));
|
toast.error(extractErrorMessage(error));
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Clock, XCircle, ReceiptText } from 'lucide-react';
|
import { Clock, XCircle, ReceiptText } from 'lucide-react';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { apiRequest } from '@/shared/request/apiRequest';
|
import { apiRequest } from '@/shared/request/apiRequest';
|
||||||
import { links } from '@/shared/request/links';
|
import { links } from '@/shared/request/links';
|
||||||
import PaymentStatus from '@/widgets/detail/paidStatus';
|
import PaymentStatus from '@/widgets/detail/paidStatus';
|
||||||
|
import { Pagination } from '@/widgets/history/ui/pagination';
|
||||||
// import { toast } from 'react-toastify';
|
// import { toast } from 'react-toastify';
|
||||||
// import { PaymentModal } from '@/features/modals/paymentModal/ui/Paymentmodal';
|
// import { PaymentModal } from '@/features/modals/paymentModal/ui/Paymentmodal';
|
||||||
|
|
||||||
@@ -36,9 +37,11 @@ function formatPrice(price: string) {
|
|||||||
|
|
||||||
// ─── Component ─────────────────────────────────────────────────────────────────
|
// ─── Component ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
export function PaymentsTable() {
|
export function PaymentsTable() {
|
||||||
const t = useTranslations('Cabinet');
|
const t = useTranslations('Cabinet');
|
||||||
// const [isPaymentOpen, setIsPaymentOpen] = useState(false);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ['pay_history'],
|
queryKey: ['pay_history'],
|
||||||
queryFn: (): Promise<Inspection[]> =>
|
queryFn: (): Promise<Inspection[]> =>
|
||||||
@@ -47,6 +50,12 @@ export function PaymentsTable() {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const totalPages = Math.ceil((data?.length ?? 0) / PAGE_SIZE);
|
||||||
|
const pageItems = data?.slice(
|
||||||
|
(currentPage - 1) * PAGE_SIZE,
|
||||||
|
currentPage * PAGE_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
// const payment = useMutation({
|
// const payment = useMutation({
|
||||||
// mutationKey: ['payload'],
|
// mutationKey: ['payload'],
|
||||||
// mutationFn: ({ order_id }: { order_id: number }) =>
|
// mutationFn: ({ order_id }: { order_id: number }) =>
|
||||||
@@ -93,6 +102,7 @@ export function PaymentsTable() {
|
|||||||
<p className="text-sm">{t('noPayments')}</p>
|
<p className="text-sm">{t('noPayments')}</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -115,9 +125,7 @@ export function PaymentsTable() {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-50">
|
<tbody className="divide-y divide-slate-50">
|
||||||
{data.map((row) => {
|
{pageItems!.map((row) => (
|
||||||
// const service_fee = row.total_price + row.discount;
|
|
||||||
return (
|
|
||||||
<tr
|
<tr
|
||||||
key={row.id}
|
key={row.id}
|
||||||
className="hover:bg-slate-50/60 transition-colors"
|
className="hover:bg-slate-50/60 transition-colors"
|
||||||
@@ -155,25 +163,17 @@ export function PaymentsTable() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
{/* <PaymentModal
|
|
||||||
isOpen={isPaymentOpen}
|
|
||||||
onClose={() => setIsPaymentOpen(false)}
|
|
||||||
price={{
|
|
||||||
service_fee: Number(service_fee),
|
|
||||||
discount: Number(row.discount) || 0,
|
|
||||||
total_price: Number(row.total_price) || 0,
|
|
||||||
}}
|
|
||||||
onConfirmPayment={() => {
|
|
||||||
handleSubmit({ document_id: 0 });
|
|
||||||
}}
|
|
||||||
isLoading={payment.isPending}
|
|
||||||
/> */}
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
totalPages={totalPages}
|
||||||
|
onPageChange={setCurrentPage}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Download, CreditCard, Eye } from 'lucide-react';
|
import { Download, CreditCard, Eye } from 'lucide-react';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
@@ -10,6 +10,7 @@ import { links } from '@/shared/request/links';
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import type { SiDocument } from '../../lib/types';
|
import type { SiDocument } from '../../lib/types';
|
||||||
import { useRouter, useParams } from 'next/navigation';
|
import { useRouter, useParams } from 'next/navigation';
|
||||||
|
import { Pagination } from '@/widgets/history/ui/pagination';
|
||||||
|
|
||||||
// ─── State badge ───────────────────────────────────────────────────────────────
|
// ─── State badge ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -180,9 +181,18 @@ const SiRow: React.FC<{ item: SiDocument; index: number }> = ({
|
|||||||
|
|
||||||
// ─── SiTable ───────────────────────────────────────────────────────────────────
|
// ─── SiTable ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
export const SiTable: React.FC = () => {
|
export const SiTable: React.FC = () => {
|
||||||
const t = useTranslations('Cabinet');
|
const t = useTranslations('Cabinet');
|
||||||
const { items, isLoading, isError } = useSiHistory();
|
const { items, isLoading, isError } = useSiHistory();
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(items.length / PAGE_SIZE);
|
||||||
|
const pageItems = items.slice(
|
||||||
|
(currentPage - 1) * PAGE_SIZE,
|
||||||
|
currentPage * PAGE_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -228,12 +238,21 @@ export const SiTable: React.FC = () => {
|
|||||||
{!isLoading && !isError && items.length === 0 && <EmptyState />}
|
{!isLoading && !isError && items.length === 0 && <EmptyState />}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
!isError &&
|
!isError &&
|
||||||
items.map((item, i) => (
|
pageItems.map((item, i) => (
|
||||||
<SiRow key={item.id} item={item} index={i + 1} />
|
<SiRow
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
index={(currentPage - 1) * PAGE_SIZE + i + 1}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<Pagination
|
||||||
|
currentPage={currentPage}
|
||||||
|
totalPages={totalPages}
|
||||||
|
onPageChange={setCurrentPage}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -36,14 +36,15 @@ export const useHistory = (pageSize = DEFAULT_PAGE_SIZE): UseHistoryReturn => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
const start = (currentPage - 1) * pageSize;
|
||||||
setState({
|
setState({
|
||||||
items: data?.results || [],
|
items: (data.results || []).slice(start, start + pageSize),
|
||||||
status: 'success',
|
status: 'success',
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
setTotal(data.total || 0);
|
setTotal(data.total || 0);
|
||||||
}
|
}
|
||||||
}, [data]);
|
}, [data, currentPage, pageSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refetch();
|
refetch();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useRouter } from '@/shared/config/i18n/navigation';
|
|||||||
const PlagiarismLanding = () => {
|
const PlagiarismLanding = () => {
|
||||||
const route = useRouter();
|
const route = useRouter();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const data = localStorage.getItem('user');
|
const data = localStorage.getItem('access_token');
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
route.push('/plagiat');
|
route.push('/plagiat');
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { usePlagiarismForm } from '../lib/usePlagiraism';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { PaymentModal } from '@/features/modals/paymentModal/ui/Paymentmodal';
|
import { PaymentModal } from '@/features/modals/paymentModal/ui/Paymentmodal';
|
||||||
import DocumentsTypes from './documentsType';
|
import DocumentsTypes from './documentsType';
|
||||||
|
import { PLAGIAT_SERVICE_FEE } from '@/shared/lib/metadata';
|
||||||
|
|
||||||
export const inputCls = `
|
export const inputCls = `
|
||||||
w-full px-3.5 py-3.5 text-[14px] text-slate-800
|
w-full px-3.5 py-3.5 text-[14px] text-slate-800
|
||||||
@@ -113,7 +114,7 @@ export function PlagiarismCheckForm() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* left part */}
|
{/* left part */}
|
||||||
<div className="flex flex-col gap-9 md:max-w-[50%] w-full">
|
<div className="flex flex-col gap-6 md:max-w-[50%] w-full">
|
||||||
{/* Topic */}
|
{/* Topic */}
|
||||||
<FieldWrapper
|
<FieldWrapper
|
||||||
label={t('documentTopic')}
|
label={t('documentTopic')}
|
||||||
@@ -131,6 +132,9 @@ export function PlagiarismCheckForm() {
|
|||||||
maxLength={200}
|
maxLength={200}
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
/>
|
/>
|
||||||
|
<p className="text-sm text-stone-500 ml-2 ">
|
||||||
|
{t('service_price', { PLAGIAT_SERVICE_FEE })}
|
||||||
|
</p>
|
||||||
</FieldWrapper>
|
</FieldWrapper>
|
||||||
|
|
||||||
{/* Sender Full Name (read-only) */}
|
{/* Sender Full Name (read-only) */}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { SERTIFICATE_PRICE } from '@/shared/lib/metadata';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
// ─── FieldWrapper ────────────────────────────────────────────────────────────
|
// ─── FieldWrapper ────────────────────────────────────────────────────────────
|
||||||
@@ -219,6 +221,7 @@ export function CertificateCheckbox({
|
|||||||
title = 'Return result with certificate',
|
title = 'Return result with certificate',
|
||||||
description = 'An official certificate will be attached to your originality report.',
|
description = 'An official certificate will be attached to your originality report.',
|
||||||
}: CertificateCheckboxProps) {
|
}: CertificateCheckboxProps) {
|
||||||
|
const t = useTranslations('PlagiarismCheck');
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={`
|
className={`
|
||||||
@@ -260,6 +263,9 @@ export function CertificateCheckbox({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-semibold text-stone-800">{title}</p>
|
<p className="text-sm font-semibold text-stone-800">{title}</p>
|
||||||
|
<p className="text-sm text-stone-500">
|
||||||
|
{t('sertificate_price', { SERTIFICATE_PRICE })}
|
||||||
|
</p>
|
||||||
<p className="text-xs text-stone-500 mt-0.5">{description}</p>
|
<p className="text-xs text-stone-500 mt-0.5">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
Reference in New Issue
Block a user