Improve how literary works are displayed and add annotation support

Refactors EnhancedReadingView to handle optional tags, updates apiRequest to return a generic type, and introduces the Annotation type.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: cbacfb18-842a-4116-a907-18c0105ad8ec
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/39b5c689-6e8a-4d5a-9792-69cc81a56534/87f6d858-e481-4210-ae2d-1b5aa7299a3e.jpg
This commit is contained in:
mukimovd 2025-05-01 03:21:57 +00:00
parent 04619aa3cb
commit fc244419be
4 changed files with 21 additions and 7 deletions

View File

@ -246,11 +246,11 @@ export function EnhancedReadingView({ work, translations }: EnhancedReadingViewP
<p className="text-xs text-navy/70 dark:text-cream/70 font-sans">Written in {work.year}</p> <p className="text-xs text-navy/70 dark:text-cream/70 font-sans">Written in {work.year}</p>
)} )}
<p className="text-xs text-navy/70 dark:text-cream/70 font-sans capitalize"> <p className="text-xs text-navy/70 dark:text-cream/70 font-sans capitalize">
{work.type} {work.language} {work.tags.length} tags {work.type} {work.language} {work.tags?.length || 0} tags
</p> </p>
</div> </div>
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{work.tags.map(tag => ( {work.tags?.map(tag => (
<Badge <Badge
key={tag.id} key={tag.id}
variant="outline" variant="outline"
@ -384,7 +384,7 @@ export function EnhancedReadingView({ work, translations }: EnhancedReadingViewP
<div className="my-4"> <div className="my-4">
<h4 className="text-xs uppercase tracking-wider text-navy/60 dark:text-cream/60 font-sans font-medium mb-2">Tags</h4> <h4 className="text-xs uppercase tracking-wider text-navy/60 dark:text-cream/60 font-sans font-medium mb-2">Tags</h4>
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{work.tags.map(tag => ( {work.tags?.map(tag => (
<Badge <Badge
key={tag.id} key={tag.id}
variant="outline" variant="outline"

View File

@ -7,11 +7,11 @@ async function throwIfResNotOk(res: Response) {
} }
} }
export async function apiRequest( export async function apiRequest<T = any>(
method: string, method: string,
url: string, url: string,
data?: unknown | undefined, data?: unknown | undefined,
): Promise<Response> { ): Promise<T> {
const res = await fetch(url, { const res = await fetch(url, {
method, method,
headers: data ? { "Content-Type": "application/json" } : {}, headers: data ? { "Content-Type": "application/json" } : {},
@ -20,7 +20,7 @@ export async function apiRequest(
}); });
await throwIfResNotOk(res); await throwIfResNotOk(res);
return res; return await res.json();
} }
type UnauthorizedBehavior = "returnNull" | "throw"; type UnauthorizedBehavior = "returnNull" | "throw";

View File

@ -82,3 +82,17 @@ export interface ReadingContext {
fontSizeClass: string; fontSizeClass: string;
zenMode: boolean; zenMode: boolean;
} }
export interface Annotation {
id: number;
workId: number;
translationId?: number;
lineNumber: number;
userId: number;
userName: string;
userAvatar: string | null;
content: string;
createdAt: Date;
likes: number;
liked: boolean;
}

View File

@ -40,7 +40,7 @@ export default function CreateCollection() {
// Create collection mutation // Create collection mutation
const createMutation = useMutation({ const createMutation = useMutation({
mutationFn: async (values: FormValues) => { mutationFn: async (values: FormValues) => {
return await apiRequest<{ id: number; slug: string }>('POST', '/api/collections', { return await apiRequest('POST', '/api/collections', {
name: values.name, name: values.name,
description: values.description || '', description: values.description || '',
isPublic: values.isPublic, isPublic: values.isPublic,