mirror of
https://github.com/SamyRai/tercul-frontend.git
synced 2025-12-27 04:51:34 +00:00
- Enhanced annotation system with improved inline editing - Updated author components with new card and header designs - Improved reading view with enhanced line numbering and controls - Added new blog management features and tag management - Updated UI components with improved accessibility and styling - Enhanced search functionality with better filtering - Added new dashboard features and activity feeds - Improved translation selector and work comparison tools - Updated GraphQL integration and API hooks - Enhanced responsive design and mobile experience
238 lines
5.3 KiB
TypeScript
238 lines
5.3 KiB
TypeScript
import { useState, useMemo } from "react";
|
|
import { useParams } from "wouter";
|
|
import type { WorkWithAuthor } from "@/lib/types";
|
|
import {
|
|
useAuthorBySlug,
|
|
useAuthorWorks,
|
|
useAuthorTimeline,
|
|
} from "./use-author-api";
|
|
|
|
export interface AuthorProfileFilters {
|
|
selectedGenre: string | null;
|
|
selectedYear: string | null;
|
|
selectedWorkType: string | null;
|
|
selectedLanguage: string | null;
|
|
}
|
|
|
|
export interface AuthorProfileView {
|
|
view: "list" | "grid";
|
|
}
|
|
|
|
export interface AuthorProfileState
|
|
extends AuthorProfileFilters,
|
|
AuthorProfileView {
|
|
following: boolean;
|
|
followCount: number;
|
|
}
|
|
|
|
export function useAuthorProfile() {
|
|
const { slug } = useParams<{ slug: string }>();
|
|
|
|
// API calls using existing hooks
|
|
const {
|
|
data: author,
|
|
isLoading: authorLoading,
|
|
error: authorError,
|
|
} = useAuthorBySlug(slug || "");
|
|
|
|
const {
|
|
data: works,
|
|
isLoading: worksLoading,
|
|
error: worksError,
|
|
} = useAuthorWorks(author?.id || "");
|
|
|
|
const {
|
|
data: timeline,
|
|
isLoading: timelineLoading,
|
|
error: timelineError,
|
|
} = useAuthorTimeline(author?.id || "");
|
|
|
|
// Local state
|
|
const [state, setState] = useState<AuthorProfileState>({
|
|
view: "list",
|
|
selectedGenre: null,
|
|
selectedYear: null,
|
|
selectedWorkType: null,
|
|
selectedLanguage: null,
|
|
following: false,
|
|
followCount: Math.floor(Math.random() * 2000),
|
|
});
|
|
|
|
// Computed values
|
|
const computedStats = useMemo(() => {
|
|
if (!works) return null;
|
|
|
|
return {
|
|
worksCount: works.length,
|
|
translationsCount: works.reduce(
|
|
(acc, _work) => acc + Math.floor(Math.random() * 5),
|
|
0
|
|
),
|
|
readingCount: Math.floor(Math.random() * 10000),
|
|
citationCount: Math.floor(Math.random() * 1000),
|
|
annotationCount: Math.floor(Math.random() * 500),
|
|
};
|
|
}, [works]);
|
|
|
|
const filterOptions = useMemo(() => {
|
|
if (!works) return { years: [], genres: [], languages: [], workTypes: [] };
|
|
|
|
const years = Array.from(
|
|
new Set(works.map((work) => work.year?.toString()).filter(Boolean))
|
|
);
|
|
const genres = Array.from(
|
|
new Set(
|
|
works
|
|
.flatMap((work) => work.tags?.map((tag) => tag.name) || [])
|
|
.filter(Boolean)
|
|
)
|
|
);
|
|
const languages = Array.from(
|
|
new Set(works.map((work) => work.language).filter(Boolean))
|
|
);
|
|
const workTypes = Array.from(
|
|
new Set(works.map((work) => work.type).filter(Boolean))
|
|
);
|
|
|
|
return { years, genres, languages, workTypes };
|
|
}, [works]);
|
|
|
|
const filteredWorks = useMemo(() => {
|
|
if (!works) return [];
|
|
|
|
return works.filter((work) => {
|
|
if (
|
|
state.selectedGenre &&
|
|
(!work.tags ||
|
|
!work.tags.some((tag) => tag.name === state.selectedGenre))
|
|
) {
|
|
return false;
|
|
}
|
|
if (state.selectedYear && work.year?.toString() !== state.selectedYear) {
|
|
return false;
|
|
}
|
|
if (state.selectedWorkType && work.type !== state.selectedWorkType) {
|
|
return false;
|
|
}
|
|
if (state.selectedLanguage && work.language !== state.selectedLanguage) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}, [
|
|
works,
|
|
state.selectedGenre,
|
|
state.selectedYear,
|
|
state.selectedWorkType,
|
|
state.selectedLanguage,
|
|
]);
|
|
|
|
const worksByType = useMemo(() => {
|
|
if (!filteredWorks) return {};
|
|
|
|
return filteredWorks.reduce<Record<string, WorkWithAuthor[]>>(
|
|
(acc, work) => {
|
|
const type = work.type || "Other";
|
|
if (!acc[type]) {
|
|
acc[type] = [];
|
|
}
|
|
acc[type].push(work);
|
|
return acc;
|
|
},
|
|
{}
|
|
);
|
|
}, [filteredWorks]);
|
|
|
|
// Actions
|
|
const updateFilters = (filters: Partial<AuthorProfileFilters>) => {
|
|
setState((prev) => ({ ...prev, ...filters }));
|
|
};
|
|
|
|
const clearFilters = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
selectedGenre: null,
|
|
selectedYear: null,
|
|
selectedWorkType: null,
|
|
selectedLanguage: null,
|
|
}));
|
|
};
|
|
|
|
const setView = (view: "list" | "grid") => {
|
|
setState((prev) => ({ ...prev, view }));
|
|
};
|
|
|
|
const toggleFollow = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
following: !prev.following,
|
|
followCount: prev.following ? prev.followCount - 1 : prev.followCount + 1,
|
|
}));
|
|
};
|
|
|
|
// Loading and error states
|
|
const isLoading = authorLoading || worksLoading || timelineLoading;
|
|
const error = authorError || worksError || timelineError;
|
|
|
|
// Helper functions
|
|
const formatTypeName = (type: string): string => {
|
|
switch (type) {
|
|
case "poem":
|
|
return "Poems";
|
|
case "story":
|
|
return "Short Stories";
|
|
case "novel":
|
|
return "Novels";
|
|
case "play":
|
|
return "Plays";
|
|
case "essay":
|
|
return "Essays";
|
|
default:
|
|
return "Other Works";
|
|
}
|
|
};
|
|
|
|
const hasActiveFilters = !!(
|
|
state.selectedGenre ||
|
|
state.selectedYear ||
|
|
state.selectedWorkType ||
|
|
state.selectedLanguage
|
|
);
|
|
|
|
return {
|
|
// Data
|
|
author,
|
|
works,
|
|
timeline,
|
|
computedStats,
|
|
filteredWorks,
|
|
worksByType,
|
|
filterOptions,
|
|
|
|
// State
|
|
...state,
|
|
hasActiveFilters,
|
|
|
|
// Loading states
|
|
isLoading,
|
|
authorLoading,
|
|
worksLoading,
|
|
timelineLoading,
|
|
|
|
// Error states
|
|
error,
|
|
authorError,
|
|
worksError,
|
|
timelineError,
|
|
|
|
// Actions
|
|
updateFilters,
|
|
clearFilters,
|
|
setView,
|
|
toggleFollow,
|
|
|
|
// Utilities
|
|
formatTypeName,
|
|
};
|
|
}
|