refactor(web): replace preloadQuery with fetchQuery

This commit is contained in:
Aman Varshney
2025-08-29 16:28:14 +05:30
parent 11fd8eddcb
commit 33a08d6791
8 changed files with 82 additions and 65 deletions

View File

@@ -1,5 +1,12 @@
"use client"; "use client";
import { Check, ChevronDown, ChevronRight, Copy, Terminal } from "lucide-react"; import {
Check,
ChevronDown,
ChevronRight,
Copy,
Terminal,
Zap,
} from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useState } from "react"; import { useState } from "react";
import { import {
@@ -28,7 +35,7 @@ export default function CommandSection() {
}; };
return ( return (
<div className="mb-8 grid grid-cols-1 gap-4 lg:grid-cols-2"> <div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<div className="flex h-full flex-col justify-between rounded border border-border p-4"> <div className="flex h-full flex-col justify-between rounded border border-border p-4">
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -104,7 +111,7 @@ export default function CommandSection() {
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center justify-between rounded border border-border p-3"> <div className="flex items-center justify-between rounded border border-border p-3">
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2 text-sm">
<span className="text-primary"></span> <Zap className="h-4 w-4 text-primary" />
<span className="text-foreground"> <span className="text-foreground">
Interactive configuration wizard Interactive configuration wizard
</span> </span>

View File

@@ -2,7 +2,7 @@ import NpmPackage from "./npm-package";
export default function HeroSection() { export default function HeroSection() {
return ( return (
<> <div>
<div className="mb-8 flex items-center justify-center"> <div className="mb-8 flex items-center justify-center">
<div className="flex flex-wrap items-center justify-center gap-2 sm:gap-4 md:gap-6"> <div className="flex flex-wrap items-center justify-center gap-2 sm:gap-4 md:gap-6">
<pre className="ascii-art text-primary text-xs leading-tight sm:text-sm"> <pre className="ascii-art text-primary text-xs leading-tight sm:text-sm">
@@ -47,15 +47,12 @@ export default function HeroSection() {
</div> </div>
</div> </div>
<div className="mb-6 text-center"> <div className="text-center">
<p className="mx-auto text-lg text-muted-foreground"> <p className="mx-auto text-lg text-muted-foreground">
Modern CLI for scaffolding end-to-end type-safe TypeScript projects Modern CLI for scaffolding end-to-end type-safe TypeScript projects
</p> </p>
<p className="mx-auto mt-2 max-w-2xl text-muted-foreground text-sm">
Production-ready Customizable Best practices included
</p>
<NpmPackage /> <NpmPackage />
</div> </div>
</> </div>
); );
} }

View File

@@ -1,6 +1,4 @@
"use client"; "use client";
import type { api } from "@better-t-stack/backend/convex/_generated/api";
import { type Preloaded, usePreloadedQuery } from "convex/react";
import { import {
ChevronDown, ChevronDown,
ChevronUp, ChevronUp,
@@ -26,17 +24,33 @@ import {
sortSponsors, sortSponsors,
} from "@/lib/sponsor-utils"; } from "@/lib/sponsor-utils";
export default function SponsorsSection({ type SponsorEntry = {
preloadedSponsors, sponsor: {
}: { login: string;
preloadedSponsors: Preloaded<typeof api.sponsors.getSponsors>; name: string;
}) { avatarUrl: string;
const sponsorsData = usePreloadedQuery(preloadedSponsors); websiteUrl?: string;
linkUrl: string;
customLogoUrl?: string;
type: string;
};
isOneTime: boolean;
monthlyDollars: number;
privacyLevel: string;
tierName: string;
createdAt: string;
provider: string;
};
export default function SponsorsSection({
sponsors,
}: {
sponsors: Array<SponsorEntry>;
}) {
const [showPastSponsors, setShowPastSponsors] = useState(false); const [showPastSponsors, setShowPastSponsors] = useState(false);
const sponsors = const sponsorsData =
sponsorsData.map((sponsor) => ({ sponsors.map((sponsor) => ({
...sponsor, ...sponsor,
sponsor: { sponsor: {
...sponsor.sponsor, ...sponsor.sponsor,
@@ -44,16 +58,16 @@ export default function SponsorsSection({
}, },
})) || []; })) || [];
const visibleSponsors = filterVisibleSponsors(sponsors); const visibleSponsors = filterVisibleSponsors(sponsorsData);
const sortedSponsors = sortSponsors(visibleSponsors); const sortedSponsors = sortSponsors(visibleSponsors);
const currentSponsors = filterCurrentSponsors(sortedSponsors); const currentSponsors = filterCurrentSponsors(sortedSponsors);
const pastSponsors = filterPastSponsors(sortSponsors(sponsors)); const pastSponsors = filterPastSponsors(sortSponsors(sponsorsData));
const specialSponsors = sortSpecialSponsors( const specialSponsors = sortSpecialSponsors(
filterSpecialSponsors(currentSponsors), filterSpecialSponsors(currentSponsors),
); );
return ( return (
<div className="mb-12"> <div className="">
<div className="mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap"> <div className="mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Terminal className="h-5 w-5 text-primary" /> <Terminal className="h-5 w-5 text-primary" />

View File

@@ -33,7 +33,7 @@ export default function StatsSection({
const liveNpmDownloadCount = useNpmDownloadCounter(npmPackages); const liveNpmDownloadCount = useNpmDownloadCounter(npmPackages);
return ( return (
<div className="mb-8 grid grid-cols-1 gap-4 sm:mb-12 sm:grid-cols-2 lg:grid-cols-3"> <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<Link href="/analytics"> <Link href="/analytics">
<div className="cursor-pointer rounded border border-border p-4 transition-colors hover:bg-muted/10"> <div className="cursor-pointer rounded border border-border p-4 transition-colors hover:bg-muted/10">
<div className="mb-3 flex items-center gap-2"> <div className="mb-3 flex items-center gap-2">

View File

@@ -1,7 +1,5 @@
"use client"; "use client";
import type { api } from "@better-t-stack/backend/convex/_generated/api";
import { type Preloaded, usePreloadedQuery } from "convex/react";
import { Play, Terminal } from "lucide-react"; import { Play, Terminal } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import Image from "next/image"; import Image from "next/image";
@@ -99,14 +97,13 @@ const TweetCard = ({ tweetId, index }: { tweetId: string; index: number }) => (
); );
export default function Testimonials({ export default function Testimonials({
preloadedTestimonialsTweet, videos,
preloadedTestimonialsVideos, tweets,
}: { }: {
preloadedTestimonialsTweet: Preloaded<typeof api.testimonials.getTweets>; videos: Array<{ embedId: string; title: string }>;
preloadedTestimonialsVideos: Preloaded<typeof api.testimonials.getVideos>; tweets: Array<{ tweetId: string }>;
}) { }) {
const videos = usePreloadedQuery(preloadedTestimonialsVideos).reverse(); const videosReversed = [...videos].reverse();
const tweets = usePreloadedQuery(preloadedTestimonialsTweet);
const getResponsiveColumns = (numCols: number) => { const getResponsiveColumns = (numCols: number) => {
const columns: string[][] = Array(numCols) const columns: string[][] = Array(numCols)
@@ -138,7 +135,7 @@ export default function Testimonials({
}; };
return ( return (
<div className="mb-12 w-full max-w-full overflow-hidden px-4"> <div className="w-full max-w-full overflow-hidden px-4">
<div className="mb-8"> <div className="mb-8">
<div className="mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap"> <div className="mb-6 flex flex-wrap items-center justify-between gap-2 sm:flex-nowrap">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -149,7 +146,7 @@ export default function Testimonials({
</div> </div>
<div className="hidden h-px flex-1 bg-border sm:block" /> <div className="hidden h-px flex-1 bg-border sm:block" />
<span className="w-full text-right text-muted-foreground text-xs sm:w-auto sm:text-left"> <span className="w-full text-right text-muted-foreground text-xs sm:w-auto sm:text-left">
[{videos.length} ENTRIES] [{videosReversed.length} ENTRIES]
</span> </span>
</div> </div>
@@ -160,7 +157,7 @@ export default function Testimonials({
initial="hidden" initial="hidden"
animate="visible" animate="visible"
> >
{videos.map((video, index) => ( {videosReversed.map((video, index) => (
<VideoCard <VideoCard
key={`video-${video.embedId}`} key={`video-${video.embedId}`}
video={video} video={video}
@@ -177,7 +174,7 @@ export default function Testimonials({
initial="hidden" initial="hidden"
animate="visible" animate="visible"
> >
{videos.map((video, index) => ( {videosReversed.map((video, index) => (
<VideoCard <VideoCard
key={`video-${video.embedId}`} key={`video-${video.embedId}`}
video={video} video={video}

View File

@@ -1,5 +1,5 @@
import { api } from "@better-t-stack/backend/convex/_generated/api"; import { api } from "@better-t-stack/backend/convex/_generated/api";
import { preloadQuery } from "convex/nextjs"; import { fetchQuery } from "convex/nextjs";
import CommandSection from "./_components/command-section"; import CommandSection from "./_components/command-section";
import Footer from "./_components/footer"; import Footer from "./_components/footer";
import HeroSection from "./_components/hero-section"; import HeroSection from "./_components/hero-section";
@@ -8,13 +8,14 @@ import StatsSection from "./_components/stats-section";
import Testimonials from "./_components/testimonials"; import Testimonials from "./_components/testimonials";
export default async function HomePage() { export default async function HomePage() {
const preloadedSponsors = await preloadQuery(api.sponsors.getSponsors); const sponsors = await fetchQuery(api.sponsors.getSponsors);
const preloadedTestimonialsTweet = await preloadQuery( const fetchedTweets = await fetchQuery(api.testimonials.getTweets);
api.testimonials.getTweets, const fetchedVideos = await fetchQuery(api.testimonials.getVideos);
); const videos = fetchedVideos.map((v) => ({
const preloadedTestimonialsVideos = await preloadQuery( embedId: v.embedId,
api.testimonials.getVideos, title: v.title,
); }));
const tweets = fetchedTweets.map((t) => ({ tweetId: t.tweetId }));
const minimalAnalytics = await fetch( const minimalAnalytics = await fetch(
"https://r2.better-t-stack.dev/analytics-minimal.json", "https://r2.better-t-stack.dev/analytics-minimal.json",
@@ -23,18 +24,15 @@ export default async function HomePage() {
const minimalAnalyticsData = await minimalAnalytics.json(); const minimalAnalyticsData = await minimalAnalytics.json();
return ( return (
<div className="mx-auto min-h-svh max-w-[1280px]"> <main className="mx-auto min-h-svh max-w-[1280px]">
<main className="mx-auto px-4 pt-12"> <div className="mx-auto flex flex-col gap-8 px-4 pt-12">
<HeroSection /> <HeroSection />
<CommandSection /> <CommandSection />
<StatsSection analyticsData={minimalAnalyticsData} /> <StatsSection analyticsData={minimalAnalyticsData} />
<SponsorsSection preloadedSponsors={preloadedSponsors} /> <SponsorsSection sponsors={sponsors} />
<Testimonials <Testimonials tweets={tweets} videos={videos} />
preloadedTestimonialsTweet={preloadedTestimonialsTweet} </div>
preloadedTestimonialsVideos={preloadedTestimonialsVideos}
/>
</main>
<Footer /> <Footer />
</div> </main>
); );
} }

View File

@@ -1,18 +1,24 @@
"use client"; "use client";
import type { api } from "@better-t-stack/backend/convex/_generated/api";
import { type Preloaded, usePreloadedQuery } from "convex/react";
import { Terminal } from "lucide-react"; import { Terminal } from "lucide-react";
import Footer from "../../_components/footer"; import Footer from "../../_components/footer";
import ShowcaseItem from "../_components/ShowcaseItem"; import ShowcaseItem from "../_components/ShowcaseItem";
export default function ShowcasePage({ type ShowcaseProject = {
preloadedShowcase, _id: string;
}: { _creationTime: number;
preloadedShowcase: Preloaded<typeof api.showcase.getShowcaseProjects>; title: string;
}) { description: string;
const showcaseProjects = usePreloadedQuery(preloadedShowcase); imageUrl: string;
liveUrl: string;
tags: string[];
};
export default function ShowcasePage({
showcaseProjects,
}: {
showcaseProjects: Array<ShowcaseProject>;
}) {
return ( return (
<main className="mx-auto min-h-svh max-w-[1280px]"> <main className="mx-auto min-h-svh max-w-[1280px]">
<div className="container mx-auto space-y-8 px-4 py-8 pt-16"> <div className="container mx-auto space-y-8 px-4 py-8 pt-16">

View File

@@ -1,10 +1,8 @@
import { api } from "@better-t-stack/backend/convex/_generated/api"; import { api } from "@better-t-stack/backend/convex/_generated/api";
import { preloadQuery } from "convex/nextjs"; import { fetchQuery } from "convex/nextjs";
import ShowcasePage from "./_components/showcase-page"; import ShowcasePage from "./_components/showcase-page";
export default async function Showcase() { export default async function Showcase() {
const preloadedShowcase = await preloadQuery( const showcaseProjects = await fetchQuery(api.showcase.getShowcaseProjects);
api.showcase.getShowcaseProjects, return <ShowcasePage showcaseProjects={showcaseProjects} />;
);
return <ShowcasePage preloadedShowcase={preloadedShowcase} />;
} }