SI detail page cretaed
This commit is contained in:
10
src/app/[locale]/si/[id]/page.tsx
Normal file
10
src/app/[locale]/si/[id]/page.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import SiDetailPage from '@/widgets/detail/SiDetailPage';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
params: Promise<{ id: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function SiDetail({ params }: Props) {
|
||||||
|
const { id } = await params;
|
||||||
|
return <SiDetailPage id={Number(id)} />;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Download, CreditCard } from 'lucide-react';
|
import { Download, CreditCard, Eye } from 'lucide-react';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { useSiHistory } from '../../lib/hooks/useSiHistory';
|
import { useSiHistory } from '../../lib/hooks/useSiHistory';
|
||||||
import { formatDate } from '@/widgets/history/lib/utils';
|
import { formatDate } from '@/widgets/history/lib/utils';
|
||||||
@@ -9,6 +9,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 { SiButton } from '@/features/modals/siModal/page';
|
import { SiButton } from '@/features/modals/siModal/page';
|
||||||
|
import { useRouter, useParams } from 'next/navigation';
|
||||||
|
|
||||||
// ─── State badge ───────────────────────────────────────────────────────────────
|
// ─── State badge ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -89,6 +90,9 @@ const SiRow: React.FC<{ item: SiDocument; index: number }> = ({
|
|||||||
item,
|
item,
|
||||||
index,
|
index,
|
||||||
}) => {
|
}) => {
|
||||||
|
const router = useRouter();
|
||||||
|
const { locale } = useParams() as { locale: string };
|
||||||
|
|
||||||
const pay = useMutation({
|
const pay = useMutation({
|
||||||
mutationKey: ['si-payment', item.id],
|
mutationKey: ['si-payment', item.id],
|
||||||
mutationFn: () =>
|
mutationFn: () =>
|
||||||
@@ -168,7 +172,13 @@ const SiRow: React.FC<{ item: SiDocument; index: number }> = ({
|
|||||||
{pay.isPending ? '...' : "To'lash"}
|
{pay.isPending ? '...' : "To'lash"}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-slate-300 text-xs">—</span>
|
<button
|
||||||
|
onClick={() => router.push(`/${locale}/si/${item.id}`)}
|
||||||
|
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium text-blue-600 bg-blue-50 hover:bg-blue-100 active:scale-95 transition-all duration-150"
|
||||||
|
>
|
||||||
|
<Eye size={11} />
|
||||||
|
Ko'rish
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
356
src/widgets/detail/SiDetailPage.tsx
Normal file
356
src/widgets/detail/SiDetailPage.tsx
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import { apiRequest } from '@/shared/request/apiRequest';
|
||||||
|
import { links } from '@/shared/request/links';
|
||||||
|
import { Download, CloudDownload } from 'lucide-react';
|
||||||
|
|
||||||
|
// ── Types ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
type SiResult = {
|
||||||
|
original: number;
|
||||||
|
ai_possible: number;
|
||||||
|
ai: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SiDetail = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
file: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
state: 'paid' | 'unpaid';
|
||||||
|
total_words: number;
|
||||||
|
si_percantage: number | null;
|
||||||
|
result: SiResult | null;
|
||||||
|
file_size?: number;
|
||||||
|
file_extension?: string;
|
||||||
|
total_price?: number | string;
|
||||||
|
user?: { name: string; surname: string };
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function formatDate(iso: string): string {
|
||||||
|
return new Date(iso).toLocaleString('uz-UZ', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileExtension(url: string): string {
|
||||||
|
const name = url.split('/').pop() ?? '';
|
||||||
|
const ext = name.split('.').pop();
|
||||||
|
return ext ? `.${ext}` : '—';
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileName(url: string): string {
|
||||||
|
return url.split('/').pop() ?? '—';
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileSizeMb(bytes?: number): string {
|
||||||
|
if (!bytes) return '—';
|
||||||
|
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sub-components ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function InfoRow({ label, value }: { label: string; value: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between py-4 border-b border-slate-100 last:border-0">
|
||||||
|
<span className="text-sm font-medium text-slate-500">{label}</span>
|
||||||
|
<span className="text-sm font-semibold text-slate-800 text-right max-w-[60%] break-all">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SiBar({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
color,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
color: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<div className="flex items-center justify-between text-sm">
|
||||||
|
<span className="text-slate-600 font-medium">{label}</span>
|
||||||
|
<span className="font-bold text-slate-800">{value}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2.5 w-full bg-slate-100 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className={`h-full rounded-full transition-all duration-700 ${color}`}
|
||||||
|
style={{ width: `${value}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Skeleton ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Skeleton({ className }: { className?: string }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`animate-pulse bg-slate-200 rounded-lg ${className ?? ''}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadingSkeleton() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-slate-50">
|
||||||
|
<div className="h-16 bg-white border-b border-slate-200" />
|
||||||
|
<div className="max-w-4xl mx-auto px-6 py-10 space-y-6">
|
||||||
|
<Skeleton className="h-8 w-48" />
|
||||||
|
<div className="bg-white rounded-2xl border border-slate-100 p-6 space-y-4">
|
||||||
|
{Array.from({ length: 8 }).map((_, i) => (
|
||||||
|
<div key={i} className="flex justify-between">
|
||||||
|
<Skeleton className="h-4 w-40" />
|
||||||
|
<Skeleton className="h-4 w-32" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="bg-white rounded-2xl border border-slate-100 p-6 space-y-5">
|
||||||
|
{Array.from({ length: 3 }).map((_, i) => (
|
||||||
|
<Skeleton key={i} className="h-6 w-full" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Main Page ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export default function SiDetailPage({ id }: { id: number }) {
|
||||||
|
const { locale } = useParams() as { locale: string };
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(locale);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: doc,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ['si-detail', id],
|
||||||
|
queryFn: (): Promise<SiDetail> =>
|
||||||
|
apiRequest('GET', links.si_id(id)).then((res) => res.data as SiDetail),
|
||||||
|
enabled: !!id,
|
||||||
|
staleTime: 1000 * 60 * 5,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isLoading) return <LoadingSkeleton />;
|
||||||
|
|
||||||
|
if (isError || !doc) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-slate-50 flex items-center justify-center">
|
||||||
|
<div className="text-center space-y-3">
|
||||||
|
<p className="text-slate-700 font-semibold">
|
||||||
|
Ma'lumot topilmadi
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => window.history.back()}
|
||||||
|
className="text-sm text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
Ortga qaytish
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive SI percentages
|
||||||
|
const original = doc.result?.original ?? 100 - (doc.si_percantage ?? 0);
|
||||||
|
const aiPossible = doc.result?.ai_possible ?? 0;
|
||||||
|
const ai = doc.result?.ai ?? doc.si_percantage ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-slate-50 font-sans">
|
||||||
|
{/* ── Header ── */}
|
||||||
|
<header className="bg-white border-b border-slate-200 sticky top-0 z-20">
|
||||||
|
<div className="max-w-4xl mx-auto px-6 py-4 flex items-center gap-4">
|
||||||
|
<button
|
||||||
|
onClick={() => window.history.back()}
|
||||||
|
className="w-8 h-8 rounded-lg border border-slate-200 flex items-center justify-center hover:bg-slate-50 transition-colors shrink-0"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="w-4 h-4 text-slate-600"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={2}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M15 19l-7-7 7-7"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<h1 className="text-base font-bold text-slate-800 truncate">
|
||||||
|
{doc.title || 'SI tekshiruv'}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="max-w-4xl mx-auto px-6 py-8 space-y-6">
|
||||||
|
{/* ── Section 1: Asosiy ma'lumotlar ── */}
|
||||||
|
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
|
||||||
|
{/* Section header */}
|
||||||
|
<div className="bg-slate-50 border-b border-slate-100 px-6 py-4 text-center">
|
||||||
|
<h2 className="text-sm font-bold uppercase tracking-widest text-blue-600">
|
||||||
|
Asosiy ma'lumotlar
|
||||||
|
</h2>
|
||||||
|
<div className="w-10 h-0.5 bg-blue-600 mx-auto mt-1.5" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-6">
|
||||||
|
{/* Sub-header */}
|
||||||
|
<p className="text-[11px] font-bold uppercase tracking-widest text-slate-400 py-4 border-b border-slate-100">
|
||||||
|
Hujjat haqida ma'lumotlar
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<InfoRow
|
||||||
|
label="Hujjat nomi"
|
||||||
|
value={doc.title || fileName(doc.file)}
|
||||||
|
/>
|
||||||
|
{doc.user && (
|
||||||
|
<InfoRow
|
||||||
|
label="Tekshiruvchi"
|
||||||
|
value={`${doc.user.name} ${doc.user.surname}`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<InfoRow
|
||||||
|
label="Hujjat yuklangan vaqti"
|
||||||
|
value={formatDate(doc.created_at)}
|
||||||
|
/>
|
||||||
|
<InfoRow
|
||||||
|
label="Hujjat faylining original nomi"
|
||||||
|
value={fileName(doc.file)}
|
||||||
|
/>
|
||||||
|
<InfoRow
|
||||||
|
label="So'zlar soni"
|
||||||
|
value={
|
||||||
|
doc.total_words > 0
|
||||||
|
? doc.total_words.toLocaleString('uz-UZ')
|
||||||
|
: '—'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<InfoRow
|
||||||
|
label="Hujjat fayli kenggaytmasi"
|
||||||
|
value={fileExtension(doc.file)}
|
||||||
|
/>
|
||||||
|
{doc.file_size !== undefined && (
|
||||||
|
<InfoRow
|
||||||
|
label="Hujjat fayli o'lchami"
|
||||||
|
value={fileSizeMb(doc.file_size)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{doc.total_price !== undefined && (
|
||||||
|
<InfoRow
|
||||||
|
label="Yechilgan summa"
|
||||||
|
value={`${Number(doc.total_price).toLocaleString('uz-UZ')} so'm`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Download button */}
|
||||||
|
{doc.file && (
|
||||||
|
<div className="py-5 flex justify-end">
|
||||||
|
<a
|
||||||
|
href={doc.file}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl border border-cyan-500 text-cyan-600 text-sm font-semibold hover:bg-cyan-50 transition-colors"
|
||||||
|
>
|
||||||
|
<Download size={15} />
|
||||||
|
Original hujjatni yuklab olish
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ── Section 2: SI detektor natijalari ── */}
|
||||||
|
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
|
||||||
|
{/* Header row */}
|
||||||
|
<div className="px-6 py-5 flex items-start justify-between gap-4 border-b border-slate-100">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
{/* PDF icon */}
|
||||||
|
<div className="w-12 h-14 bg-red-100 rounded-lg flex items-center justify-center shrink-0">
|
||||||
|
<svg
|
||||||
|
className="w-7 h-7 text-red-500"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M7 2a2 2 0 00-2 2v16a2 2 0 002 2h10a2 2 0 002-2V8l-6-6H7zm5 1l5 5h-5V3z" />
|
||||||
|
<text
|
||||||
|
x="5"
|
||||||
|
y="20"
|
||||||
|
fontSize="5"
|
||||||
|
fill="white"
|
||||||
|
fontWeight="bold"
|
||||||
|
>
|
||||||
|
PDF
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-black text-slate-800 leading-snug">
|
||||||
|
Hujjatning SI detektori natijalari
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-slate-500 mt-1.5 leading-relaxed max-w-lg">
|
||||||
|
Ushbu oynada foydalanuvchi tomonidan yuklangan matn
|
||||||
|
sun'iy intellekt (SI) yordamida yozilgan bo'lish
|
||||||
|
ehtimoli bo'yicha tahlil natijalari aks etirilgan.
|
||||||
|
Detektor matnning stilistik, grammatik va semantik
|
||||||
|
xususiyatlarini baholab, uning qanchalik darajada sun'iy
|
||||||
|
intellekt tomonidan generatsiya qilingan bo'lishi
|
||||||
|
mumkinligini foizlik ko'rinishida ko'rsatadi.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{doc.file && (
|
||||||
|
<a
|
||||||
|
href={doc.file}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl bg-slate-800 text-white text-sm font-semibold hover:bg-slate-700 transition-colors shrink-0"
|
||||||
|
>
|
||||||
|
<CloudDownload size={16} />
|
||||||
|
Yuklab olish
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Bars */}
|
||||||
|
<div className="px-6 py-6 space-y-5">
|
||||||
|
<SiBar
|
||||||
|
label="Original matn"
|
||||||
|
value={original}
|
||||||
|
color="bg-emerald-400"
|
||||||
|
/>
|
||||||
|
<SiBar
|
||||||
|
label="Ehtimoliy Sun'iy Intellekt"
|
||||||
|
value={aiPossible}
|
||||||
|
color="bg-amber-400"
|
||||||
|
/>
|
||||||
|
<SiBar label="Sun'iy Intellekt" value={ai} color="bg-red-500" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -140,6 +140,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);
|
||||||
checkdocumentRequest.mutate(fd);
|
checkdocumentRequest.mutate(fd);
|
||||||
},
|
},
|
||||||
[form],
|
[form],
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import {
|
|||||||
} from './Plagiraismui';
|
} from './Plagiraismui';
|
||||||
import { usePlagiarismForm } from '../lib/usePlagiraism';
|
import { usePlagiarismForm } from '../lib/usePlagiraism';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { DOCUMENT_TYPES } from '@/features/modals/sertificateModal/types';
|
|
||||||
import { PaymentModal } from '@/features/modals/paymentModal/ui/Paymentmodal';
|
import { PaymentModal } from '@/features/modals/paymentModal/ui/Paymentmodal';
|
||||||
|
import DocumentsTypes from './documentsType';
|
||||||
|
|
||||||
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
|
||||||
bg-blue-50 border border-blue-200 rounded-xl
|
bg-blue-50 border border-blue-200 rounded-xl
|
||||||
placeholder:text-blue-400
|
placeholder:text-blue-400
|
||||||
@@ -177,26 +177,11 @@ export function PlagiarismCheckForm() {
|
|||||||
<div className="border-t border-stone-100" />
|
<div className="border-t border-stone-100" />
|
||||||
|
|
||||||
{/* Document type */}
|
{/* Document type */}
|
||||||
<FieldWrapper htmlFor="document_type" label="Hujjat turi">
|
<DocumentsTypes
|
||||||
<select
|
|
||||||
id="document_type"
|
|
||||||
value={form.document_type}
|
value={form.document_type}
|
||||||
onChange={(e) =>
|
onChange={setOption}
|
||||||
setOption(e.target.value as typeof form.document_type)
|
disabled={submission.status === 'success'}
|
||||||
}
|
/>
|
||||||
disabled={isLoading || submission.status === 'success'}
|
|
||||||
className={`${inputCls} cursor-pointer`}
|
|
||||||
>
|
|
||||||
<option value="" disabled>
|
|
||||||
Hujjat turini tanlang...
|
|
||||||
</option>
|
|
||||||
{DOCUMENT_TYPES.map((type) => (
|
|
||||||
<option key={type.value} value={type.value}>
|
|
||||||
{type.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</FieldWrapper>
|
|
||||||
|
|
||||||
{/* Submit */}
|
{/* Submit */}
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
|||||||
53
src/widgets/fileUpload/ui/documentsType.tsx
Normal file
53
src/widgets/fileUpload/ui/documentsType.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import { FieldWrapper } from './Plagiraismui';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { apiRequest } from '@/shared/request/apiRequest';
|
||||||
|
import { links } from '@/shared/request/links';
|
||||||
|
import { inputCls } from './Plagiraismcheckform';
|
||||||
|
|
||||||
|
type DocumentType = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DocumentsTypesProps {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DocumentsTypes({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
disabled,
|
||||||
|
}: DocumentsTypesProps) {
|
||||||
|
const { data, isLoading } = useQuery({
|
||||||
|
queryKey: ['document_types'],
|
||||||
|
queryFn: (): Promise<DocumentType[]> =>
|
||||||
|
apiRequest('GET', links.document_types).then(
|
||||||
|
(res) => res.data as DocumentType[],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldWrapper htmlFor="document_type" label="Hujjat turi">
|
||||||
|
<select
|
||||||
|
id="document_type"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
disabled={isLoading || disabled}
|
||||||
|
className={`${inputCls} cursor-pointer`}
|
||||||
|
>
|
||||||
|
<option value="" disabled>
|
||||||
|
{isLoading ? 'Yuklanmoqda...' : 'Hujjat turini tanlang...'}
|
||||||
|
</option>
|
||||||
|
{data?.map((type) => (
|
||||||
|
<option key={type.id} value={String(type.id)}>
|
||||||
|
{type.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</FieldWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user