mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
refactor(web): replace preloadQuery with fetchQuery
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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} />;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user