diff --git a/.forgejo/workflows/build-and-deploy.yml b/.forgejo/workflows/build-and-deploy.yml index 0338440..e7d0a83 100644 --- a/.forgejo/workflows/build-and-deploy.yml +++ b/.forgejo/workflows/build-and-deploy.yml @@ -20,9 +20,9 @@ jobs: go-version: '^1.25' check-latest: false cache-dependency-path: | - go.sum + modules/backend/go.sum - - name: Build backend + - name: Build Go app run: | cd modules/backend go mod tidy @@ -35,19 +35,6 @@ jobs: name: nyanimedb-backend.tar.gz path: modules/backend/nyanimedb-backend.tar.gz - - name: Build auth - run: | - cd modules/auth - go mod tidy - go build -o auth . - tar -czvf nyanimedb-auth.tar.gz auth - - - name: Upload built auth to artifactory - uses: actions/upload-artifact@v3 - with: - name: nyanimedb-auth.tar.gz - path: modules/auth/nyanimedb-auth.tar.gz - # Build frontend - uses: actions/setup-node@v5 with: @@ -89,14 +76,6 @@ jobs: push: true tags: meowgit.nekoea.red/nihonium/nyanimedb-backend:latest - - name: Build and push auth image - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfiles/Dockerfile_auth - push: true - tags: meowgit.nekoea.red/nihonium/nyanimedb-auth:latest - - name: Build and push frontend image uses: docker/build-push-action@v6 with: diff --git a/api/_build/openapi.yaml b/api/_build/openapi.yaml index 720b686..e7482c1 100644 --- a/api/_build/openapi.yaml +++ b/api/_build/openapi.yaml @@ -11,52 +11,52 @@ paths: parameters: - $ref: '#/components/parameters/cursor' - $ref: '#/components/parameters/title_sort' - - name: sort_forward - in: query + - in: query + name: sort_forward schema: type: boolean default: true - - name: word - in: query + - in: query + name: word schema: type: string - - name: status - in: query - description: List of title statuses to filter + - in: query + name: status schema: type: array items: $ref: '#/components/schemas/TitleStatus' - explode: false + description: List of title statuses to filter style: form - - name: rating - in: query + explode: false + - in: query + name: rating schema: type: number format: double - - name: release_year - in: query + - in: query + name: release_year schema: type: integer format: int32 - - name: release_season - in: query + - in: query + name: release_season schema: $ref: '#/components/schemas/ReleaseSeason' - - name: limit - in: query + - in: query + name: limit schema: type: integer format: int32 default: 10 - - name: offset - in: query + - in: query + name: offset schema: type: integer format: int32 default: 0 - - name: fields - in: query + - in: query + name: fields schema: type: string default: all @@ -69,10 +69,10 @@ paths: type: object properties: data: - description: List of titles type: array items: $ref: '#/components/schemas/Title' + description: List of titles cursor: $ref: '#/components/schemas/CursorObj' required: @@ -86,17 +86,16 @@ paths: description: Unknown server error '/titles/{title_id}': get: - operationId: getTitle summary: Get title description parameters: - - name: title_id - in: path + - in: path + name: title_id required: true schema: type: integer format: int64 - - name: fields - in: query + - in: query + name: fields schema: type: string default: all @@ -117,16 +116,15 @@ paths: description: Unknown server error '/users/{user_id}': get: - operationId: getUsersId summary: Get user info parameters: - - name: user_id - in: path + - in: path + name: user_id required: true schema: type: string - - name: fields - in: query + - in: query + name: fields schema: type: string default: all @@ -144,59 +142,59 @@ paths: '500': description: Unknown server error patch: - operationId: updateUser summary: Partially update a user account description: | Update selected user profile fields (excluding password). Password updates must be done via the dedicated auth-service (`/auth/`). Fields not provided in the request body remain unchanged. + operationId: updateUser parameters: - name: user_id in: path - description: User ID (primary key) required: true schema: type: integer format: int64 + description: User ID (primary key) example: 123 requestBody: required: true content: application/json: schema: - description: Only provided fields are updated. Omitted fields remain unchanged. type: object properties: avatar_id: - description: ID of the user avatar (references `images.id`); set to `null` to remove avatar type: integer format: int64 - example: 42 nullable: true + description: ID of the user avatar (references `images.id`); set to `null` to remove avatar + example: 42 mail: - description: User email (must be unique and valid) type: string format: email - example: john.doe.updated@example.com pattern: '^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9_-]+$' + description: User email (must be unique and valid) + example: john.doe.updated@example.com nickname: - description: 'Username (alphanumeric + `_` or `-`, 3–16 chars)' type: string - example: john_doe_43 + pattern: '^[a-zA-Z0-9_-]{3,16}$' + description: 'Username (alphanumeric + `_` or `-`, 3–16 chars)' maxLength: 16 minLength: 3 - pattern: '^[a-zA-Z0-9_-]{3,16}$' + example: john_doe_43 disp_name: + type: string description: Display name - type: string - example: John Smith maxLength: 32 + example: John Smith user_desc: - description: User description / bio type: string - example: Just a curious developer. + description: User description / bio maxLength: 512 + example: Just a curious developer. additionalProperties: false + description: Only provided fields are updated. Omitted fields remain unchanged. responses: '200': description: User updated successfully. Returns updated user representation (excluding sensitive fields). @@ -224,64 +222,64 @@ paths: parameters: - $ref: '#/components/parameters/cursor' - $ref: '#/components/parameters/title_sort' - - name: user_id - in: path + - in: path + name: user_id required: true schema: type: string - - name: sort_forward - in: query + - in: query + name: sort_forward schema: type: boolean default: true - - name: word - in: query + - in: query + name: word schema: type: string - - name: status - in: query - description: List of title statuses to filter + - in: query + name: status schema: type: array items: $ref: '#/components/schemas/TitleStatus' - explode: false + description: List of title statuses to filter style: form - - name: watch_status - in: query + explode: false + - in: query + name: watch_status schema: type: array items: $ref: '#/components/schemas/UserTitleStatus' - explode: false style: form - - name: rating - in: query + explode: false + - in: query + name: rating schema: type: number format: double - - name: my_rate - in: query + - in: query + name: my_rate schema: type: integer format: int32 - - name: release_year - in: query + - in: query + name: release_year schema: type: integer format: int32 - - name: release_season - in: query + - in: query + name: release_season schema: $ref: '#/components/schemas/ReleaseSeason' - - name: limit - in: query + - in: query + name: limit schema: type: integer format: int32 default: 10 - - name: fields - in: query + - in: query + name: fields schema: type: string default: all @@ -311,17 +309,17 @@ paths: '500': description: Unknown server error post: - operationId: addUserTitle summary: Add a title to a user description: 'User adding title to list af watched, status required' + operationId: addUserTitle parameters: - name: user_id in: path - description: ID of the user to assign the title to required: true schema: type: integer format: int64 + description: ID of the user to assign the title to example: 123 requestBody: required: true @@ -329,6 +327,9 @@ paths: application/json: schema: type: object + required: + - title_id + - status properties: title_id: type: integer @@ -338,16 +339,36 @@ paths: rate: type: integer format: int32 - required: - - title_id - - status responses: '200': description: Title successfully added to user content: application/json: schema: - $ref: '#/components/schemas/UserTitleMini' + type: object + required: + - user_id + - title_id + - status + properties: + user_id: + type: integer + format: int64 + title_id: + type: integer + format: int64 + status: + $ref: '#/components/schemas/UserTitleStatus' + rate: + type: integer + format: int32 + review_id: + type: integer + format: int64 + ctime: + type: string + format: date-time + additionalProperties: false '400': description: 'Invalid request body (missing fields, invalid types, etc.)' '401': @@ -361,17 +382,17 @@ paths: '500': description: Internal server error patch: - operationId: updateUserTitle summary: Update a usertitle description: User updating title list of watched + operationId: updateUserTitle parameters: - name: user_id in: path - description: ID of the user to assign the title to required: true schema: type: integer format: int64 + description: ID of the user to assign the title to example: 123 requestBody: required: true @@ -379,6 +400,8 @@ paths: application/json: schema: type: object + required: + - title_id properties: title_id: type: integer @@ -388,15 +411,13 @@ paths: rate: type: integer format: int32 - required: - - title_id responses: '200': description: Title successfully updated content: application/json: schema: - $ref: '#/components/schemas/UserTitleMini' + $ref: '#/paths/~1users~1%7Buser_id%7D~1titles/post/responses/200/content/application~1json/schema' '400': description: 'Invalid request body (missing fields, invalid types, etc.)' '401': @@ -422,36 +443,25 @@ components: schema: $ref: '#/components/schemas/TitleSort' schemas: + CursorObj: + type: object + required: + - id + properties: + id: + type: integer + format: int64 + param: + type: string TitleSort: - description: Title sort order type: string + description: Title sort order default: id enum: - id - year - rating - views - TitleStatus: - description: Title status - type: string - enum: - - finished - - ongoing - - planned - ReleaseSeason: - description: Title release season - type: string - enum: - - winter - - spring - - summer - - fall - StorageType: - description: Image storage type - type: string - enum: - - s3 - - local Image: type: object properties: @@ -459,11 +469,65 @@ components: type: integer format: int64 storage_type: - $ref: '#/components/schemas/StorageType' + type: string + description: Image storage type + enum: + - s3 + - local image_path: type: string + TitleStatus: + type: string + description: Title status + enum: + - finished + - ongoing + - planned + ReleaseSeason: + type: string + description: Title release season + enum: + - winter + - spring + - summer + - fall + UserTitleStatus: + type: string + description: User's title status + enum: + - finished + - planned + - dropped + - in-progress + Review: + type: object + additionalProperties: true + Tag: + type: object + description: 'A localized tag: keys are language codes (ISO 639-1), values are tag names' + additionalProperties: + type: string + example: + en: Shojo + ru: Сёдзё + ja: 少女 + Tags: + type: array + description: Array of localized tags + items: + $ref: '#/components/schemas/Tag' + example: + - en: Shojo + ru: Сёдзё + ja: 少女 + - en: Shounen + ru: Сёнен + ja: 少年 Studio: type: object + required: + - id + - name properties: id: type: integer @@ -474,50 +538,21 @@ components: $ref: '#/components/schemas/Image' description: type: string - required: - - id - - name - Tag: - description: 'A localized tag: keys are language codes (ISO 639-1), values are tag names' - type: object - example: - en: Shojo - ru: Сёдзё - ja: 少女 - additionalProperties: - type: string - Tags: - description: Array of localized tags - type: array - items: - $ref: '#/components/schemas/Tag' - example: - - en: Shojo - ru: Сёдзё - ja: 少女 - - en: Shounen - ru: Сёнен - ja: 少年 Title: type: object + required: + - id + - title_names + - tags properties: id: - description: Unique title ID (primary key) type: integer format: int64 + description: Unique title ID (primary key) example: 1 title_names: - description: 'Localized titles. Key = language (ISO 639-1), value = list of names' type: object - example: - en: - - Attack on Titan - - AoT - ru: - - Атака титанов - - Титаны - ja: - - 進撃の巨人 + description: 'Localized titles. Key = language (ISO 639-1), value = list of names' additionalProperties: type: array items: @@ -527,6 +562,15 @@ components: example: - Attack on Titan - AoT + example: + en: + - Attack on Titan + - AoT + ru: + - Атака титанов + - Титаны + ja: + - 進撃の巨人 studio: $ref: '#/components/schemas/Studio' tags: @@ -557,68 +601,51 @@ components: additionalProperties: type: number format: double - required: - - id - - title_names - - tags - CursorObj: - type: object - properties: - id: - type: integer - format: int64 - param: - type: string - required: - - id + additionalProperties: true User: type: object properties: id: - description: Unique user ID (primary key) type: integer format: int64 + description: Unique user ID (primary key) example: 1 image: $ref: '#/components/schemas/Image' mail: - description: User email type: string format: email + description: User email example: john.doe@example.com nickname: + type: string description: Username (alphanumeric + _ or -) - type: string - example: john_doe_42 maxLength: 16 + example: john_doe_42 disp_name: + type: string description: Display name - type: string - example: John Doe maxLength: 32 + example: John Doe user_desc: - description: User description type: string - example: Just a regular user. + description: User description maxLength: 512 + example: Just a regular user. creation_date: - description: Timestamp when the user was created type: string format: date-time + description: Timestamp when the user was created example: '2025-10-10T23:45:47.908073Z' required: - user_id - nickname - UserTitleStatus: - description: User's title status - type: string - enum: - - finished - - planned - - dropped - - in-progress UserTitle: type: object + required: + - user_id + - title_id + - status properties: user_id: type: integer @@ -636,34 +663,3 @@ components: ctime: type: string format: date-time - required: - - user_id - - title_id - - status - UserTitleMini: - type: object - properties: - user_id: - type: integer - format: int64 - title_id: - type: integer - format: int64 - status: - $ref: '#/components/schemas/UserTitleStatus' - rate: - type: integer - format: int32 - review_id: - type: integer - format: int64 - ctime: - type: string - format: date-time - required: - - user_id - - title_id - - status - Review: - type: object - additionalProperties: true diff --git a/api/paths/titles-id.yaml b/api/paths/titles-id.yaml index 235743f..01fa504 100644 --- a/api/paths/titles-id.yaml +++ b/api/paths/titles-id.yaml @@ -1,6 +1,5 @@ get: summary: Get title description - operationId: getTitle parameters: - in: path name: title_id diff --git a/api/paths/users-id-titles.yaml b/api/paths/users-id-titles.yaml index 4f11ab6..1580cc1 100644 --- a/api/paths/users-id-titles.yaml +++ b/api/paths/users-id-titles.yaml @@ -117,10 +117,11 @@ post: type: integer format: int64 status: - $ref: '../schemas/enums/UserTitleStatus.yaml' + $ref: ../schemas/enums/UserTitleStatus.yaml rate: type: integer format: int32 + responses: '200': description: Title successfully added to user @@ -128,6 +129,7 @@ post: application/json: schema: $ref: '../schemas/UserTitleMini.yaml' + '400': description: Invalid request body (missing fields, invalid types, etc.) '401': @@ -167,7 +169,7 @@ patch: type: integer format: int64 status: - $ref: '../schemas/enums/UserTitleStatus.yaml' + $ref: ../schemas/enums/UserTitleStatus.yaml rate: type: integer format: int32 @@ -179,6 +181,7 @@ patch: application/json: schema: $ref: '../schemas/UserTitleMini.yaml' + '400': description: Invalid request body (missing fields, invalid types, etc.) '401': diff --git a/api/paths/users-id.yaml b/api/paths/users-id.yaml index fe62e46..06f4a19 100644 --- a/api/paths/users-id.yaml +++ b/api/paths/users-id.yaml @@ -1,6 +1,5 @@ get: summary: Get user info - operationId: getUsersId parameters: - in: path name: user_id diff --git a/api/schemas/Title.yaml b/api/schemas/Title.yaml index 877ee24..7497d1f 100644 --- a/api/schemas/Title.yaml +++ b/api/schemas/Title.yaml @@ -60,3 +60,4 @@ properties: additionalProperties: type: number format: double +additionalProperties: true diff --git a/api/schemas/UserTitleMini.yaml b/api/schemas/UserTitleMini.yaml index e1a5a74..9e45e95 100644 --- a/api/schemas/UserTitleMini.yaml +++ b/api/schemas/UserTitleMini.yaml @@ -21,3 +21,4 @@ properties: ctime: type: string format: date-time +additionalProperties: false diff --git a/modules/frontend/src/App.tsx b/modules/frontend/src/App.tsx index e2c909f..3ecfa2d 100644 --- a/modules/frontend/src/App.tsx +++ b/modules/frontend/src/App.tsx @@ -2,7 +2,6 @@ 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"; @@ -25,9 +24,7 @@ const App: React.FC = () => { /> } /> - } /> - } /> ); diff --git a/modules/frontend/src/api/core/OpenAPI.ts b/modules/frontend/src/api/core/OpenAPI.ts index 6ce873e..185e5c3 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: 'http://10.1.0.65:8081/api/v1', + BASE: '/api/v1', VERSION: '1.0.0', WITH_CREDENTIALS: false, CREDENTIALS: 'include', diff --git a/modules/frontend/src/api/index.ts b/modules/frontend/src/api/index.ts index 9013fc7..80ae491 100644 --- a/modules/frontend/src/api/index.ts +++ b/modules/frontend/src/api/index.ts @@ -12,7 +12,6 @@ export type { CursorObj } from './models/CursorObj'; export type { Image } from './models/Image'; export type { ReleaseSeason } from './models/ReleaseSeason'; export type { Review } from './models/Review'; -export type { StorageType } from './models/StorageType'; export type { Studio } from './models/Studio'; export type { Tag } from './models/Tag'; export type { Tags } from './models/Tags'; @@ -22,7 +21,6 @@ export type { TitleSort } from './models/TitleSort'; export type { TitleStatus } from './models/TitleStatus'; export type { User } from './models/User'; export type { UserTitle } from './models/UserTitle'; -export type { UserTitleMini } from './models/UserTitleMini'; export type { UserTitleStatus } from './models/UserTitleStatus'; export { DefaultService } from './services/DefaultService'; diff --git a/modules/frontend/src/api/models/Image.ts b/modules/frontend/src/api/models/Image.ts index 887bf2f..a94de74 100644 --- a/modules/frontend/src/api/models/Image.ts +++ b/modules/frontend/src/api/models/Image.ts @@ -2,10 +2,12 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { StorageType } from './StorageType'; export type Image = { id?: number; - storage_type?: StorageType; + /** + * Image storage type + */ + storage_type?: 's3' | 'local'; image_path?: string; }; diff --git a/modules/frontend/src/api/models/StorageType.ts b/modules/frontend/src/api/models/StorageType.ts deleted file mode 100644 index f6d086b..0000000 --- a/modules/frontend/src/api/models/StorageType.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -/** - * Image storage type - */ -export type StorageType = 's3' | 'local'; diff --git a/modules/frontend/src/api/models/Title.ts b/modules/frontend/src/api/models/Title.ts index 9ffdeb6..4da7aa3 100644 --- a/modules/frontend/src/api/models/Title.ts +++ b/modules/frontend/src/api/models/Title.ts @@ -2,30 +2,4 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Image } from './Image'; -import type { ReleaseSeason } from './ReleaseSeason'; -import type { Studio } from './Studio'; -import type { Tags } from './Tags'; -import type { TitleStatus } from './TitleStatus'; -export type Title = { - /** - * Unique title ID (primary key) - */ - id: number; - /** - * Localized titles. Key = language (ISO 639-1), value = list of names - */ - title_names: Record>; - studio?: Studio; - tags: Tags; - poster?: Image; - title_status?: TitleStatus; - rating?: number; - rating_count?: number; - release_year?: number; - release_season?: ReleaseSeason; - episodes_aired?: number; - episodes_all?: number; - episodes_len?: Record; -}; - +export type Title = Record; diff --git a/modules/frontend/src/api/models/UserTitleMini.ts b/modules/frontend/src/api/models/UserTitleMini.ts deleted file mode 100644 index 2b223ce..0000000 --- a/modules/frontend/src/api/models/UserTitleMini.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { UserTitleStatus } from './UserTitleStatus'; -export type UserTitleMini = { - user_id: number; - title_id: number; - status: UserTitleStatus; - rate?: number; - review_id?: number; - ctime?: string; -}; - diff --git a/modules/frontend/src/api/services/DefaultService.ts b/modules/frontend/src/api/services/DefaultService.ts index 5070fae..874971e 100644 --- a/modules/frontend/src/api/services/DefaultService.ts +++ b/modules/frontend/src/api/services/DefaultService.ts @@ -9,7 +9,6 @@ import type { TitleSort } from '../models/TitleSort'; import type { TitleStatus } from '../models/TitleStatus'; import type { User } from '../models/User'; import type { UserTitle } from '../models/UserTitle'; -import type { UserTitleMini } from '../models/UserTitleMini'; import type { UserTitleStatus } from '../models/UserTitleStatus'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; @@ -79,7 +78,7 @@ export class DefaultService { * @returns Title Title description * @throws ApiError */ - public static getTitle( + public static getTitles1( titleId: number, fields: string = 'all', ): CancelablePromise { @@ -106,7 +105,7 @@ export class DefaultService { * @returns User User info * @throws ApiError */ - public static getUsersId( + public static getUsers( userId: string, fields: string = 'all', ): CancelablePromise<User> { @@ -249,17 +248,22 @@ export class DefaultService { * User adding title to list af watched, status required * @param userId ID of the user to assign the title to * @param requestBody - * @returns UserTitleMini Title successfully added to user + * @returns any Title successfully added to user * @throws ApiError */ public static addUserTitle( userId: number, - requestBody: { + requestBody: UserTitle, + ): CancelablePromise<{ + data?: { + user_id: number; title_id: number; status: UserTitleStatus; rate?: number; - }, - ): CancelablePromise<UserTitleMini> { + review_id?: number; + ctime?: string; + }; + }> { return __request(OpenAPI, { method: 'POST', url: '/users/{user_id}/titles', @@ -278,37 +282,4 @@ export class DefaultService { }, }); } - /** - * Update a usertitle - * User updating title list of watched - * @param userId ID of the user to assign the title to - * @param requestBody - * @returns UserTitleMini Title successfully updated - * @throws ApiError - */ - public static updateUserTitle( - userId: number, - requestBody: { - title_id: number; - status?: UserTitleStatus; - rate?: number; - }, - ): CancelablePromise<UserTitleMini> { - return __request(OpenAPI, { - method: 'PATCH', - url: '/users/{user_id}/titles', - path: { - 'user_id': userId, - }, - body: requestBody, - mediaType: 'application/json', - errors: { - 400: `Invalid request body (missing fields, invalid types, etc.)`, - 401: `Unauthorized — missing or invalid auth token`, - 403: `Forbidden — user not allowed to update title`, - 404: `User or Title not found`, - 500: `Internal server error`, - }, - }); - } } diff --git a/modules/frontend/src/auth/core/OpenAPI.ts b/modules/frontend/src/auth/core/OpenAPI.ts index 79aa305..2d0edf8 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: 'http://10.1.0.65:8081/auth', + BASE: '/auth', VERSION: '1.0.0', WITH_CREDENTIALS: false, CREDENTIALS: 'include', diff --git a/modules/frontend/src/pages/TitlePage/TitlePage.module.css b/modules/frontend/src/pages/TitlePage/TitlePage.module.css new file mode 100644 index 0000000..e69de29 diff --git a/modules/frontend/src/pages/TitlePage/TitlePage.tsx b/modules/frontend/src/pages/TitlePage/TitlePage.tsx index 5ea0e3d..7fe9de7 100644 --- a/modules/frontend/src/pages/TitlePage/TitlePage.tsx +++ b/modules/frontend/src/pages/TitlePage/TitlePage.tsx @@ -1,140 +1,64 @@ -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"; +// import React, { useEffect, useState } from "react"; +// import { useParams } from "react-router-dom"; +// import { DefaultService } from "../../api/services/DefaultService"; +// import type { User } from "../../api/models/User"; +// import styles from "./UserPage.module.css"; -const STATUS_BUTTONS: { status: UserTitleStatus; icon: React.ReactNode; label: string }[] = [ - { status: "planned", icon: <ClockIcon className="w-6 h-6" />, label: "Planned" }, - { status: "finished", icon: <CheckCircleIcon className="w-6 h-6" />, label: "Finished" }, - { status: "in-progress", icon: <PlayCircleIcon className="w-6 h-6" />, label: "In Progress" }, - { status: "dropped", icon: <XCircleIcon className="w-6 h-6" />, label: "Dropped" }, -]; +// const UserPage: React.FC = () => { +// const { id } = useParams<{ id: string }>(); +// const [user, setUser] = useState<User | null>(null); +// const [loading, setLoading] = useState(true); +// const [error, setError] = useState<string | null>(null); -export default function TitlePage() { - const params = useParams(); - const titleId = Number(params.id); +// useEffect(() => { +// if (!id) return; - const [title, setTitle] = useState<Title | null>(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState<string | null>(null); +// const getTitleInfo = async () => { +// try { +// const userInfo = await DefaultService.getTitle(id, "all"); +// setUser(userInfo); +// } catch (err) { +// console.error(err); +// setError("Failed to fetch user info."); +// } finally { +// setLoading(false); +// } +// }; +// getTitleInfo(); +// }, [id]); - const [userStatus, setUserStatus] = useState<UserTitleStatus | null>(null); - const [updatingStatus, setUpdatingStatus] = useState(false); +// if (loading) return <div className={styles.loader}>Loading...</div>; +// if (error) return <div className={styles.error}>{error}</div>; +// if (!user) return <div className={styles.error}>User not found.</div>; - 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]); +// return ( +// <div className={styles.container}> +// <div className={styles.card}> +// <div className={styles.avatar}> +// {user.avatar_id ? ( +// <img +// src={`/images/${user.avatar_id}.png`} +// alt="User Avatar" +// className={styles.avatarImg} +// /> +// ) : ( +// <div className={styles.avatarPlaceholder}> +// {user.disp_name?.[0] || "U"} +// </div> +// )} +// </div> - const handleStatusClick = async (status: UserTitleStatus) => { - if (updatingStatus || userStatus === status) return; +// <div className={styles.info}> +// <h1 className={styles.name}>{user.disp_name || user.nickname}</h1> +// <p className={styles.nickname}>@{user.nickname}</p> +// {user.user_desc && <p className={styles.desc}>{user.user_desc}</p>} +// <p className={styles.created}> +// Joined: {new Date(user.creation_date).toLocaleDateString()} +// </p> +// </div> +// </div> +// </div> +// ); +// }; - 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 <div className="mt-20 font-medium text-black">Loading title...</div>; - if (error) return <div className="mt-20 text-red-600 font-medium">{error}</div>; - if (!title) return null; - - return ( - <div className="w-full min-h-screen bg-gray-50 p-6 flex justify-center"> - <div className="flex flex-col md:flex-row bg-white shadow-lg rounded-xl max-w-4xl w-full p-6 gap-6"> - {/* Постер */} - <div className="flex flex-col items-center"> - <img - src={title.poster?.image_path || "/default-poster.png"} - alt={title.title_names?.en?.[0] || "Title poster"} - className="w-48 h-72 object-cover rounded-lg mb-4" - /> - - {/* Статус кнопки с иконками */} - <div className="flex gap-2 mt-2 flex-wrap justify-center"> - {STATUS_BUTTONS.map(btn => ( - <button - key={btn.status} - onClick={() => handleStatusClick(btn.status)} - disabled={updatingStatus} - className={`p-2 rounded-lg transition flex items-center justify-center ${ - userStatus === btn.status - ? "bg-blue-600 text-white" - : "bg-gray-200 text-gray-700 hover:bg-gray-300" - }`} - title={btn.label} - > - {btn.icon} - </button> - ))} - </div> - </div> - - {/* Информация о тайтле */} - <div className="flex-1 flex flex-col"> - <h1 className="text-3xl font-bold mb-2"> - {title.title_names?.en?.[0] || "Untitled"} - </h1> - {title.studio && <p className="text-gray-700 mb-1">Studio: {title.studio.name}</p>} - {title.title_status && <p className="text-gray-700 mb-1">Status: {title.title_status}</p>} - {title.rating !== undefined && ( - <p className="text-gray-700 mb-1"> - Rating: {title.rating} ({title.rating_count} votes) - </p> - )} - {title.release_year && ( - <p className="text-gray-700 mb-1"> - Released: {title.release_year} {title.release_season || ""} - </p> - )} - {title.episodes_aired !== undefined && ( - <p className="text-gray-700 mb-1"> - Episodes: {title.episodes_aired}/{title.episodes_all} - </p> - )} - {title.tags && title.tags.length > 0 && ( - <p className="text-gray-700 mb-1"> - Tags: {getTagsString()} - </p> - )} - </div> - </div> - </div> - ); -} +// export default UserPage; diff --git a/modules/frontend/src/pages/UserPage/UserPage.tsx b/modules/frontend/src/pages/UserPage/UserPage.tsx index eafdf6b..2e39e6b 100644 --- a/modules/frontend/src/pages/UserPage/UserPage.tsx +++ b/modules/frontend/src/pages/UserPage/UserPage.tsx @@ -15,7 +15,7 @@ const UserPage: React.FC = () => { const getUserInfo = async () => { try { - const userInfo = await DefaultService.getUsersId(id, "all"); // <-- use dynamic id + const userInfo = await DefaultService.getUsers(id, "all"); // <-- use dynamic id setUser(userInfo); } catch (err) { console.error(err); diff --git a/modules/frontend/src/pages/UsersIdPage/UsersIdPage.tsx b/modules/frontend/src/pages/UsersIdPage/UsersIdPage.tsx index 729da20..342f22c 100644 --- a/modules/frontend/src/pages/UsersIdPage/UsersIdPage.tsx +++ b/modules/frontend/src/pages/UsersIdPage/UsersIdPage.tsx @@ -41,7 +41,7 @@ export default function UsersIdPage({ userId }: UsersIdPageProps) { if (!id) return; setLoadingUser(true); try { - const result = await DefaultService.getUsersId(id, "all"); + const result = await DefaultService.getUsers(id, "all"); setUser(result); setErrorUser(null); } catch (err: any) {