I still can display thumbnails
This commit is contained in:
@@ -14,6 +14,7 @@ from starlette.responses import Response
|
||||
|
||||
from app.api.dependencies import get_document_service, get_current_user
|
||||
from app.models.document import DocumentResponse, FileDocument
|
||||
from app.models.user import UserInDB
|
||||
from app.services.document_service import DocumentService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -123,7 +124,7 @@ def _map_file_document_to_response(
|
||||
def list_documents(
|
||||
skip: int = Query(0, ge=0, description="Number of documents to skip"),
|
||||
limit: int = Query(100, ge=1, le=1000, description="Maximum number of documents to return"),
|
||||
UserInDB=Depends(get_current_user),
|
||||
user: UserInDB = Depends(get_current_user),
|
||||
document_service: DocumentService = Depends(get_document_service)
|
||||
) -> List[DocumentResponse]:
|
||||
"""
|
||||
@@ -163,7 +164,8 @@ def list_documents(
|
||||
@router.get("/objects/{file_hash}")
|
||||
async def get_object_by_hash(
|
||||
file_hash: str = Path(..., description="SHA256 hash of the object to retrieve"),
|
||||
document_service: DocumentService = Depends(get_document_service)
|
||||
document_service: DocumentService = Depends(get_document_service),
|
||||
user: UserInDB = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Serve object content by its hash.
|
||||
|
||||
@@ -13,7 +13,7 @@ const Header = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="navbar bg-base-100 shadow-lg">
|
||||
<div className="navbar bg-base-100">
|
||||
<div className="navbar-start">
|
||||
<h1 className="text-xl font-bold">MyDocManager</h1>
|
||||
</div>
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import Header from './Header';
|
||||
import {Outlet} from 'react-router-dom';
|
||||
import Menu from "./Menu.jsx";
|
||||
import styles from './Layout.module.css';
|
||||
|
||||
const Layout = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-base-200">
|
||||
<div className={styles.layoutContainer}>
|
||||
<Header/>
|
||||
<div className="flex">
|
||||
<aside className="w-64 min-h-screen bg-base-100 shadow-lg"><Menu/></aside>
|
||||
<main className="flex-1 container mx-auto px-4 py-8">
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<aside className={styles.sidebar}>
|
||||
<Menu/>
|
||||
</aside>
|
||||
<main className={styles.mainContent}>
|
||||
<div className={styles.mainContentInner}>
|
||||
<Outlet/>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
30
src/frontend/src/components/common/Layout.module.css
Normal file
30
src/frontend/src/components/common/Layout.module.css
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Layout Container */
|
||||
.layoutContainer {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-base-200);
|
||||
}
|
||||
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 16rem; /* 64px = 4rem, donc 256px = 16rem */
|
||||
background-color: var(--color-base-100);
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Main Content Area */
|
||||
.mainContent {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Main Content Inner Container */
|
||||
.mainContentInner {
|
||||
max-width: 80rem; /* container max-width */
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
* Supports different view modes: small, large, and detail
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import React, {memo} from 'react';
|
||||
import {API_BASE_URL} from "../../utils/api.js";
|
||||
|
||||
/**
|
||||
* Formats file size to human-readable format
|
||||
@@ -32,6 +33,18 @@ const formatDate = (dateString) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds full URL from relative path
|
||||
* @param {string} relativePath - Relative API path
|
||||
* @returns {string} Full URL
|
||||
*/
|
||||
const buildFullUrl = (relativePath) => {
|
||||
if (!relativePath) return '';
|
||||
// Use the base URL from your API configuration
|
||||
const baseUrl = import.meta.env.VITE_API_BASE_URL || API_BASE_URL;
|
||||
return `${baseUrl}${relativePath}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* DocumentCard component
|
||||
* @param {Object} props
|
||||
@@ -41,8 +54,8 @@ const formatDate = (dateString) => {
|
||||
* @param {function(): void} props.onDelete - Callback when delete is clicked
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
const DocumentCard = memo(({ document, viewMode, onEdit, onDelete }) => {
|
||||
const { name, originalFileType, thumbnailUrl, pageCount, fileSize, createdAt, tags, categories } = document;
|
||||
const DocumentCard = memo(({document, viewMode, onEdit, onDelete}) => {
|
||||
const {name, originalFileType, thumbnailUrl, pageCount, fileSize, createdAt, tags, categories} = document;
|
||||
|
||||
// Determine card classes based on view mode
|
||||
const getCardClasses = () => {
|
||||
@@ -64,9 +77,9 @@ const DocumentCard = memo(({ document, viewMode, onEdit, onDelete }) => {
|
||||
const renderThumbnail = () => (
|
||||
<figure className="relative overflow-hidden">
|
||||
<img
|
||||
src={`http://localhost:8000${thumbnailUrl}`}
|
||||
alt={`${thumbnailUrl} thumbnail`}
|
||||
className={`w-full object-cover ${
|
||||
src={buildFullUrl(thumbnailUrl)}
|
||||
alt={`${name}`}
|
||||
className={`w-[200px] object-cover ${
|
||||
viewMode === 'small' ? 'h-32' : viewMode === 'large' ? 'h-48' : 'h-64'
|
||||
}`}
|
||||
loading="lazy"
|
||||
@@ -80,8 +93,10 @@ const DocumentCard = memo(({ document, viewMode, onEdit, onDelete }) => {
|
||||
aria-label="Edit document"
|
||||
title="Edit"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
||||
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
@@ -90,8 +105,10 @@ const DocumentCard = memo(({ document, viewMode, onEdit, onDelete }) => {
|
||||
aria-label="Delete document"
|
||||
title="Delete"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
|
||||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,8 @@ const api = axios.create({
|
||||
},
|
||||
});
|
||||
|
||||
export { API_BASE_URL };
|
||||
|
||||
// Request interceptor to add authentication token
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
|
||||
Reference in New Issue
Block a user