diff --git a/client/src/components/authors/author-works-display.tsx b/client/src/components/authors/author-works-display.tsx
index 958ae22..7dcd067 100644
--- a/client/src/components/authors/author-works-display.tsx
+++ b/client/src/components/authors/author-works-display.tsx
@@ -42,19 +42,30 @@ export function AuthorWorksDisplay({
// Use the actual API hook to fetch author's works
const { data: works, isLoading, error } = useAuthorWorks(authorId);
+ // Convert works with tag objects back to Work type for components
+ const worksForDisplay: Work[] = useMemo(() =>
+ works?.map(work => ({
+ ...work,
+ tags: work.tags?.map(tag =>
+ typeof tag === 'string' ? tag : tag.name
+ ),
+ })) || [],
+ [works]
+ );
+
// Extract filter options from works data
const years = useMemo(
() =>
Array.from(
- new Set(works?.map((work) => work.year?.toString()).filter(Boolean))
+ new Set(worksForDisplay?.map((work) => work.year?.toString()).filter(Boolean))
).filter((year): year is string => year !== undefined),
- [works]
+ [worksForDisplay]
);
const languages = useMemo(
() =>
- Array.from(new Set(works?.map((work) => work.language).filter(Boolean))),
- [works]
+ Array.from(new Set(worksForDisplay?.map((work) => work.language).filter(Boolean))),
+ [worksForDisplay]
);
const workTypes = useMemo(
@@ -68,7 +79,7 @@ export function AuthorWorksDisplay({
// Filter works based on selected filters
const filteredWorks = useMemo(
() =>
- works?.filter((work) => {
+ worksForDisplay?.filter((work) => {
if (
filters.selectedYear &&
work.year?.toString() !== filters.selectedYear
@@ -89,7 +100,7 @@ export function AuthorWorksDisplay({
}
return true;
}),
- [works, filters]
+ [worksForDisplay, filters]
);
// Group works by type
@@ -281,7 +292,7 @@ export function AuthorWorksDisplay({
{/* Reading statistics (extracted component) */}
{works && works.length > 0 && (
diff --git a/client/src/components/authors/composables/author-stats.tsx b/client/src/components/authors/composables/author-stats.tsx
index a15f3d0..666b518 100644
--- a/client/src/components/authors/composables/author-stats.tsx
+++ b/client/src/components/authors/composables/author-stats.tsx
@@ -12,7 +12,7 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
-import type { AuthorWithStats } from "../types";
+import type { AuthorWithStats } from "@shared/schema";
import { authorUtils } from "../utils";
const authorStatsVariants = cva("", {
@@ -65,38 +65,38 @@ export function AuthorStats({
key: string;
}> = [];
- if (showWorksCount && author.worksCount) {
+ if (showWorksCount && author.works_count) {
stats.push({
icon: ,
label: "Works",
- value: authorUtils.formatNumber(author.worksCount, format),
+ value: authorUtils.formatNumber(author.works_count, format),
key: "works",
});
}
- if (showFollowersCount && author.followersCount) {
+ if (showFollowersCount && author.followers_count) {
stats.push({
icon: ,
label: "Followers",
- value: authorUtils.formatNumber(author.followersCount, format),
+ value: authorUtils.formatNumber(author.followers_count, format),
key: "followers",
});
}
- if (showRating && author.averageRating) {
+ if (showRating && author.average_rating) {
stats.push({
icon: ,
label: "Rating",
- value: authorUtils.formatRating(author.averageRating),
+ value: authorUtils.formatRating(author.average_rating),
key: "rating",
});
}
- if (showReadsCount && author.totalReads) {
+ if (showReadsCount && author.total_reads) {
stats.push({
icon: ,
label: "Reads",
- value: authorUtils.formatNumber(author.totalReads, format),
+ value: authorUtils.formatNumber(author.total_reads, format),
key: "reads",
});
}
diff --git a/client/src/components/authors/types.ts b/client/src/components/authors/types.ts
index a36a03b..555b02b 100644
--- a/client/src/components/authors/types.ts
+++ b/client/src/components/authors/types.ts
@@ -97,6 +97,16 @@ export interface AuthorDisplayUtils {
text: string;
isTruncated: boolean;
};
+
+ /**
+ * Format numbers for display
+ */
+ formatNumber: (num: number, format?: 'numbers' | 'abbreviated' | 'full') => string;
+
+ /**
+ * Format rating for display
+ */
+ formatRating: (rating: number) => string;
}
// Timeline event for author pages
diff --git a/client/src/components/authors/utils.ts b/client/src/components/authors/utils.ts
index dc8f6cb..a861e37 100644
--- a/client/src/components/authors/utils.ts
+++ b/client/src/components/authors/utils.ts
@@ -30,6 +30,28 @@ export const authorUtils: AuthorDisplayUtils = {
.slice(0, 2);
},
+ /**
+ * Format numbers for display
+ */
+ formatNumber: (num: number, format: 'numbers' | 'abbreviated' | 'full' = 'full'): string => {
+ if (format === 'abbreviated') {
+ if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
+ if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
+ return num.toString();
+ }
+ if (format === 'numbers') {
+ return num.toLocaleString();
+ }
+ return num.toString();
+ },
+
+ /**
+ * Format rating for display
+ */
+ formatRating: (rating: number): string => {
+ return rating.toFixed(1);
+ },
+
/**
* Get display location from country/city
*/
diff --git a/client/src/components/blog/tag-manager.tsx b/client/src/components/blog/tag-manager.tsx
index 068b2ca..9f8e45b 100644
--- a/client/src/components/blog/tag-manager.tsx
+++ b/client/src/components/blog/tag-manager.tsx
@@ -32,7 +32,7 @@ export function TagManager({ name, tags, label = "Tags" }: TagManagerProps) {
const formTags = form.getValues(name) || [];
if (tags && formTags.length > 0) {
const initialTags = formTags
- .map((id: number) => tags.find((t) => t.id === id))
+ .map((id: string) => tags.find((t) => t.id === id))
.filter(Boolean) as Tag[];
setSelectedTags(initialTags);
}
@@ -42,8 +42,7 @@ export function TagManager({ name, tags, label = "Tags" }: TagManagerProps) {
const handleAddTag = () => {
if (!selectedTagId) return;
- const tagId = parseInt(selectedTagId, 10);
- const tag = tags?.find((t) => t.id === tagId);
+ const tag = tags?.find((t) => t.id === selectedTagId);
if (tag && !selectedTags.some((t) => t.id === tag.id)) {
setSelectedTags([...selectedTags, tag]);
@@ -57,14 +56,14 @@ export function TagManager({ name, tags, label = "Tags" }: TagManagerProps) {
};
// Handle tag removal
- const handleRemoveTag = (tagId: number) => {
+ const handleRemoveTag = (tagId: string) => {
setSelectedTags(selectedTags.filter((tag) => tag.id !== tagId));
// Update form values
const currentTags = form.getValues(name) || [];
form.setValue(
name,
- currentTags.filter((id: number) => id !== tagId),
+ currentTags.filter((id: string) => id !== tagId),
{ shouldValidate: true },
);
};
diff --git a/client/src/components/common/__tests__/WorkCard.test.tsx b/client/src/components/common/__tests__/WorkCard.test.tsx
index 8551cb4..b42b1cd 100644
--- a/client/src/components/common/__tests__/WorkCard.test.tsx
+++ b/client/src/components/common/__tests__/WorkCard.test.tsx
@@ -1,19 +1,22 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { WorkCard } from '../WorkCard';
-import { WorkWithAuthor } from '@/lib/types';
+import type { Work } from '@shared/schema';
import { Toaster } from '@/components/ui/toaster';
-const mockWork: WorkWithAuthor = {
- id: 1,
+const mockWork: Work & { author: { id: string; name: string } } = {
+ id: '1',
title: 'Test Work',
slug: 'test-work',
type: 'poem',
year: 2023,
language: 'English',
+ content: 'Test content',
description: 'This is a test description.',
+ createdAt: new Date().toISOString(),
likes: 10,
- tags: [{ id: 1, name: 'Test Tag' }],
- author: { id: 1, name: 'Test Author' },
+ tags: ['test-tag'],
+ authorId: '1',
+ author: { id: '1', name: 'Test Author' },
};
describe('WorkCard', () => {
diff --git a/jest.config.cjs b/jest.config.cjs
index 04687ee..60511a1 100644
--- a/jest.config.cjs
+++ b/jest.config.cjs
@@ -3,10 +3,16 @@ module.exports = {
testEnvironment: 'jest-environment-jsdom',
setupFilesAfterEnv: ['/jest.setup.js'],
extensionsToTreatAsEsm: ['.ts', '.tsx'],
+ globals: {
+ 'ts-jest': {
+ useESM: true,
+ },
+ },
moduleNameMapper: {
'\\\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\\\.(gif|ttf|eot|svg|png)$': 'jest-transform-stub',
'^@/(.*)$': '/client/src/$1',
+ '^@shared/(.*)$': '/shared/$1',
},
transform: {
'^.+\\\\.tsx?$': ['babel-jest', { useESM: true }],
@@ -14,4 +20,8 @@ module.exports = {
transformIgnorePatterns: [
'/node_modules/(?!wouter|lucide-react)/',
],
+ testMatch: [
+ '/client/src/**/__tests__/**/*.(ts|tsx)',
+ '/client/src/**/*.(test|spec).(ts|tsx)',
+ ],
};
diff --git a/package.json b/package.json
index d4857d7..db418f1 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"check": "tsc",
"lint": "tsc --noEmit",
"test": "yarn test:unit && yarn test:e2e",
- "test:unit": "yarn jest",
+ "test:unit": "jest",
"test:e2e": "playwright test"
},
"dependencies": {