diff --git a/src/app/affiliates/page.tsx b/src/app/affiliates/page.tsx index 8346591a..397bdad9 100644 --- a/src/app/affiliates/page.tsx +++ b/src/app/affiliates/page.tsx @@ -61,10 +61,10 @@ function commissionUsdHint(offer: { price_sats: number; }, btcUsd: number | null): string | null { if (offer.commission_type === "percentage" && offer.price_sats > 0) { - return `≈ $${(offer.price_sats * offer.commission_rate).toFixed(2)} USD`; + return `≈ $${(offer.price_sats * offer.commission_rate).toFixed(2)} USD`; } if (offer.commission_type === "flat" && offer.commission_flat_sats > 0 && btcUsd) { - return `≈ $${((offer.commission_flat_sats / 1e8) * btcUsd).toFixed(2)} USD`; + return `≈ $${((offer.commission_flat_sats / 1e8) * btcUsd).toFixed(2)} USD`; } return null; } @@ -241,7 +241,7 @@ async function AffiliatesList({ searchParams }: { searchParams: AffiliatesPagePr )} - {/* product_url domain hint removed — URL is hidden from public listing (#20) */} + {/* product_url domain hint removed — URL is hidden from public listing (#20) */}
diff --git a/src/app/api/users/[username]/reviews/route.ts b/src/app/api/users/[username]/reviews/route.ts index f1dd62a4..90d9a6da 100644 --- a/src/app/api/users/[username]/reviews/route.ts +++ b/src/app/api/users/[username]/reviews/route.ts @@ -55,9 +55,26 @@ export async function GET( // Calculate average rating from all reviews const totalReviews = count || 0; let averageRating = 0; - if (reviews && reviews.length > 0) { - const sumRatings = reviews.reduce((sum, r) => sum + r.rating, 0); - averageRating = totalReviews > 0 ? sumRatings / reviews.length : 0; + if (totalReviews > 0) { + const { data: allRatings, error: ratingsError } = await supabase + .from("reviews") + .select("rating") + .eq("reviewee_id", profile.id) + .range(0, totalReviews - 1); + + if (ratingsError) { + return NextResponse.json({ error: ratingsError.message }, { status: 400 }); + } + + if (!allRatings || allRatings.length !== totalReviews) { + return NextResponse.json( + { error: "Unable to calculate average rating" }, + { status: 500 } + ); + } + + const sumRatings = allRatings.reduce((sum, review) => sum + review.rating, 0); + averageRating = sumRatings / allRatings.length; } return NextResponse.json({ diff --git a/src/app/directory/page.tsx b/src/app/directory/page.tsx index 419a9edc..054ca9f4 100644 --- a/src/app/directory/page.tsx +++ b/src/app/directory/page.tsx @@ -85,7 +85,7 @@ async function DirectoryList({
@@ -294,12 +294,12 @@ export default async function DirectoryPage({

- Discover projects built by the community. List yours for 50 âš¡ + Discover projects built by the community. List yours for 50 ⚡ sats.

@@ -338,7 +338,7 @@ export default async function DirectoryPage({ })}`} className="ml-1 hover:text-destructive" > - ✕ + ✕ diff --git a/src/app/for-hire/[[...tags]]/page.tsx b/src/app/for-hire/[[...tags]]/page.tsx index d5bc9fb0..43edb8f1 100644 --- a/src/app/for-hire/[[...tags]]/page.tsx +++ b/src/app/for-hire/[[...tags]]/page.tsx @@ -39,7 +39,7 @@ export async function generateMetadata({ params }: GigsPageProps): Promise c.toUpperCase())); } query = query.overlaps("skills_required", [...expandedTags]); @@ -251,7 +251,7 @@ export default async function ForHirePage({ params, searchParams }: GigsPageProp

I will...

People and agents offering their services. Want to hire instead?{" "} - Post a gig → + Post a gig →

}> diff --git a/src/app/gigs/[[...tags]]/page.tsx b/src/app/gigs/[[...tags]]/page.tsx index cda50f1f..9ed56b42 100644 --- a/src/app/gigs/[[...tags]]/page.tsx +++ b/src/app/gigs/[[...tags]]/page.tsx @@ -120,7 +120,7 @@ async function GigsList({ expandedTags.add(tag.toLowerCase()); expandedTags.add(tag.charAt(0).toUpperCase() + tag.slice(1)); // Title case expandedTags.add(tag.toUpperCase()); - // Handle multi-word: "node.js" → "Node.js", "next.js" → "Next.js" + // Handle multi-word: "node.js" → "Node.js", "next.js" → "Next.js" expandedTags.add(tag.replace(/\b\w/g, c => c.toUpperCase())); } query = query.overlaps("skills_required", [...expandedTags]); @@ -258,7 +258,7 @@ export default async function GigsPage({ params, searchParams }: GigsPageProps)

Gigs (Hiring)

Clients posting work they need done. Looking for work instead?{" "} - Browse "I will..." listings → + Browse "I will..." listings →

}> diff --git a/src/app/mcp/page.tsx b/src/app/mcp/page.tsx index b9414845..b0bbe67b 100644 --- a/src/app/mcp/page.tsx +++ b/src/app/mcp/page.tsx @@ -15,14 +15,14 @@ import { parsePageParam } from "@/lib/pagination"; export const metadata: Metadata = { title: "MCP Server Marketplace | ugig.net", description: - "Browse MCP servers — tools, integrations, and APIs that AI agents can connect to via the Model Context Protocol.", + "Browse MCP servers — tools, integrations, and APIs that AI agents can connect to via the Model Context Protocol.", alternates: { canonical: "/mcp", }, openGraph: { title: "MCP Server Marketplace | ugig.net", description: - "Browse MCP servers — tools, integrations, and APIs that AI agents can connect to via the Model Context Protocol.", + "Browse MCP servers — tools, integrations, and APIs that AI agents can connect to via the Model Context Protocol.", url: "/mcp", type: "website", }, @@ -30,7 +30,7 @@ export const metadata: Metadata = { card: "summary_large_image", title: "MCP Server Marketplace | ugig.net", description: - "Browse MCP servers — tools, integrations, and APIs that AI agents can connect to via the Model Context Protocol.", + "Browse MCP servers — tools, integrations, and APIs that AI agents can connect to via the Model Context Protocol.", }, }; @@ -156,7 +156,7 @@ async function McpList({ searchParams }: { searchParams: McpPageProps["searchPar {btcUsd && (

- ≈ ${((listing.price_sats / 1e8) * btcUsd).toFixed(2)} + ≈ ${((listing.price_sats / 1e8) * btcUsd).toFixed(2)}

)} @@ -315,7 +315,7 @@ export default async function McpPage({ searchParams }: McpPageProps) {

- Browse MCP servers — tools, integrations, and APIs for AI agents. + Browse MCP servers — tools, integrations, and APIs for AI agents.

{/* Filters */} @@ -402,7 +402,7 @@ export default async function McpPage({ searchParams }: McpPageProps) { ...(queryParams.category ? { category: queryParams.category } : {}), ...(queryParams.sort ? { sort: queryParams.sort } : {}), })}`} className="ml-1 hover:text-destructive"> - ✕ + ✕ diff --git a/src/app/prompts/page.tsx b/src/app/prompts/page.tsx index 76fedb95..95a79c50 100644 --- a/src/app/prompts/page.tsx +++ b/src/app/prompts/page.tsx @@ -15,14 +15,14 @@ import { parsePageParam } from "@/lib/pagination"; export const metadata: Metadata = { title: "Prompt Marketplace | ugig.net", description: - "Browse AI prompts — expertly crafted prompts for coding, writing, analysis, creative work, and more.", + "Browse AI prompts — expertly crafted prompts for coding, writing, analysis, creative work, and more.", alternates: { canonical: "/prompts", }, openGraph: { title: "Prompt Marketplace | ugig.net", description: - "Browse AI prompts — expertly crafted prompts for coding, writing, analysis, creative work, and more.", + "Browse AI prompts — expertly crafted prompts for coding, writing, analysis, creative work, and more.", url: "/prompts", type: "website", }, @@ -30,7 +30,7 @@ export const metadata: Metadata = { card: "summary_large_image", title: "Prompt Marketplace | ugig.net", description: - "Browse AI prompts — expertly crafted prompts for coding, writing, analysis, creative work, and more.", + "Browse AI prompts — expertly crafted prompts for coding, writing, analysis, creative work, and more.", }, }; @@ -156,7 +156,7 @@ async function PromptList({ searchParams }: { searchParams: PromptsPageProps["se {btcUsd && (

- ≈ ${((listing.price_sats / 1e8) * btcUsd).toFixed(2)} + ≈ ${((listing.price_sats / 1e8) * btcUsd).toFixed(2)}

)} @@ -325,7 +325,7 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) {

- Browse AI prompts — expertly crafted for coding, writing, analysis, and more. + Browse AI prompts — expertly crafted for coding, writing, analysis, and more.

{/* Filters */} @@ -412,7 +412,7 @@ export default async function PromptsPage({ searchParams }: PromptsPageProps) { ...(queryParams.category ? { category: queryParams.category } : {}), ...(queryParams.sort ? { sort: queryParams.sort } : {}), })}`} className="ml-1 hover:text-destructive"> - ✕ + ✕ diff --git a/src/app/skills/page.tsx b/src/app/skills/page.tsx index cb9c984d..6f1d9731 100644 --- a/src/app/skills/page.tsx +++ b/src/app/skills/page.tsx @@ -155,7 +155,7 @@ async function SkillsList({ searchParams }: { searchParams: SkillsPageProps["sea {btcUsd && (

- ≈ ${((listing.price_sats / 1e8) * btcUsd).toFixed(2)} + ≈ ${((listing.price_sats / 1e8) * btcUsd).toFixed(2)}

)} @@ -313,7 +313,7 @@ export default async function SkillsPage({ searchParams }: SkillsPageProps) {

- Browse agent skills — install tools, automations, and workflows. + Browse agent skills — install tools, automations, and workflows.

{/* Filters */} @@ -391,7 +391,7 @@ export default async function SkillsPage({ searchParams }: SkillsPageProps) { })}`} className="text-xs text-muted-foreground hover:text-destructive flex items-center ml-1" > - ✕ clear + ✕ clear )} @@ -430,7 +430,7 @@ export default async function SkillsPage({ searchParams }: SkillsPageProps) { ...(queryParams.category ? { category: queryParams.category } : {}), ...(queryParams.sort ? { sort: queryParams.sort } : {}), })}`} className="ml-1 hover:text-destructive"> - ✕ + ✕ diff --git a/src/lib/pagination.test.ts b/src/lib/pagination.test.ts index 92fbb8c0..a4aae4a7 100644 --- a/src/lib/pagination.test.ts +++ b/src/lib/pagination.test.ts @@ -19,6 +19,6 @@ describe("parsePageParam", () => { }); it("caps very large page values", () => { - expect(parsePageParam("999999999")).toBe(100_000); + expect(parsePageParam("999999999")).toBe(1_000); }); }); diff --git a/src/lib/pagination.ts b/src/lib/pagination.ts index 3b162272..3ef10d87 100644 --- a/src/lib/pagination.ts +++ b/src/lib/pagination.ts @@ -1,4 +1,4 @@ -const DEFAULT_MAX_PAGE = 100_000; +const DEFAULT_MAX_PAGE = 1_000; export function parsePageParam( value: string | null | undefined,