fix base_api error

This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-04-10 16:53:29 +05:00
parent 553db7194a
commit b4fd590eda
9 changed files with 151 additions and 58 deletions

View File

@@ -43,9 +43,9 @@ export function useCertificateModal({
const certificateMutation = useMutation({ const certificateMutation = useMutation({
mutationFn: (payload: CertificatePayload) => mutationFn: (payload: CertificatePayload) =>
apiRequest('POST', links.sertifikat(document_id), payload).then( apiRequest<ArrayBuffer>('POST', links.sertifikat(document_id), payload, {
(res) => res.data as ArrayBuffer, responseType: 'arraybuffer',
), }).then((res) => res.data),
onSuccess: (data: ArrayBuffer) => { onSuccess: (data: ArrayBuffer) => {
if (data) { if (data) {
const blob = new Blob([data], { type: 'application/pdf' }); const blob = new Blob([data], { type: 'application/pdf' });
@@ -54,7 +54,7 @@ export function useCertificateModal({
a.href = objectUrl; a.href = objectUrl;
a.download = `certificate-${document_id}.pdf`; a.download = `certificate-${document_id}.pdf`;
a.click(); a.click();
URL.revokeObjectURL(objectUrl); setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
} }
setSuccess(true); setSuccess(true);
setTimeout(() => { setTimeout(() => {

View File

@@ -45,7 +45,7 @@ function extractErrorMessage(error: AxiosError): string {
// ─── Constants ───────────────────────────────────────────────────────────────── // ─── Constants ─────────────────────────────────────────────────────────────────
// const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL; // const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
const baseUrl = 'https://dev-api.anti-plagiat.uz/api/v1'; const baseUrl = 'https://api.anti-plagiat.uz/api/v1';
const DEFAULT_LOCALE = 'uz'; // fallback locale for redirect const DEFAULT_LOCALE = 'uz'; // fallback locale for redirect
// ─── Token helpers ───────────────────────────────────────────────────────────── // ─── Token helpers ─────────────────────────────────────────────────────────────

View File

@@ -18,7 +18,7 @@ function NavigationMenu({
data-slot="navigation-menu" data-slot="navigation-menu"
data-viewport={viewport} data-viewport={viewport}
className={cn( className={cn(
'group/navigation-menu relative flex max-w-max flex-1 items-center justify-center', ' relative flex max-w-max flex-1 items-center justify-center',
className, className,
)} )}
{...props} {...props}

View File

@@ -0,0 +1,12 @@
import { CabinetSection } from '@/widgets/cabinet/lib/types';
import { create } from 'zustand';
type CabinetNavZustand = {
navItem: CabinetSection;
setNavItem: (item: string) => void;
};
export const useCabinetNav = create<CabinetNavZustand>((set) => ({
navItem: 'dashboard',
setNavItem: (item: string) => set({ navItem: item || 'dash' }),
}));

View File

@@ -1,14 +1,43 @@
'use client'; 'use client';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { useCabinetNav } from '@/shared/zustand/cabinetNav';
import type { CabinetSection } from '../types'; import type { CabinetSection } from '../types';
const VALID_SECTIONS: CabinetSection[] = [
'dashboard',
'plagiat',
'si',
'payments',
'profile',
];
export const useCabinet = () => { export const useCabinet = () => {
const { navItem, setNavItem } = useCabinetNav();
const navItemZustrand = useCabinetNav((state) => state.navItem);
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [activeSection, setActiveSection] = const [activeSection, setActiveSection] =
useState<CabinetSection>('dashboard'); useState<CabinetSection>('dashboard');
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
useEffect(() => {
if (navItemZustrand) {
const activeSection: CabinetSection = VALID_SECTIONS.includes(
navItemZustrand as CabinetSection,
)
? (navItemZustrand as CabinetSection)
: 'dashboard';
setActiveSection(activeSection);
} else {
const activeSection: CabinetSection = VALID_SECTIONS.includes(
navItem as CabinetSection,
)
? (navItem as CabinetSection)
: 'dashboard';
setActiveSection(activeSection);
}
}, [navItemZustrand]);
const navigate = (section: CabinetSection) => { const navigate = (section: CabinetSection) => {
setActiveSection(section); setNavItem(section);
setIsSidebarOpen(false); setIsSidebarOpen(false);
}; };

View File

@@ -10,7 +10,7 @@ import PaymentStatus from './paidStatus';
import Sertifikat from '@/features/modals/sertificateModal/sertifikat'; import Sertifikat from '@/features/modals/sertificateModal/sertifikat';
// ── Types ──────────────────────────────────────────────────────────────────── // ── Types ────────────────────────────────────────────────────────────────────
const baseUrl = 'https://dev-api.anti-plagiat.uz/api/v1'; const baseUrl = 'https://api.anti-plagiat.uz/api/v1';
interface AnalyzeText { interface AnalyzeText {
[key: string]: number | string; [key: string]: number | string;
} }
@@ -399,10 +399,25 @@ export default function DocumentDetailPage({ id }: { id: number }) {
<PaymentStatus status={doc.state} /> <PaymentStatus status={doc.state} />
<Sertifikat document_id={Number(id)} /> <Sertifikat document_id={Number(id)} />
{doc.file && ( {doc.file && (
<a <button
href={`${baseUrl}${doc.file}`} onClick={async () => {
target="_blank" const url = `${baseUrl}/shared/documents/${doc.id}/download`;
rel="noopener noreferrer" const res = await apiRequest<ArrayBuffer>(
'GET',
url,
undefined,
{ responseType: 'arraybuffer', baseURL: baseUrl },
);
const blob = new Blob([res.data], {
type: 'application/pdf',
});
const objectUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = objectUrl;
a.download = `document-${id}.pdf`;
a.click();
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
}}
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-900 text-white text-xs font-semibold hover:bg-slate-700 transition-colors" className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-900 text-white text-xs font-semibold hover:bg-slate-700 transition-colors"
> >
<svg <svg
@@ -419,7 +434,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
/> />
</svg> </svg>
{t('downloadPdf')} {t('downloadPdf')}
</a> </button>
)} )}
</div> </div>
</div> </div>
@@ -486,7 +501,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-8"> <div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-8">
<div className="flex flex-wrap justify-around gap-8"> <div className="flex flex-wrap justify-around gap-8">
<ScoreRing <ScoreRing
value={res.ai} value={res.originality}
label={t('scoreOriginality')} label={t('scoreOriginality')}
color="#10b981" color="#10b981"
/> />
@@ -502,7 +517,7 @@ export default function DocumentDetailPage({ id }: { id: number }) {
color="#6366f1" color="#6366f1"
/> />
<ScoreRing <ScoreRing
value={res.originality} value={res.ai}
label={t('scoreAiContent')} label={t('scoreAiContent')}
color="#ef4444" color="#ef4444"
/> />

View File

@@ -4,4 +4,5 @@ export interface MenuItem {
description?: string; description?: string;
icon?: React.ComponentType<{ className?: string }>; icon?: React.ComponentType<{ className?: string }>;
items?: MenuItem[]; items?: MenuItem[];
key: string;
} }

View File

@@ -1,3 +1,5 @@
'use client';
import { usePathname } from 'next/navigation';
import { MenuItem } from '../lib/model'; import { MenuItem } from '../lib/model';
const SubMenuLink = ({ const SubMenuLink = ({
@@ -7,11 +9,17 @@ const SubMenuLink = ({
item: MenuItem; item: MenuItem;
logOut?: () => void; logOut?: () => void;
}) => { }) => {
const pathname = usePathname();
const isCabinet = pathname.includes('/cabinet');
return ( return (
<a <a
className="flex flex-row gap-4 rounded-md p-3 leading-none no-underline transition-colors outline-none select-none hover:bg-muted hover:text-accent-foreground" className="flex flex-row gap-4 rounded-md p-3 leading-none no-underline transition-colors outline-none select-none hover:bg-muted hover:text-accent-foreground"
href={item.url} href={isCabinet && item.url === '/cabinet' ? undefined : item.url}
onClick={() => { onClick={(e) => {
if (isCabinet && item.url === '/cabinet') {
e.preventDefault();
}
if (logOut) { if (logOut) {
logOut(); logOut();
} }

View File

@@ -2,37 +2,70 @@
import { Link } from '@/shared/config/i18n/navigation'; import { Link } from '@/shared/config/i18n/navigation';
import { Button } from '@/shared/ui/button'; import { Button } from '@/shared/ui/button';
import { import {
NavigationMenu, DropdownMenu,
NavigationMenuContent, DropdownMenuContent,
NavigationMenuItem, DropdownMenuItem,
NavigationMenuLink, DropdownMenuTrigger,
NavigationMenuList, } from '@/shared/ui/dropdown-menu';
NavigationMenuTrigger,
} from '@/shared/ui/navigation-menu';
import SubMenuLink from './SubMenuLink'; import SubMenuLink from './SubMenuLink';
import { ChangeLang } from './ChangeLang'; import { ChangeLang } from './ChangeLang';
import { useLoginModal, useRegisterModal } from '@/shared/zustand/auth'; import { useLoginModal, useRegisterModal } from '@/shared/zustand/auth';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { useUserPlagiatStore } from '@/shared/zustand/user'; import { useUserPlagiatStore } from '@/shared/zustand/user';
import { LogOut, User } from 'lucide-react'; import {
ChevronDown,
LogOut,
User,
LayoutDashboard,
FileSearch,
BrainCircuit,
CreditCard,
} from 'lucide-react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useCabinetNav } from '@/shared/zustand/cabinetNav';
function AuthButtons() { function AuthButtons() {
const t = useTranslations('Navbar'); const t = useTranslations('Navbar');
const t_cab = useTranslations('Cabinet');
const setNavItem = useCabinetNav((state) => state.setNavItem);
const [localUser, setLocalUser] = useState<{ const [localUser, setLocalUser] = useState<{
id: number; id: number;
name: string; name: string;
surname: string; surname: string;
} | null>(null); } | null>(null);
const [open, setOpen] = useState(false);
const auth = { const auth = {
login: { title: t('login'), url: '#' }, login: { title: t('login'), url: '#' },
signup: { title: t('signup'), url: '#' }, signup: { title: t('signup'), url: '#' },
}; };
const userItem = [ const userItem = [
{ title: t('profile'), url: '/cabinet', icon: User }, { title: t('profile'), url: '/cabinet', icon: User, key: 'profile' },
{ title: t('logout'), url: '/', icon: LogOut }, {
url: '/cabinet',
title: t_cab('dashboard'),
icon: LayoutDashboard,
key: 'dashboard',
},
{
url: '/cabinet',
title: t_cab('plagiat'),
icon: FileSearch,
key: 'plagiat',
},
{
url: '/cabinet',
title: t_cab('siNav'),
icon: BrainCircuit,
key: 'si',
},
{
url: '/cabinet',
title: t_cab('payments'),
icon: CreditCard,
key: 'payments',
},
{ title: t('logout'), url: '/', icon: LogOut, key: 'logout' },
]; ];
const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal); const toggleLoginModal = useLoginModal((state) => state.toggleLoginModal);
@@ -51,7 +84,6 @@ function AuthButtons() {
useEffect(() => { useEffect(() => {
const data = localStorage.getItem('user'); const data = localStorage.getItem('user');
if (data) { if (data) {
setLocalUser(JSON.parse(data)); setLocalUser(JSON.parse(data));
} else { } else {
@@ -65,33 +97,29 @@ function AuthButtons() {
<div className="sm:flex hidden"> <div className="sm:flex hidden">
<ChangeLang /> <ChangeLang />
</div> </div>
<NavigationMenu viewport={true}> <DropdownMenu open={open} onOpenChange={setOpen}>
<NavigationMenuList> <DropdownMenuTrigger className="inline-flex items-center gap-1 text-lg font-medium outline-none">
<NavigationMenuItem> {localUser.name}
<NavigationMenuTrigger className="text-lg"> <ChevronDown className="size-4" />
{localUser.name} </DropdownMenuTrigger>
</NavigationMenuTrigger> <DropdownMenuContent className="">
<NavigationMenuContent className="bg-popover text-popover-foreground"> {userItem.map((subItem) => (
{userItem.map((subItem) => ( <DropdownMenuItem key={subItem.title} asChild>
<NavigationMenuLink <SubMenuLink
asChild logOut={() => {
key={subItem.title} setOpen(false);
className="w-80" if (subItem.url !== '/cabinet') {
> clearTokens();
<SubMenuLink } else {
logOut={() => { setNavItem(subItem.key);
if (subItem.url !== '/cabinet') { }
clearTokens(); }}
} item={subItem}
}} />
item={subItem} </DropdownMenuItem>
/> ))}
</NavigationMenuLink> </DropdownMenuContent>
))} </DropdownMenu>
</NavigationMenuContent>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div> </div>
); );
} }