diff --git a/modules/frontend/src/App.tsx b/modules/frontend/src/App.tsx
index 3ecfa2d..e2c909f 100644
--- a/modules/frontend/src/App.tsx
+++ b/modules/frontend/src/App.tsx
@@ -2,6 +2,7 @@ import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import UsersIdPage from "./pages/UsersIdPage/UsersIdPage";
import TitlesPage from "./pages/TitlesPage/TitlesPage";
+import TitlePage from "./pages/TitlePage/TitlePage";
import { LoginPage } from "./pages/LoginPage/LoginPage";
import { Header } from "./components/Header/Header";
@@ -24,7 +25,9 @@ const App: React.FC = () => {
/>
} />
+
} />
+ } />
);
diff --git a/modules/frontend/src/api/core/OpenAPI.ts b/modules/frontend/src/api/core/OpenAPI.ts
index 185e5c3..6ce873e 100644
--- a/modules/frontend/src/api/core/OpenAPI.ts
+++ b/modules/frontend/src/api/core/OpenAPI.ts
@@ -20,7 +20,7 @@ export type OpenAPIConfig = {
};
export const OpenAPI: OpenAPIConfig = {
- BASE: '/api/v1',
+ BASE: 'http://10.1.0.65:8081/api/v1',
VERSION: '1.0.0',
WITH_CREDENTIALS: false,
CREDENTIALS: 'include',
diff --git a/modules/frontend/src/auth/core/OpenAPI.ts b/modules/frontend/src/auth/core/OpenAPI.ts
index 2d0edf8..79aa305 100644
--- a/modules/frontend/src/auth/core/OpenAPI.ts
+++ b/modules/frontend/src/auth/core/OpenAPI.ts
@@ -20,7 +20,7 @@ export type OpenAPIConfig = {
};
export const OpenAPI: OpenAPIConfig = {
- BASE: '/auth',
+ BASE: 'http://10.1.0.65:8081/auth',
VERSION: '1.0.0',
WITH_CREDENTIALS: false,
CREDENTIALS: 'include',
diff --git a/modules/frontend/src/pages/TitlePage/TitlePage.tsx b/modules/frontend/src/pages/TitlePage/TitlePage.tsx
index e69de29..5ea0e3d 100644
--- a/modules/frontend/src/pages/TitlePage/TitlePage.tsx
+++ b/modules/frontend/src/pages/TitlePage/TitlePage.tsx
@@ -0,0 +1,140 @@
+import { useEffect, useState } from "react";
+import { useParams } from "react-router-dom";
+import { DefaultService } from "../../api/services/DefaultService";
+import type { Title, UserTitleStatus } from "../../api";
+import {
+ ClockIcon,
+ CheckCircleIcon,
+ PlayCircleIcon,
+ XCircleIcon,
+} from "@heroicons/react/24/solid";
+
+const STATUS_BUTTONS: { status: UserTitleStatus; icon: React.ReactNode; label: string }[] = [
+ { status: "planned", icon: , label: "Planned" },
+ { status: "finished", icon: , label: "Finished" },
+ { status: "in-progress", icon: , label: "In Progress" },
+ { status: "dropped", icon: , label: "Dropped" },
+];
+
+export default function TitlePage() {
+ const params = useParams();
+ const titleId = Number(params.id);
+
+ const [title, setTitle] = useState
(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const [userStatus, setUserStatus] = useState(null);
+ const [updatingStatus, setUpdatingStatus] = useState(false);
+
+ useEffect(() => {
+ const fetchTitle = async () => {
+ setLoading(true);
+ try {
+ const data = await DefaultService.getTitle(titleId, "all");
+ setTitle(data);
+ setError(null);
+ } catch (err: any) {
+ console.error(err);
+ setError(err?.message || "Failed to fetch title");
+ } finally {
+ setLoading(false);
+ }
+ };
+ fetchTitle();
+ }, [titleId]);
+
+ const handleStatusClick = async (status: UserTitleStatus) => {
+ if (updatingStatus || userStatus === status) return;
+
+ const userId = Number(localStorage.getItem("userId"));
+ if (!userId) {
+ alert("You must be logged in to set status.");
+ return;
+ }
+
+ setUpdatingStatus(true);
+ try {
+ await DefaultService.addUserTitle(userId, {
+ title_id: titleId,
+ status,
+ });
+ setUserStatus(status);
+ } catch (err: any) {
+ console.error(err);
+ alert(err?.message || "Failed to set status");
+ } finally {
+ setUpdatingStatus(false);
+ }
+ };
+
+ const getTagsString = () =>
+ title?.tags?.map(tag => tag.en).filter(Boolean).join(", ");
+
+ if (loading) return Loading title...
;
+ if (error) return {error}
;
+ if (!title) return null;
+
+ return (
+
+
+ {/* Постер */}
+
+
![{title.title_names?.en?.[0]]({title.poster?.image_path)
+
+ {/* Статус кнопки с иконками */}
+
+ {STATUS_BUTTONS.map(btn => (
+
+ ))}
+
+
+
+ {/* Информация о тайтле */}
+
+
+ {title.title_names?.en?.[0] || "Untitled"}
+
+ {title.studio &&
Studio: {title.studio.name}
}
+ {title.title_status &&
Status: {title.title_status}
}
+ {title.rating !== undefined && (
+
+ Rating: {title.rating} ({title.rating_count} votes)
+
+ )}
+ {title.release_year && (
+
+ Released: {title.release_year} {title.release_season || ""}
+
+ )}
+ {title.episodes_aired !== undefined && (
+
+ Episodes: {title.episodes_aired}/{title.episodes_all}
+
+ )}
+ {title.tags && title.tags.length > 0 && (
+
+ Tags: {getTagsString()}
+
+ )}
+
+
+
+ );
+}