mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
Add Todo button to homepage when examples are included
Add project creation time display in success message
This commit is contained in:
5
.changeset/fresh-books-sink.md
Normal file
5
.changeset/fresh-books-sink.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-better-t-stack": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add Todo button to homepage when examples are included
|
||||||
@@ -22,10 +22,7 @@
|
|||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": ["dist", "template"],
|
||||||
"dist",
|
|
||||||
"template"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^0.10.0",
|
"@clack/prompts": "^0.10.0",
|
||||||
"commander": "^13.1.0",
|
"commander": "^13.1.0",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ async function setupTodoExample(
|
|||||||
}
|
}
|
||||||
|
|
||||||
await updateHeaderWithTodoLink(projectDir, auth);
|
await updateHeaderWithTodoLink(projectDir, auth);
|
||||||
|
await addTodoButtonToHomepage(projectDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,3 +129,28 @@ async function updateRouterIndex(projectDir: string): Promise<void> {
|
|||||||
await fs.writeFile(routerFile, routerContent);
|
await fs.writeFile(routerFile, routerContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function addTodoButtonToHomepage(projectDir: string): Promise<void> {
|
||||||
|
const indexPath = path.join(
|
||||||
|
projectDir,
|
||||||
|
"packages/client/src/routes/index.tsx",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (await fs.pathExists(indexPath)) {
|
||||||
|
let indexContent = await fs.readFile(indexPath, "utf8");
|
||||||
|
|
||||||
|
indexContent = indexContent.replace(
|
||||||
|
/<div id="buttons"><\/div>/,
|
||||||
|
`<div id="buttons" className="mt-4 flex flex-col gap-4 sm:flex-row sm:items-center">
|
||||||
|
<Button asChild>
|
||||||
|
<Link to="/todos" className="flex items-center">
|
||||||
|
View Todo Demo
|
||||||
|
<ArrowRight className="ml-1 h-4 w-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>`,
|
||||||
|
);
|
||||||
|
|
||||||
|
await fs.writeFile(indexPath, indexContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ process.on("SIGINT", () => {
|
|||||||
const program = new Command();
|
const program = new Command();
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
program
|
program
|
||||||
.name("create-better-t-stack")
|
.name("create-better-t-stack")
|
||||||
.description("Create a new Better-T Stack project")
|
.description("Create a new Better-T Stack project")
|
||||||
@@ -169,7 +171,12 @@ async function main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
outro(pc.magenta("Project created successfully!"));
|
const elapsedTimeInSeconds = ((Date.now() - startTime) / 1000).toFixed(2);
|
||||||
|
outro(
|
||||||
|
pc.magenta(
|
||||||
|
`Project created successfully in ${pc.bold(elapsedTimeInSeconds)} seconds!`,
|
||||||
|
),
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
s.stop(pc.red("Failed"));
|
s.stop(pc.red("Failed"));
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ export async function gatherConfig(
|
|||||||
? getTursoSetupChoice(flags.turso)
|
? getTursoSetupChoice(flags.turso)
|
||||||
: Promise.resolve(false),
|
: Promise.resolve(false),
|
||||||
addons: () => getAddonsChoice(flags.addons),
|
addons: () => getAddonsChoice(flags.addons),
|
||||||
examples: () => getExamplesChoice(flags.examples),
|
examples: ({ results }) =>
|
||||||
|
getExamplesChoice(flags.examples, results.database),
|
||||||
git: () => getGitChoice(flags.git),
|
git: () => getGitChoice(flags.git),
|
||||||
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
||||||
noInstall: () => getNoInstallChoice(flags.noInstall),
|
noInstall: () => getNoInstallChoice(flags.noInstall),
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
||||||
import pc from "picocolors";
|
import pc from "picocolors";
|
||||||
import { DEFAULT_CONFIG } from "../constants";
|
import { DEFAULT_CONFIG } from "../constants";
|
||||||
import type { ProjectExamples } from "../types";
|
import type { ProjectDatabase, ProjectExamples } from "../types";
|
||||||
|
|
||||||
export async function getExamplesChoice(
|
export async function getExamplesChoice(
|
||||||
examples?: ProjectExamples[],
|
examples?: ProjectExamples[],
|
||||||
|
database?: ProjectDatabase,
|
||||||
): Promise<ProjectExamples[]> {
|
): Promise<ProjectExamples[]> {
|
||||||
if (examples !== undefined) return examples;
|
if (examples !== undefined) return examples;
|
||||||
|
|
||||||
|
if (database === "none") return [];
|
||||||
|
|
||||||
const response = await multiselect<ProjectExamples>({
|
const response = await multiselect<ProjectExamples>({
|
||||||
message: "Which examples would you like to include?",
|
message: "Which examples would you like to include?",
|
||||||
options: [
|
options: [
|
||||||
|
|||||||
@@ -67,15 +67,7 @@ function HomeComponent() {
|
|||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
<div id="buttons"></div>
|
||||||
<div className="mt-4 flex flex-col gap-4 sm:flex-row sm:items-center">
|
|
||||||
<Button asChild>
|
|
||||||
<Link to="/todos" className="flex items-center">
|
|
||||||
View Todo Demo
|
|
||||||
<ArrowRight className="ml-1 h-4 w-4" />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { trpc } from "@/utils/trpc";
|
|
||||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
|
||||||
import { ArrowRight } from "lucide-react";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
|
||||||
component: HomeComponent,
|
|
||||||
});
|
|
||||||
|
|
||||||
const TITLE_TEXT = `
|
|
||||||
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
||||||
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
||||||
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
|
||||||
██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗
|
|
||||||
██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║
|
|
||||||
╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
||||||
|
|
||||||
████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗
|
|
||||||
╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
|
||||||
██║ ███████╗ ██║ ███████║██║ █████╔╝
|
|
||||||
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
||||||
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
||||||
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
||||||
`;
|
|
||||||
|
|
||||||
function HomeComponent() {
|
|
||||||
const healthCheck = trpc.healthCheck.useQuery();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="container mx-auto max-w-3xl px-4 py-2">
|
|
||||||
<pre className="overflow-x-auto font-mono text-sm">{TITLE_TEXT}</pre>
|
|
||||||
<div className="grid gap-6">
|
|
||||||
<section className="rounded-lg border p-4">
|
|
||||||
<h2 className="mb-2 font-medium">API Status</h2>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div
|
|
||||||
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
|
|
||||||
/>
|
|
||||||
<span className="text-sm text-muted-foreground">
|
|
||||||
{healthCheck.isLoading
|
|
||||||
? "Checking..."
|
|
||||||
: healthCheck.data
|
|
||||||
? "Connected"
|
|
||||||
: "Disconnected"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2 className="mb-3 font-medium">Core Features</h2>
|
|
||||||
<ul className="grid grid-cols-2 gap-3">
|
|
||||||
<FeatureItem
|
|
||||||
title="Type-Safe API"
|
|
||||||
description="End-to-end type safety with tRPC"
|
|
||||||
/>
|
|
||||||
<FeatureItem
|
|
||||||
title="Modern React"
|
|
||||||
description="TanStack Router + TanStack Query"
|
|
||||||
/>
|
|
||||||
<FeatureItem
|
|
||||||
title="Fast Backend"
|
|
||||||
description="Lightweight Hono server"
|
|
||||||
/>
|
|
||||||
<FeatureItem
|
|
||||||
title="Beautiful UI"
|
|
||||||
description="TailwindCSS + shadcn/ui components"
|
|
||||||
/>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div className="mt-4 flex flex-col gap-4 sm:flex-row sm:items-center">
|
|
||||||
<Button asChild>
|
|
||||||
<Link to="/todos" className="flex items-center">
|
|
||||||
View Todo Demo
|
|
||||||
<ArrowRight className="ml-1 h-4 w-4" />
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function FeatureItem({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<li className="border-l-2 border-primary py-1 pl-3">
|
|
||||||
<h3 className="font-medium">{title}</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">{description}</p>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user