plagiatcheck part complated base new request types
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { PlagiarismCheckForm } from '@/widgets/fileUpload/ui/Plagiraismcheckform';
|
|
||||||
import { HistoryPage } from '@/widgets/history';
|
import { HistoryPage } from '@/widgets/history';
|
||||||
|
import { PlagiarismCheckForm } from '@/widgets/plagiatCheck/ui/Plagiraismcheckform';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export interface PriceCalculate {
|
|||||||
service_fee: number;
|
service_fee: number;
|
||||||
discount?: number;
|
discount?: number;
|
||||||
total_price: number;
|
total_price: number;
|
||||||
currency: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PaymentModalProps {
|
export interface PaymentModalProps {
|
||||||
|
|||||||
@@ -59,21 +59,21 @@ export const PriceSummary = ({
|
|||||||
<PriceRow
|
<PriceRow
|
||||||
label={t('serviceFee')}
|
label={t('serviceFee')}
|
||||||
amount={priceCalculate.service_fee || 0}
|
amount={priceCalculate.service_fee || 0}
|
||||||
currency={priceCalculate.currency}
|
currency="UZS"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{priceCalculate.discount && (
|
{priceCalculate.discount && (
|
||||||
<PriceRow
|
<PriceRow
|
||||||
label={t('certificateLabel')}
|
label={t('discountLabel')}
|
||||||
amount={priceCalculate.discount}
|
amount={priceCalculate.discount}
|
||||||
currency={priceCalculate.currency}
|
currency="UZS"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PriceRow
|
<PriceRow
|
||||||
label={t('total')}
|
label={t('total')}
|
||||||
amount={priceCalculate.total_price}
|
amount={priceCalculate.total_price}
|
||||||
currency={priceCalculate.currency}
|
currency="UZS"
|
||||||
highlight
|
highlight
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { calculatePrice, DEFAULT_PRICING, formatPrice } from '../utils/pricing';
|
import { DEFAULT_PRICING, formatPrice } from '../utils/pricing';
|
||||||
import { FileUploadModalProps } from '../utils/tyeps';
|
import { FileUploadModalProps } from '../utils/tyeps';
|
||||||
import { useFileUpload } from '../utils/useFileUpload';
|
import { useFileUpload } from '../utils/useFileUpload';
|
||||||
import { SUPPORTED_EXTENSIONS } from '../utils/wordCount';
|
import { SUPPORTED_EXTENSIONS } from '../utils/wordCount';
|
||||||
@@ -28,6 +28,8 @@ export function FileUploadModal({
|
|||||||
handleDragLeave,
|
handleDragLeave,
|
||||||
handleRemoveFile,
|
handleRemoveFile,
|
||||||
openFilePicker,
|
openFilePicker,
|
||||||
|
canSubmit,
|
||||||
|
handleSubmit,
|
||||||
} = useFileUpload();
|
} = useFileUpload();
|
||||||
|
|
||||||
// Close on Escape key
|
// Close on Escape key
|
||||||
@@ -48,17 +50,8 @@ export function FileUploadModal({
|
|||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
const canSubmit =
|
const wordCount = uploadedFile?.word_count ?? 0;
|
||||||
documentName.trim().length > 0 &&
|
const totalPrice = uploadedFile?.total_price ?? 0;
|
||||||
uploadedFile?.status === 'done' &&
|
|
||||||
!isProcessing;
|
|
||||||
|
|
||||||
const wordCount = uploadedFile?.wordCount ?? 0;
|
|
||||||
const totalPrice = calculatePrice(wordCount, pricing);
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (!canSubmit || !uploadedFile) return;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Backdrop
|
// Backdrop
|
||||||
|
|||||||
@@ -5,14 +5,6 @@ const DEFAULT_PRICING: PricingConfig = {
|
|||||||
minimumPayment: 10000, // 10 000 so'm minimum
|
minimumPayment: 10000, // 10 000 so'm minimum
|
||||||
};
|
};
|
||||||
|
|
||||||
export function calculatePrice(
|
|
||||||
wordCount: number,
|
|
||||||
config: PricingConfig = DEFAULT_PRICING,
|
|
||||||
): number {
|
|
||||||
const calculated = wordCount * config.pricePerWord;
|
|
||||||
return Math.max(calculated, config.minimumPayment);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatPrice(amount: number): string {
|
export function formatPrice(amount: number): string {
|
||||||
return new Intl.NumberFormat('uz-UZ').format(amount) + " so'm";
|
return new Intl.NumberFormat('uz-UZ').format(amount) + " so'm";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ export interface UploadedFile {
|
|||||||
file: File;
|
file: File;
|
||||||
name: string;
|
name: string;
|
||||||
sizeKB: number;
|
sizeKB: number;
|
||||||
wordCount: number;
|
word_count: number;
|
||||||
|
total_price: number;
|
||||||
status: 'uploading' | 'done' | 'error';
|
status: 'uploading' | 'done' | 'error';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,30 @@
|
|||||||
|
|
||||||
import { useState, useCallback, useRef } from 'react';
|
import { useState, useCallback, useRef } from 'react';
|
||||||
import { UploadedFile } from './tyeps';
|
import { UploadedFile } from './tyeps';
|
||||||
import { countWordsFromFile, SUPPORTED_EXTENSIONS } from './wordCount';
|
import { SUPPORTED_EXTENSIONS } from './wordCount';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
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 { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
// ── API response types ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface WordCountApiResponse {
|
||||||
|
word_count: number;
|
||||||
|
total_price: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateSIOrderResponse {
|
||||||
|
id: number;
|
||||||
|
order_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SIPaymentResponse {
|
||||||
|
payment_link: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Return type ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
interface UseFileUploadReturn {
|
interface UseFileUploadReturn {
|
||||||
documentName: string;
|
documentName: string;
|
||||||
setDocumentName: (name: string) => void;
|
setDocumentName: (name: string) => void;
|
||||||
@@ -16,92 +34,135 @@ interface UseFileUploadReturn {
|
|||||||
isProcessing: boolean;
|
isProcessing: boolean;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
fileInputRef: React.RefObject<HTMLInputElement | null>;
|
fileInputRef: React.RefObject<HTMLInputElement | null>;
|
||||||
handleFileSelect: (file: File) => Promise<void>;
|
handleFileSelect: (file: File) => void;
|
||||||
handleDrop: (e: React.DragEvent) => void;
|
handleDrop: (e: React.DragEvent) => void;
|
||||||
handleDragOver: (e: React.DragEvent) => void;
|
handleDragOver: (e: React.DragEvent) => void;
|
||||||
handleDragLeave: () => void;
|
handleDragLeave: () => void;
|
||||||
handleRemoveFile: () => void;
|
handleRemoveFile: () => void;
|
||||||
openFilePicker: () => void;
|
openFilePicker: () => void;
|
||||||
|
handleSubmit: () => void;
|
||||||
|
canSubmit: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Hook ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function useFileUpload(): UseFileUploadReturn {
|
export function useFileUpload(): UseFileUploadReturn {
|
||||||
const [documentName, setDocumentName] = useState('');
|
const [documentName, setDocumentName] = useState('');
|
||||||
const [uploadedFile, setUploadedFile] = useState<UploadedFile | null>(null);
|
const [uploadedFile, setUploadedFile] = useState<UploadedFile | null>(null);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [isProcessing, setIsProcessing] = useState(false);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
const wordCount = useMutation({
|
// ── Step 3: Payment ────────────────────────────────────────────────────────
|
||||||
mutationFn: (data: FormData) => apiRequest('POST', links.si_create, data),
|
const siPayment = useMutation({
|
||||||
|
mutationKey: ['si-payment'],
|
||||||
|
mutationFn: (order_id: number) =>
|
||||||
|
apiRequest<SIPaymentResponse>('POST', links.si_payment(order_id)),
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
console.log(res);
|
window.open(res.data.payment_link, '_self');
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
console.log(err instanceof Error ? err.message : 'Unknown error');
|
toast.error(
|
||||||
toast.error(err instanceof Error ? err.message : 'Unknown error');
|
err instanceof Error ? err.message : "To'lovda xatolik yuz berdi",
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Step 2: Create SI order ────────────────────────────────────────────────
|
||||||
|
const createSIOrder = useMutation({
|
||||||
|
mutationKey: ['si-create'],
|
||||||
|
mutationFn: (data: FormData) =>
|
||||||
|
apiRequest<CreateSIOrderResponse>('POST', links.si_create, data),
|
||||||
|
onSuccess: (res) => {
|
||||||
|
siPayment.mutate(res.data.order_id);
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
toast.error(err instanceof Error ? err.message : 'Xatolik yuz berdi');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Step 1: Upload file & get word count + price ───────────────────────────
|
||||||
|
const wordCountMutation = useMutation({
|
||||||
|
mutationKey: ['si-word-count'],
|
||||||
|
mutationFn: (data: FormData) =>
|
||||||
|
apiRequest<WordCountApiResponse>('POST', links.wordCount, data),
|
||||||
|
onSuccess: (res) => {
|
||||||
|
setUploadedFile((prev) =>
|
||||||
|
prev
|
||||||
|
? {
|
||||||
|
...prev,
|
||||||
|
status: 'done',
|
||||||
|
word_count: res.data.word_count ?? 0,
|
||||||
|
total_price: res.data.total_price ?? 0,
|
||||||
|
}
|
||||||
|
: prev,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
const message = err instanceof Error ? err.message : 'Fayl yuklanmadi';
|
||||||
|
setError(message);
|
||||||
|
setUploadedFile((prev) => (prev ? { ...prev, status: 'error' } : prev));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── File validation ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const validateFile = (file: File): string | null => {
|
const validateFile = (file: File): string | null => {
|
||||||
const ext = '.' + file.name.split('.').pop()?.toLowerCase();
|
const ext = '.' + file.name.split('.').pop()?.toLowerCase();
|
||||||
if (!SUPPORTED_EXTENSIONS.includes(ext)) {
|
if (!SUPPORTED_EXTENSIONS.includes(ext)) {
|
||||||
return `Unsupported file type. Allowed: ${SUPPORTED_EXTENSIONS.join(', ')}`;
|
return `Qo'llab-quvvatlanmaydigan fayl turi. Ruxsat etilgan: ${SUPPORTED_EXTENSIONS.join(', ')}`;
|
||||||
}
|
}
|
||||||
if (file.size > 50 * 1024 * 1024) {
|
if (file.size > 50 * 1024 * 1024) {
|
||||||
return 'File size must be less than 50 MB';
|
return 'Fayl hajmi 50 MB dan oshmasligi kerak';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileSelect = useCallback(async (file: File) => {
|
// ── Step 1 trigger ─────────────────────────────────────────────────────────
|
||||||
setError(null);
|
|
||||||
|
|
||||||
const validationError = validateFile(file);
|
const handleFileSelect = useCallback(
|
||||||
if (validationError) {
|
(file: File) => {
|
||||||
setError(validationError);
|
setError(null);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimistic UI: show file immediately
|
const validationError = validateFile(file);
|
||||||
const optimistic: UploadedFile = {
|
if (validationError) {
|
||||||
file,
|
setError(validationError);
|
||||||
name: file.name,
|
return;
|
||||||
sizeKB: Math.round(file.size / 1024),
|
}
|
||||||
wordCount: 0,
|
|
||||||
status: 'uploading',
|
|
||||||
};
|
|
||||||
setUploadedFile(optimistic);
|
|
||||||
setIsProcessing(true);
|
|
||||||
|
|
||||||
// Auto-fill document name if empty
|
// Optimistic: show file chip immediately while backend responds
|
||||||
setDocumentName((prev) =>
|
|
||||||
prev.trim() === '' ? file.name.replace(/\.[^/.]+$/, '') : prev,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Count words on the frontend (no round-trip needed)
|
|
||||||
const result = await countWordsFromFile(file);
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
setError(result.error);
|
|
||||||
setUploadedFile({ ...optimistic, status: 'error', wordCount: 0 });
|
|
||||||
} else {
|
|
||||||
setUploadedFile({
|
setUploadedFile({
|
||||||
...optimistic,
|
file,
|
||||||
status: 'done',
|
name: file.name,
|
||||||
wordCount: result.count,
|
sizeKB: Math.round(file.size / 1024),
|
||||||
|
word_count: 0,
|
||||||
|
total_price: 0,
|
||||||
|
status: 'uploading',
|
||||||
});
|
});
|
||||||
}
|
|
||||||
console.log('running');
|
// Auto-fill document name if blank
|
||||||
if (!file) return;
|
setDocumentName((prev) =>
|
||||||
console.log('running inner');
|
prev.trim() === '' ? file.name.replace(/\.[^/.]+$/, '') : prev,
|
||||||
|
);
|
||||||
|
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append('file', file);
|
||||||
|
wordCountMutation.mutate(fd);
|
||||||
|
},
|
||||||
|
[wordCountMutation],
|
||||||
|
);
|
||||||
|
|
||||||
|
// ── Step 2 trigger (Check button) ─────────────────────────────────────────
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(() => {
|
||||||
|
if (!uploadedFile?.file || !documentName.trim()) return;
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('title', file.name.replace(/\.[^/.]+$/, ''));
|
fd.append('title', documentName.trim());
|
||||||
fd.append('file', file);
|
fd.append('file', uploadedFile.file);
|
||||||
wordCount.mutate(fd);
|
createSIOrder.mutate(fd);
|
||||||
console.log('stop');
|
}, [uploadedFile, documentName, createSIOrder]);
|
||||||
setIsProcessing(false);
|
|
||||||
}, []);
|
// ── Drag & drop ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const handleDrop = useCallback(
|
const handleDrop = useCallback(
|
||||||
(e: React.DragEvent) => {
|
(e: React.DragEvent) => {
|
||||||
@@ -132,6 +193,18 @@ export function useFileUpload(): UseFileUploadReturn {
|
|||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// ── Derived state ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const isProcessing =
|
||||||
|
wordCountMutation.isPending ||
|
||||||
|
createSIOrder.isPending ||
|
||||||
|
siPayment.isPending;
|
||||||
|
|
||||||
|
const canSubmit =
|
||||||
|
documentName.trim().length > 0 &&
|
||||||
|
uploadedFile?.status === 'done' &&
|
||||||
|
!isProcessing;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
documentName,
|
documentName,
|
||||||
setDocumentName,
|
setDocumentName,
|
||||||
@@ -146,5 +219,7 @@ export function useFileUpload(): UseFileUploadReturn {
|
|||||||
handleDragLeave,
|
handleDragLeave,
|
||||||
handleRemoveFile,
|
handleRemoveFile,
|
||||||
openFilePicker,
|
openFilePicker,
|
||||||
|
handleSubmit,
|
||||||
|
canSubmit,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@
|
|||||||
"paymentMethod": "Payment Method",
|
"paymentMethod": "Payment Method",
|
||||||
"security": "Secured by Payme · SSL encrypted",
|
"security": "Secured by Payme · SSL encrypted",
|
||||||
"serviceFee": "Service fee",
|
"serviceFee": "Service fee",
|
||||||
"certificateLabel": "Certificate",
|
"discountLabel": "Discount",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"paymentRequired": "Payment not completed",
|
"paymentRequired": "Payment not completed",
|
||||||
"connecting": "Connecting to Payme…",
|
"connecting": "Connecting to Payme…",
|
||||||
|
|||||||
@@ -230,7 +230,7 @@
|
|||||||
"paymentMethod": "Способ оплаты",
|
"paymentMethod": "Способ оплаты",
|
||||||
"security": "Защищено Payme · SSL шифрование",
|
"security": "Защищено Payme · SSL шифрование",
|
||||||
"serviceFee": "Стоимость услуги",
|
"serviceFee": "Стоимость услуги",
|
||||||
"certificateLabel": "Сертификат",
|
"discountLabel": "Скидка",
|
||||||
"total": "Итого",
|
"total": "Итого",
|
||||||
"paymentRequired": "Оплата не произведена",
|
"paymentRequired": "Оплата не произведена",
|
||||||
"connecting": "Подключение к Payme…",
|
"connecting": "Подключение к Payme…",
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ declare const messages: {
|
|||||||
paymentMethod: "To'lov usuli";
|
paymentMethod: "To'lov usuli";
|
||||||
security: 'Payme tomonidan himoyalangan · SSL shifrlash';
|
security: 'Payme tomonidan himoyalangan · SSL shifrlash';
|
||||||
serviceFee: "Xizmat to'lovi";
|
serviceFee: "Xizmat to'lovi";
|
||||||
certificateLabel: 'Sertifikat';
|
discountLabel: 'Chegirma';
|
||||||
total: 'Jami';
|
total: 'Jami';
|
||||||
paymentRequired: "To'lov qilinmagan";
|
paymentRequired: "To'lov qilinmagan";
|
||||||
connecting: 'Paymega ulanmoqda…';
|
connecting: 'Paymega ulanmoqda…';
|
||||||
|
|||||||
@@ -231,7 +231,7 @@
|
|||||||
"paymentMethod": "To'lov usuli",
|
"paymentMethod": "To'lov usuli",
|
||||||
"security": "Payme tomonidan himoyalangan · SSL shifrlash",
|
"security": "Payme tomonidan himoyalangan · SSL shifrlash",
|
||||||
"serviceFee": "Xizmat to'lovi",
|
"serviceFee": "Xizmat to'lovi",
|
||||||
"certificateLabel": "Sertifikat",
|
"discountLabel": "Chegirma",
|
||||||
"total": "Jami",
|
"total": "Jami",
|
||||||
"paymentRequired":"To'lov qilinmagan",
|
"paymentRequired":"To'lov qilinmagan",
|
||||||
"connecting": "Paymega ulanmoqda…",
|
"connecting": "Paymega ulanmoqda…",
|
||||||
|
|||||||
@@ -9,9 +9,12 @@ export const links = {
|
|||||||
`/shared/certificate/${document_id}/pdf/`,
|
`/shared/certificate/${document_id}/pdf/`,
|
||||||
si: '/shared/ai_document/list/',
|
si: '/shared/ai_document/list/',
|
||||||
si_id: (si_id: number) => `/shared/ai_document/list/${si_id}/`,
|
si_id: (si_id: number) => `/shared/ai_document/list/${si_id}/`,
|
||||||
si_payment: (document_id: number) => `/shared/ai_document/pay/${document_id}`,
|
si_payment: (document_id: number) =>
|
||||||
|
`/shared/ai_document/pay/${document_id}/`,
|
||||||
si_create: '/shared/ai_document/create/',
|
si_create: '/shared/ai_document/create/',
|
||||||
document_types: '/shared/document_types/',
|
document_types: '/shared/document_types/',
|
||||||
pay_history: '/shared/orders/all/',
|
pay_history: '/shared/orders/all/',
|
||||||
statistics: '/shared/statistics/',
|
statistics: '/shared/statistics/',
|
||||||
|
wordCount: '/shared/check_file/',
|
||||||
|
users: '/users/profile/',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// ─── Domain Types ───────────────────────────────────────────────────────────
|
// ─── Domain Types ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
import { PriceCalculate } from '@/features/modals/paymentModal/lib/types';
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -21,6 +23,11 @@ export interface PlagiarismSubmissionResponse {
|
|||||||
certificateUrl?: string;
|
certificateUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CheckDocumentRequestResponse extends PriceCalculate {
|
||||||
|
id: number;
|
||||||
|
order_id: number;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Form State Types ────────────────────────────────────────────────────────
|
// ─── Form State Types ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export interface PlagiarismFormState {
|
export interface PlagiarismFormState {
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
|
CheckDocumentRequestResponse,
|
||||||
PlagiarismFormErrors,
|
PlagiarismFormErrors,
|
||||||
PlagiarismFormState,
|
PlagiarismFormState,
|
||||||
SubmissionState,
|
SubmissionState,
|
||||||
@@ -11,6 +12,7 @@ import { useUserPlagiatStore } from '@/shared/zustand/user';
|
|||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { links } from '@/shared/request/links';
|
import { links } from '@/shared/request/links';
|
||||||
import { apiRequest } from '@/shared/request/apiRequest';
|
import { apiRequest } from '@/shared/request/apiRequest';
|
||||||
|
import { PriceCalculate } from '@/features/modals/paymentModal/lib/types';
|
||||||
|
|
||||||
// ─── Initial States ──────────────────────────────────────────────────────────
|
// ─── Initial States ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -23,6 +25,12 @@ const INITIAL_FORM: PlagiarismFormState = {
|
|||||||
document_type: 'boshqa',
|
document_type: 'boshqa',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PRICE: PriceCalculate = {
|
||||||
|
service_fee: 0,
|
||||||
|
discount: 0,
|
||||||
|
total_price: 0,
|
||||||
|
};
|
||||||
|
|
||||||
const INITIAL_SUBMISSION: SubmissionState = {
|
const INITIAL_SUBMISSION: SubmissionState = {
|
||||||
status: 'idle',
|
status: 'idle',
|
||||||
error: null,
|
error: null,
|
||||||
@@ -51,9 +59,8 @@ export function usePlagiarismForm() {
|
|||||||
const [isPaymentOpen, setIsPaymentOpen] = useState(false);
|
const [isPaymentOpen, setIsPaymentOpen] = useState(false);
|
||||||
const [submission, setSubmission] =
|
const [submission, setSubmission] =
|
||||||
useState<SubmissionState>(INITIAL_SUBMISSION);
|
useState<SubmissionState>(INITIAL_SUBMISSION);
|
||||||
// const route = useRouter();
|
|
||||||
// const [document_id, setDocument_id] = useState<number>(0);
|
|
||||||
const [order_id, setOrder_id] = useState<number>(0);
|
const [order_id, setOrder_id] = useState<number>(0);
|
||||||
|
const [prices, setPrices] = useState<PriceCalculate>(PRICE);
|
||||||
|
|
||||||
const checkdocumentRequest = useMutation({
|
const checkdocumentRequest = useMutation({
|
||||||
mutationKey: ['plagiarismCheck'],
|
mutationKey: ['plagiarismCheck'],
|
||||||
@@ -61,9 +68,14 @@ export function usePlagiarismForm() {
|
|||||||
apiRequest('POST', links.plagiarismCheck, data),
|
apiRequest('POST', links.plagiarismCheck, data),
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
console.log('uploda: ', res);
|
console.log('uploda: ', res);
|
||||||
const resdata = res.data as { id: number; order_id: number };
|
const resdata = res.data as CheckDocumentRequestResponse;
|
||||||
|
const priceInfo: PriceCalculate = {
|
||||||
|
total_price: resdata?.total_price || 0,
|
||||||
|
discount: resdata?.discount || 0,
|
||||||
|
service_fee: resdata?.service_fee || 0,
|
||||||
|
};
|
||||||
|
setPrices(priceInfo);
|
||||||
console.log('order_id:', resdata.id);
|
console.log('order_id:', resdata.id);
|
||||||
// setDocument_id(resdata.id);
|
|
||||||
setOrder_id(resdata.order_id);
|
setOrder_id(resdata.order_id);
|
||||||
setSubmission({ status: 'success', error: null });
|
setSubmission({ status: 'success', error: null });
|
||||||
setForm(INITIAL_FORM);
|
setForm(INITIAL_FORM);
|
||||||
@@ -83,7 +95,6 @@ export function usePlagiarismForm() {
|
|||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
console.log('payment res: ', res);
|
console.log('payment res: ', res);
|
||||||
window.open(res.data.payment_link, '_self');
|
window.open(res.data.payment_link, '_self');
|
||||||
//route.push(`/${document_id}`);
|
|
||||||
setIsPaymentOpen(false);
|
setIsPaymentOpen(false);
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (err) => {
|
||||||
@@ -140,7 +151,7 @@ export function usePlagiarismForm() {
|
|||||||
fd.append('file', form.file!); // File object — multipart/form-data
|
fd.append('file', form.file!); // File object — multipart/form-data
|
||||||
fd.append('certificate', String(form.certificate));
|
fd.append('certificate', String(form.certificate));
|
||||||
fd.append('total_price', '41200');
|
fd.append('total_price', '41200');
|
||||||
fd.append('document_type', form.document_type);
|
fd.append('type', form.document_type);
|
||||||
checkdocumentRequest.mutate(fd);
|
checkdocumentRequest.mutate(fd);
|
||||||
},
|
},
|
||||||
[form],
|
[form],
|
||||||
@@ -177,5 +188,6 @@ export function usePlagiarismForm() {
|
|||||||
setIsPaymentOpen,
|
setIsPaymentOpen,
|
||||||
isPaymentOpen,
|
isPaymentOpen,
|
||||||
setOption,
|
setOption,
|
||||||
|
prices,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -63,6 +63,7 @@ export function PlagiarismCheckForm() {
|
|||||||
isPaymentOpen,
|
isPaymentOpen,
|
||||||
setOption,
|
setOption,
|
||||||
setIsPaymentOpen,
|
setIsPaymentOpen,
|
||||||
|
prices,
|
||||||
} = usePlagiarismForm();
|
} = usePlagiarismForm();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -203,12 +204,7 @@ export function PlagiarismCheckForm() {
|
|||||||
<PaymentModal
|
<PaymentModal
|
||||||
isOpen={isPaymentOpen}
|
isOpen={isPaymentOpen}
|
||||||
onClose={() => setIsPaymentOpen(false)}
|
onClose={() => setIsPaymentOpen(false)}
|
||||||
price={{
|
price={prices}
|
||||||
service_fee: 41200,
|
|
||||||
discount: 5200,
|
|
||||||
total_price: 36000,
|
|
||||||
currency: 'UZS',
|
|
||||||
}}
|
|
||||||
onConfirmPayment={handleSubmit}
|
onConfirmPayment={handleSubmit}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
/>
|
/>
|
||||||
Reference in New Issue
Block a user