tercul-frontend/client/src/hooks/use-author-profile.ts
Damir Mukimov 4a23f496fa
Major frontend development updates
- 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
2025-11-27 03:44:09 +01:00

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,
};
}