mirror of
https://github.com/FranP-code/create-better-t-stack.git
synced 2025-10-12 23:52:15 +00:00
rename features to addons
This commit is contained in:
5
.changeset/early-falcons-itch.md
Normal file
5
.changeset/early-falcons-itch.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"create-better-t-stack": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
rename features to addons
|
||||||
@@ -11,7 +11,7 @@ export const DEFAULT_CONFIG: ProjectConfig = {
|
|||||||
database: "sqlite",
|
database: "sqlite",
|
||||||
orm: "drizzle",
|
orm: "drizzle",
|
||||||
auth: true,
|
auth: true,
|
||||||
features: [],
|
addons: [],
|
||||||
git: true,
|
git: true,
|
||||||
packageManager: "npm",
|
packageManager: "npm",
|
||||||
noInstall: false,
|
noInstall: false,
|
||||||
|
|||||||
@@ -2,21 +2,18 @@ import path from "node:path";
|
|||||||
import { log } from "@clack/prompts";
|
import { log } from "@clack/prompts";
|
||||||
import fs from "fs-extra";
|
import fs from "fs-extra";
|
||||||
import pc from "picocolors";
|
import pc from "picocolors";
|
||||||
import type { ProjectFeature } from "../types";
|
import type { ProjectAddons } from "../types";
|
||||||
|
|
||||||
export async function setupFeatures(
|
export async function setupAddons(projectDir: string, addons: ProjectAddons[]) {
|
||||||
projectDir: string,
|
if (addons.includes("docker")) {
|
||||||
features: ProjectFeature[],
|
|
||||||
) {
|
|
||||||
if (features.includes("docker")) {
|
|
||||||
await setupDocker(projectDir);
|
await setupDocker(projectDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.includes("github-actions")) {
|
if (addons.includes("github-actions")) {
|
||||||
await setupGithubActions(projectDir);
|
await setupGithubActions(projectDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (features.includes("SEO")) {
|
if (addons.includes("SEO")) {
|
||||||
log.info(
|
log.info(
|
||||||
pc.yellow(
|
pc.yellow(
|
||||||
"SEO feature is still a work-in-progress and will be available in a future update.",
|
"SEO feature is still a work-in-progress and will be available in a future update.",
|
||||||
@@ -5,10 +5,10 @@ import fs from "fs-extra";
|
|||||||
import pc from "picocolors";
|
import pc from "picocolors";
|
||||||
import { PKG_ROOT } from "../constants";
|
import { PKG_ROOT } from "../constants";
|
||||||
import type { ProjectConfig } from "../types";
|
import type { ProjectConfig } from "../types";
|
||||||
|
import { setupAddons } from "./addons-setup";
|
||||||
import { configureAuth } from "./auth-setup";
|
import { configureAuth } from "./auth-setup";
|
||||||
import { createReadme } from "./create-readme";
|
import { createReadme } from "./create-readme";
|
||||||
import { setupDatabase } from "./db-setup";
|
import { setupDatabase } from "./db-setup";
|
||||||
import { setupFeatures } from "./feature-setup";
|
|
||||||
import { displayPostInstallInstructions } from "./post-installation";
|
import { displayPostInstallInstructions } from "./post-installation";
|
||||||
|
|
||||||
export async function createProject(options: ProjectConfig): Promise<string> {
|
export async function createProject(options: ProjectConfig): Promise<string> {
|
||||||
@@ -92,8 +92,8 @@ export async function createProject(options: ProjectConfig): Promise<string> {
|
|||||||
await $({ cwd: projectDir })`git init`;
|
await $({ cwd: projectDir })`git init`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.features.length > 0) {
|
if (options.addons.length > 0) {
|
||||||
await setupFeatures(projectDir, options.features);
|
await setupAddons(projectDir, options.addons);
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageJsonPath = path.join(projectDir, "package.json");
|
const packageJsonPath = path.join(projectDir, "package.json");
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function generateReadmeContent(options: ProjectConfig): string {
|
|||||||
packageManager,
|
packageManager,
|
||||||
database,
|
database,
|
||||||
auth,
|
auth,
|
||||||
features = [],
|
addons = [],
|
||||||
orm = "drizzle",
|
orm = "drizzle",
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ This project was created with [Better-T-Stack](https://github.com/better-t-stack
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
${generateFeaturesList(database, auth, features, orm)}
|
${generateFeaturesList(database, auth, addons, orm)}
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export async function setupDatabase(
|
|||||||
|
|
||||||
if (databaseType === "none") {
|
if (databaseType === "none") {
|
||||||
await fs.remove(path.join(serverDir, "src/db"));
|
await fs.remove(path.join(serverDir, "src/db"));
|
||||||
log.info(pc.yellow("Database configuration removed"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export async function installDependencies({
|
|||||||
packageManager,
|
packageManager,
|
||||||
}: InstallDependenciesOptions) {
|
}: InstallDependenciesOptions) {
|
||||||
const s = spinner();
|
const s = spinner();
|
||||||
log.info(pc.blue(`Installing dependencies using ${packageManager}...`));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
s.start(`Running ${packageManager} install...`);
|
s.start(`Running ${packageManager} install...`);
|
||||||
@@ -34,7 +33,7 @@ export async function installDependencies({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
s.stop(pc.green("Dependencies installed successfully"));
|
s.stop("Dependencies installed successfully");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
s.stop(pc.red("Failed to install dependencies"));
|
s.stop(pc.red("Failed to install dependencies"));
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { DEFAULT_CONFIG } from "./constants";
|
|||||||
import { createProject } from "./helpers/create-project";
|
import { createProject } from "./helpers/create-project";
|
||||||
import { installDependencies } from "./helpers/install-dependencies";
|
import { installDependencies } from "./helpers/install-dependencies";
|
||||||
import { gatherConfig } from "./prompts/config-prompts";
|
import { gatherConfig } from "./prompts/config-prompts";
|
||||||
import type { ProjectConfig, ProjectFeature } from "./types";
|
import type { ProjectAddons, ProjectConfig } from "./types";
|
||||||
import { displayConfig } from "./utils/display-config";
|
import { displayConfig } from "./utils/display-config";
|
||||||
import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
|
import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
|
||||||
import { getVersion } from "./utils/get-version";
|
import { getVersion } from "./utils/get-version";
|
||||||
@@ -33,7 +33,7 @@ async function main() {
|
|||||||
.option("--docker", "Include Docker setup")
|
.option("--docker", "Include Docker setup")
|
||||||
.option("--github-actions", "Include GitHub Actions")
|
.option("--github-actions", "Include GitHub Actions")
|
||||||
.option("--seo", "Include SEO setup")
|
.option("--seo", "Include SEO setup")
|
||||||
.option("--no-features", "Skip all additional features")
|
.option("--no-addons", "Skip all additional addons")
|
||||||
.option("--git", "Include git setup")
|
.option("--git", "Include git setup")
|
||||||
.option("--no-git", "Skip git initialization")
|
.option("--no-git", "Skip git initialization")
|
||||||
.option("--npm", "Use npm package manager")
|
.option("--npm", "Use npm package manager")
|
||||||
@@ -75,15 +75,15 @@ async function main() {
|
|||||||
...((options.docker ||
|
...((options.docker ||
|
||||||
options.githubActions ||
|
options.githubActions ||
|
||||||
options.seo ||
|
options.seo ||
|
||||||
options.features === false) && {
|
options.addons === false) && {
|
||||||
features:
|
addons:
|
||||||
options.features === false
|
options.addons === false
|
||||||
? []
|
? []
|
||||||
: ([
|
: ([
|
||||||
...(options.docker ? ["docker"] : []),
|
...(options.docker ? ["docker"] : []),
|
||||||
...(options.githubActions ? ["github-actions"] : []),
|
...(options.githubActions ? ["github-actions"] : []),
|
||||||
...(options.seo ? ["SEO"] : []),
|
...(options.seo ? ["SEO"] : []),
|
||||||
] as ProjectFeature[]),
|
] as ProjectAddons[]),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,9 +117,9 @@ async function main() {
|
|||||||
: DEFAULT_CONFIG.noInstall,
|
: DEFAULT_CONFIG.noInstall,
|
||||||
packageManager:
|
packageManager:
|
||||||
flagConfig.packageManager ?? DEFAULT_CONFIG.packageManager,
|
flagConfig.packageManager ?? DEFAULT_CONFIG.packageManager,
|
||||||
features: flagConfig.features?.length
|
addons: flagConfig.addons?.length
|
||||||
? flagConfig.features
|
? flagConfig.addons
|
||||||
: DEFAULT_CONFIG.features,
|
: DEFAULT_CONFIG.addons,
|
||||||
turso:
|
turso:
|
||||||
"turso" in options
|
"turso" in options
|
||||||
? options.turso
|
? options.turso
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
import { cancel, isCancel, multiselect } from "@clack/prompts";
|
||||||
import pc from "picocolors";
|
import pc from "picocolors";
|
||||||
import type { ProjectFeature } from "../types";
|
import type { ProjectAddons } from "../types";
|
||||||
|
|
||||||
export async function getFeaturesChoice(
|
export async function getAddonsChoice(
|
||||||
features?: ProjectFeature[],
|
Addons?: ProjectAddons[],
|
||||||
): Promise<ProjectFeature[]> {
|
): Promise<ProjectAddons[]> {
|
||||||
if (features !== undefined) return features;
|
if (Addons !== undefined) return Addons;
|
||||||
|
|
||||||
const response = await multiselect<ProjectFeature>({
|
const response = await multiselect<ProjectAddons>({
|
||||||
message: "Which features would you like to add?",
|
message: "Which Addons would you like to add?",
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
value: "docker",
|
value: "docker",
|
||||||
@@ -2,14 +2,14 @@ import { cancel, group } from "@clack/prompts";
|
|||||||
import pc from "picocolors";
|
import pc from "picocolors";
|
||||||
import type {
|
import type {
|
||||||
PackageManager,
|
PackageManager,
|
||||||
|
ProjectAddons,
|
||||||
ProjectConfig,
|
ProjectConfig,
|
||||||
ProjectDatabase,
|
ProjectDatabase,
|
||||||
ProjectFeature,
|
|
||||||
ProjectOrm,
|
ProjectOrm,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
import { getAddonsChoice } from "./addons";
|
||||||
import { getAuthChoice } from "./auth";
|
import { getAuthChoice } from "./auth";
|
||||||
import { getDatabaseChoice } from "./database";
|
import { getDatabaseChoice } from "./database";
|
||||||
import { getFeaturesChoice } from "./features";
|
|
||||||
import { getGitChoice } from "./git";
|
import { getGitChoice } from "./git";
|
||||||
import { getNoInstallChoice } from "./install";
|
import { getNoInstallChoice } from "./install";
|
||||||
import { getORMChoice } from "./orm";
|
import { getORMChoice } from "./orm";
|
||||||
@@ -22,7 +22,7 @@ interface PromptGroupResults {
|
|||||||
database: ProjectDatabase;
|
database: ProjectDatabase;
|
||||||
orm: ProjectOrm;
|
orm: ProjectOrm;
|
||||||
auth: boolean;
|
auth: boolean;
|
||||||
features: ProjectFeature[];
|
addons: ProjectAddons[];
|
||||||
git: boolean;
|
git: boolean;
|
||||||
packageManager: PackageManager;
|
packageManager: PackageManager;
|
||||||
noInstall: boolean;
|
noInstall: boolean;
|
||||||
@@ -46,7 +46,7 @@ export async function gatherConfig(
|
|||||||
results.database === "sqlite"
|
results.database === "sqlite"
|
||||||
? getTursoSetupChoice(flags.turso)
|
? getTursoSetupChoice(flags.turso)
|
||||||
: Promise.resolve(false),
|
: Promise.resolve(false),
|
||||||
features: () => getFeaturesChoice(flags.features),
|
addons: () => getAddonsChoice(flags.addons),
|
||||||
git: () => getGitChoice(flags.git),
|
git: () => getGitChoice(flags.git),
|
||||||
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
packageManager: () => getPackageManagerChoice(flags.packageManager),
|
||||||
noInstall: () => getNoInstallChoice(flags.noInstall),
|
noInstall: () => getNoInstallChoice(flags.noInstall),
|
||||||
@@ -64,7 +64,7 @@ export async function gatherConfig(
|
|||||||
database: result.database,
|
database: result.database,
|
||||||
orm: result.orm,
|
orm: result.orm,
|
||||||
auth: result.auth,
|
auth: result.auth,
|
||||||
features: result.features,
|
addons: result.addons,
|
||||||
git: result.git,
|
git: result.git,
|
||||||
packageManager: result.packageManager,
|
packageManager: result.packageManager,
|
||||||
noInstall: result.noInstall,
|
noInstall: result.noInstall,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export async function getNoInstallChoice(
|
|||||||
if (noInstall !== undefined) return noInstall;
|
if (noInstall !== undefined) return noInstall;
|
||||||
|
|
||||||
const response = await confirm({
|
const response = await confirm({
|
||||||
message: "Install dependencies after creating project?",
|
message: "Install dependencies?",
|
||||||
initialValue: !DEFAULT_CONFIG.noInstall,
|
initialValue: !DEFAULT_CONFIG.noInstall,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
export type ProjectDatabase = "sqlite" | "postgres" | "none";
|
export type ProjectDatabase = "sqlite" | "postgres" | "none";
|
||||||
export type ProjectOrm = "drizzle" | "prisma" | "none";
|
export type ProjectOrm = "drizzle" | "prisma" | "none";
|
||||||
export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
||||||
export type ProjectFeature = "docker" | "github-actions" | "SEO";
|
export type ProjectAddons = "docker" | "github-actions" | "SEO";
|
||||||
|
|
||||||
export interface ProjectConfig {
|
export interface ProjectConfig {
|
||||||
projectName: string;
|
projectName: string;
|
||||||
database: ProjectDatabase;
|
database: ProjectDatabase;
|
||||||
orm: ProjectOrm;
|
orm: ProjectOrm;
|
||||||
auth: boolean;
|
auth: boolean;
|
||||||
features: ProjectFeature[];
|
addons: ProjectAddons[];
|
||||||
git: boolean;
|
git: boolean;
|
||||||
packageManager: PackageManager;
|
packageManager: PackageManager;
|
||||||
noInstall?: boolean;
|
noInstall?: boolean;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ export function displayConfig(config: Partial<ProjectConfig>) {
|
|||||||
if (config.auth !== undefined) {
|
if (config.auth !== undefined) {
|
||||||
configDisplay.push(`${pc.blue("Authentication:")} ${config.auth}`);
|
configDisplay.push(`${pc.blue("Authentication:")} ${config.auth}`);
|
||||||
}
|
}
|
||||||
if (config.features?.length) {
|
if (config.addons?.length) {
|
||||||
configDisplay.push(`${pc.blue("Features:")} ${config.features.join(", ")}`);
|
configDisplay.push(`${pc.blue("Addons:")} ${config.addons.join(", ")}`);
|
||||||
}
|
}
|
||||||
if (config.git !== undefined) {
|
if (config.git !== undefined) {
|
||||||
configDisplay.push(`${pc.blue("Git Init:")} ${config.git}`);
|
configDisplay.push(`${pc.blue("Git Init:")} ${config.git}`);
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ export function generateReproducibleCommand(config: ProjectConfig): string {
|
|||||||
flags.push(`--${config.packageManager}`);
|
flags.push(`--${config.packageManager}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.features.length > 0) {
|
if (config.addons.length > 0) {
|
||||||
for (const feature of config.features) {
|
for (const addon of config.addons) {
|
||||||
flags.push(`--${feature}`);
|
flags.push(`--${addon}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
flags.push("--no-features");
|
flags.push("--no-addons");
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseCommand = "npx create-better-t-stack";
|
const baseCommand = "npx create-better-t-stack";
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ const CodeContainer = () => {
|
|||||||
<span className="text-yellow-400">yes</span>
|
<span className="text-yellow-400">yes</span>
|
||||||
</p>
|
</p>
|
||||||
<p className="text-white">
|
<p className="text-white">
|
||||||
Features:{" "}
|
Addons:{" "}
|
||||||
<span className="text-yellow-400">
|
<span className="text-yellow-400">
|
||||||
docker, github-actions, SEO
|
docker, github-actions, SEO
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ interface ActiveNodes {
|
|||||||
orm: string;
|
orm: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
packageManager: string;
|
packageManager: string;
|
||||||
features: {
|
addons: {
|
||||||
docker: boolean;
|
docker: boolean;
|
||||||
githubActions: boolean;
|
githubActions: boolean;
|
||||||
seo: boolean;
|
seo: boolean;
|
||||||
@@ -62,7 +62,7 @@ const CustomizableStack = () => {
|
|||||||
orm: "drizzle",
|
orm: "drizzle",
|
||||||
auth: "better-auth",
|
auth: "better-auth",
|
||||||
packageManager: "npm",
|
packageManager: "npm",
|
||||||
features: {
|
addons: {
|
||||||
docker: false,
|
docker: false,
|
||||||
githubActions: false,
|
githubActions: false,
|
||||||
seo: false,
|
seo: false,
|
||||||
@@ -128,10 +128,10 @@ const CustomizableStack = () => {
|
|||||||
setActiveNodes((prev) => ({
|
setActiveNodes((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[category]: techId,
|
[category]: techId,
|
||||||
...(category === "features" && {
|
...(category === "addons" && {
|
||||||
features: {
|
addons: {
|
||||||
...prev.features,
|
...prev.addons,
|
||||||
[techId]: !prev.features[techId as keyof typeof prev.features],
|
[techId]: !prev.addons[techId as keyof typeof prev.addons],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
@@ -342,16 +342,15 @@ const CustomizableStack = () => {
|
|||||||
const command = "npx create-better-t-stack my-app";
|
const command = "npx create-better-t-stack my-app";
|
||||||
const flags: string[] = [];
|
const flags: string[] = [];
|
||||||
|
|
||||||
// Check if all defaults are being used
|
|
||||||
const isAllDefaults =
|
const isAllDefaults =
|
||||||
activeNodes.database === "sqlite" &&
|
activeNodes.database === "sqlite" &&
|
||||||
activeNodes.auth === "better-auth" &&
|
activeNodes.auth === "better-auth" &&
|
||||||
activeNodes.orm === "drizzle" &&
|
activeNodes.orm === "drizzle" &&
|
||||||
activeNodes.packageManager === "npm" &&
|
activeNodes.packageManager === "npm" &&
|
||||||
activeNodes.features.git === true &&
|
activeNodes.addons.git === true &&
|
||||||
!activeNodes.features.docker &&
|
!activeNodes.addons.docker &&
|
||||||
!activeNodes.features.githubActions &&
|
!activeNodes.addons.githubActions &&
|
||||||
!activeNodes.features.seo;
|
!activeNodes.addons.seo;
|
||||||
|
|
||||||
// If using all defaults, just use -y flag
|
// If using all defaults, just use -y flag
|
||||||
if (isAllDefaults) {
|
if (isAllDefaults) {
|
||||||
@@ -387,19 +386,19 @@ const CustomizableStack = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Feature flags
|
// Feature flags
|
||||||
if (activeNodes.features.docker) {
|
if (activeNodes.addons.docker) {
|
||||||
flags.push("--docker");
|
flags.push("--docker");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeNodes.features.githubActions) {
|
if (activeNodes.addons.githubActions) {
|
||||||
flags.push("--github-actions");
|
flags.push("--github-actions");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeNodes.features.seo) {
|
if (activeNodes.addons.seo) {
|
||||||
flags.push("--seo");
|
flags.push("--seo");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!activeNodes.features.git) {
|
if (!activeNodes.addons.git) {
|
||||||
flags.push("--no-git");
|
flags.push("--no-git");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,21 +407,17 @@ const CustomizableStack = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full max-w-5xl mx-auto z-50 mt-24">
|
<div className="relative w-full max-w-5xl mx-auto z-50 mt-24">
|
||||||
{/* Command Display - Fixed at top with proper centering */}
|
|
||||||
<div className="absolute -top-16 left-0 right-0 mx-auto flex justify-center z-50">
|
<div className="absolute -top-16 left-0 right-0 mx-auto flex justify-center z-50">
|
||||||
<CommandDisplay command={command} />
|
<CommandDisplay command={command} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main container with proper layout */}
|
|
||||||
<div className="relative rounded-xl border border-gray-800 overflow-hidden">
|
<div className="relative rounded-xl border border-gray-800 overflow-hidden">
|
||||||
<div className="absolute inset-0 backdrop-blur-3xl bg-gradient-to-r from-blue-500/10 via-purple-500/10 to-pink-500/10" />
|
<div className="absolute inset-0 backdrop-blur-3xl bg-gradient-to-r from-blue-500/10 via-purple-500/10 to-pink-500/10" />
|
||||||
|
|
||||||
{/* Tech selector fixed to the left side */}
|
|
||||||
<div className="absolute left-0 top-0 bottom-0 lg:w-52 md:w-44 w-36 z-50 bg-gray-950/30 border-r border-gray-800/50">
|
<div className="absolute left-0 top-0 bottom-0 lg:w-52 md:w-44 w-36 z-50 bg-gray-950/30 border-r border-gray-800/50">
|
||||||
<TechSelector onSelect={handleTechSelect} activeNodes={activeNodes} />
|
<TechSelector onSelect={handleTechSelect} activeNodes={activeNodes} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Help text */}
|
|
||||||
<div className="max-sm:hidden bg-gray-950/30 lg:p-4 p-1 absolute lg:top-4 top-2 lg:right-4 right-2 z-50 w-80 rounded-xl border border-gray-800 backdrop-blur-3xl">
|
<div className="max-sm:hidden bg-gray-950/30 lg:p-4 p-1 absolute lg:top-4 top-2 lg:right-4 right-2 z-50 w-80 rounded-xl border border-gray-800 backdrop-blur-3xl">
|
||||||
<div className="lg:text-sm text-xs text-gray-300 text-center">
|
<div className="lg:text-sm text-xs text-gray-300 text-center">
|
||||||
Select technologies from the left panel to customize your stack. The
|
Select technologies from the left panel to customize your stack. The
|
||||||
@@ -430,7 +425,6 @@ const CustomizableStack = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Flow container with proper spacing from the selector */}
|
|
||||||
<div className="h-[600px] lg:pl-52 md:pl-44 pl-36 relative backdrop-blur-sm bg-gray-950/50">
|
<div className="h-[600px] lg:pl-52 md:pl-44 pl-36 relative backdrop-blur-sm bg-gray-950/50">
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ interface ActiveNodes {
|
|||||||
orm: string;
|
orm: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
packageManager: string;
|
packageManager: string;
|
||||||
features: {
|
addons: {
|
||||||
docker: boolean;
|
docker: boolean;
|
||||||
githubActions: boolean;
|
githubActions: boolean;
|
||||||
seo: boolean;
|
seo: boolean;
|
||||||
@@ -38,11 +38,11 @@ const techOptions: Record<string, TechOption[]> = {
|
|||||||
{ id: "yarn", label: "Yarn", category: "packageManager" },
|
{ id: "yarn", label: "Yarn", category: "packageManager" },
|
||||||
{ id: "bun", label: "Bun", category: "packageManager" },
|
{ id: "bun", label: "Bun", category: "packageManager" },
|
||||||
],
|
],
|
||||||
features: [
|
addons: [
|
||||||
{ id: "docker", label: "Docker", category: "features" },
|
{ id: "docker", label: "Docker", category: "addons" },
|
||||||
{ id: "githubActions", label: "GitHub Actions", category: "features" },
|
{ id: "githubActions", label: "GitHub Actions", category: "addons" },
|
||||||
{ id: "seo", label: "SEO", category: "features" },
|
{ id: "seo", label: "SEO", category: "addons" },
|
||||||
{ id: "git", label: "Git", category: "features" },
|
{ id: "git", label: "Git", category: "addons" },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export function TechSelector({ onSelect, activeNodes }: TechSelectorProps) {
|
|||||||
|
|
||||||
{/* Regular tech options */}
|
{/* Regular tech options */}
|
||||||
{Object.entries(techOptions)
|
{Object.entries(techOptions)
|
||||||
.filter(([category]) => category !== "features")
|
.filter(([category]) => category !== "addons")
|
||||||
.map(([category, options]) => (
|
.map(([category, options]) => (
|
||||||
<div key={category} className="space-y-2">
|
<div key={category} className="space-y-2">
|
||||||
<div className="text-xs text-gray-400 capitalize">{category}</div>
|
<div className="text-xs text-gray-400 capitalize">{category}</div>
|
||||||
@@ -71,7 +71,7 @@ export function TechSelector({ onSelect, activeNodes }: TechSelectorProps) {
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
className={`cursor-pointer hover:bg-gray-700 ${
|
className={`cursor-pointer hover:bg-gray-700 ${
|
||||||
activeNodes[
|
activeNodes[
|
||||||
category as keyof Omit<ActiveNodes, "features">
|
category as keyof Omit<ActiveNodes, "addons">
|
||||||
] === option.id && "bg-blue-600 text-white"
|
] === option.id && "bg-blue-600 text-white"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onSelect(category, option.id)}
|
onClick={() => onSelect(category, option.id)}
|
||||||
@@ -85,18 +85,18 @@ export function TechSelector({ onSelect, activeNodes }: TechSelectorProps) {
|
|||||||
|
|
||||||
{/* Feature toggles */}
|
{/* Feature toggles */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="text-xs text-gray-400">Features</div>
|
<div className="text-xs text-gray-400">Addons</div>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{techOptions.features.map((option) => (
|
{techOptions.addons.map((option) => (
|
||||||
<Badge
|
<Badge
|
||||||
key={option.id}
|
key={option.id}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className={`cursor-pointer hover:bg-gray-700 ${
|
className={`cursor-pointer hover:bg-gray-700 ${
|
||||||
activeNodes.features[
|
activeNodes.addons[
|
||||||
option.id as keyof typeof activeNodes.features
|
option.id as keyof typeof activeNodes.addons
|
||||||
] === true && "bg-blue-600 text-white"
|
] === true && "bg-blue-600 text-white"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onSelect("features", option.id)}
|
onClick={() => onSelect("addons", option.id)}
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
Reference in New Issue
Block a user