From 63c171f83ee1d938c694dff2870f51b7b569fe88 Mon Sep 17 00:00:00 2001 From: Felipe Cardoso Date: Sat, 8 Nov 2025 15:46:52 +0100 Subject: [PATCH] Add interactive demo components and feature sections to homepage - Introduced `DemoSection`, showcasing live feature demos with interactive cards and test credentials for admin and auth flows. - Added `FeatureGrid` with dynamic animations, highlighting major application features like RBAC, documentation, and deployment readiness. - Built reusable `FeatureCard` for feature details, including icons, descriptions, and CTAs. - Implemented `TechStackSection` to display modern tools and technologies used in the stack with tooltips. - Updated dependencies: added `framer-motion`, `lucide-react`, and `react-syntax-highlighter`. --- frontend/package-lock.json | 185 ++++++++++++++++++ frontend/package.json | 3 + frontend/src/app/page.tsx | 179 ++++++++--------- .../src/components/home/AnimatedTerminal.tsx | 140 +++++++++++++ frontend/src/components/home/CTASection.tsx | 124 ++++++++++++ .../src/components/home/ContextSection.tsx | 59 ++++++ .../components/home/DemoCredentialsModal.tsx | 141 +++++++++++++ frontend/src/components/home/DemoSection.tsx | 97 +++++++++ frontend/src/components/home/FeatureCard.tsx | 63 ++++++ frontend/src/components/home/FeatureGrid.tsx | 117 +++++++++++ frontend/src/components/home/Header.tsx | 155 +++++++++++++++ frontend/src/components/home/HeroSection.tsx | 150 ++++++++++++++ .../src/components/home/PhilosophySection.tsx | 103 ++++++++++ .../src/components/home/QuickStartCode.tsx | 101 ++++++++++ frontend/src/components/home/StatsSection.tsx | 134 +++++++++++++ .../src/components/home/TechStackSection.tsx | 104 ++++++++++ frontend/src/hooks/usePrefersReducedMotion.ts | 33 ++++ 17 files changed, 1796 insertions(+), 92 deletions(-) create mode 100644 frontend/src/components/home/AnimatedTerminal.tsx create mode 100644 frontend/src/components/home/CTASection.tsx create mode 100644 frontend/src/components/home/ContextSection.tsx create mode 100644 frontend/src/components/home/DemoCredentialsModal.tsx create mode 100644 frontend/src/components/home/DemoSection.tsx create mode 100644 frontend/src/components/home/FeatureCard.tsx create mode 100644 frontend/src/components/home/FeatureGrid.tsx create mode 100644 frontend/src/components/home/Header.tsx create mode 100644 frontend/src/components/home/HeroSection.tsx create mode 100644 frontend/src/components/home/PhilosophySection.tsx create mode 100644 frontend/src/components/home/QuickStartCode.tsx create mode 100644 frontend/src/components/home/StatsSection.tsx create mode 100644 frontend/src/components/home/TechStackSection.tsx create mode 100644 frontend/src/hooks/usePrefersReducedMotion.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 942b8a5..4024db2 100755 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -22,10 +22,12 @@ "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@tanstack/react-query": "^5.90.5", + "@types/react-syntax-highlighter": "^15.5.13", "axios": "^1.13.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "framer-motion": "^12.23.24", "gray-matter": "^4.0.3", "lucide-react": "^0.552.0", "next": "^15.5.6", @@ -34,6 +36,7 @@ "react-dom": "^19.0.0", "react-hook-form": "^7.66.0", "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^16.1.0", "recharts": "^2.15.4", "rehype-autolink-headings": "^7.1.0", "rehype-highlight": "^7.0.2", @@ -5185,6 +5188,12 @@ "@types/pg": "*" } }, + "node_modules/@types/prismjs": { + "version": "1.26.5", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", + "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", @@ -5204,6 +5213,15 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-syntax-highlighter": { + "version": "15.5.13", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", + "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/shimmer": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", @@ -8757,6 +8775,19 @@ "reusify": "^1.0.4" } }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -8910,6 +8941,14 @@ "node": ">= 6" } }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" + } + }, "node_modules/forwarded-parse": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", @@ -8917,6 +8956,33 @@ "dev": true, "license": "MIT" }, + "node_modules/framer-motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", + "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -9456,6 +9522,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -9525,6 +9604,23 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/highlight.js": { "version": "11.11.1", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", @@ -9534,6 +9630,12 @@ "node": ">=12.0.0" } }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -13199,6 +13301,21 @@ "dev": true, "license": "MIT" }, + "node_modules/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -14216,6 +14333,15 @@ "license": "MIT", "peer": true }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -14553,6 +14679,49 @@ } } }, + "node_modules/react-syntax-highlighter": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", + "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "highlight.js": "^10.4.1", + "highlightjs-vue": "^1.0.0", + "lowlight": "^1.17.0", + "prismjs": "^1.30.0", + "refractor": "^5.0.0" + }, + "engines": { + "node": ">= 16.20.2" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/react-syntax-highlighter/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/react-syntax-highlighter/node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -14658,6 +14827,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/refractor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", + "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^9.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index fdfd66d..054b6bf 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,10 +35,12 @@ "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@tanstack/react-query": "^5.90.5", + "@types/react-syntax-highlighter": "^15.5.13", "axios": "^1.13.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "framer-motion": "^12.23.24", "gray-matter": "^4.0.3", "lucide-react": "^0.552.0", "next": "^15.5.6", @@ -47,6 +49,7 @@ "react-dom": "^19.0.0", "react-hook-form": "^7.66.0", "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^16.1.0", "recharts": "^2.15.4", "rehype-autolink-headings": "^7.1.0", "rehype-highlight": "^7.0.2", diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 3eee014..2911460 100755 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,100 +1,95 @@ -import Image from "next/image"; +/** + * Homepage / Landing Page + * Main landing page for the FastNext Template project + * Showcases features, tech stack, and provides demos for developers + */ + +import { Header } from '@/components/home/Header'; +import { HeroSection } from '@/components/home/HeroSection'; +import { ContextSection } from '@/components/home/ContextSection'; +import { AnimatedTerminal } from '@/components/home/AnimatedTerminal'; +import { FeatureGrid } from '@/components/home/FeatureGrid'; +import { DemoSection } from '@/components/home/DemoSection'; +import { StatsSection } from '@/components/home/StatsSection'; +import { TechStackSection } from '@/components/home/TechStackSection'; +import { PhilosophySection } from '@/components/home/PhilosophySection'; +import { QuickStartCode } from '@/components/home/QuickStartCode'; +import { CTASection } from '@/components/home/CTASection'; export default function Home() { return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - src/app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
+
+ {/* Header Navigation */} +
- + {/* Main Content */} +
+ {/* Hero Section with CTAs */} + + + {/* What is this template? */} + + + {/* Animated Terminal with Quick Start */} + + + {/* 6 Feature Cards Grid */} + + + {/* Interactive Demo Cards */} + + + {/* Statistics with Animated Counters */} + + + {/* Tech Stack Grid */} + + + {/* For Developers, By Developers */} + + + {/* Quick Start Code Block */} + + + {/* Final CTA Section */} +
-
); diff --git a/frontend/src/components/home/AnimatedTerminal.tsx b/frontend/src/components/home/AnimatedTerminal.tsx new file mode 100644 index 0000000..7c0ae12 --- /dev/null +++ b/frontend/src/components/home/AnimatedTerminal.tsx @@ -0,0 +1,140 @@ +/** + * Animated Terminal + * Terminal with typing animation showing installation/setup commands + */ + +'use client'; + +import { useEffect, useState, useRef } from 'react'; +import { motion } from 'framer-motion'; +import { Terminal, Play } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import Link from 'next/link'; + +const commands = [ + { text: '# Clone the repository', delay: 0 }, + { text: '$ git clone https://github.com/your-org/fast-next-template.git', delay: 800 }, + { text: '$ cd fast-next-template', delay: 1600 }, + { text: '', delay: 2200 }, + { text: '# Start with Docker (one command)', delay: 2400 }, + { text: '$ docker-compose up', delay: 3200 }, + { text: '', delay: 4000 }, + { text: '✓ Backend running at http://localhost:8000', delay: 4200, isSuccess: true }, + { text: '✓ Frontend running at http://localhost:3000', delay: 4400, isSuccess: true }, + { text: '✓ Admin panel at http://localhost:3000/admin', delay: 4600, isSuccess: true }, + { text: '✓ API docs at http://localhost:8000/docs', delay: 4800, isSuccess: true }, +]; + +export function AnimatedTerminal() { + const [displayedLines, setDisplayedLines] = useState([]); + const [isAnimating, setIsAnimating] = useState(false); + const containerRef = useRef(null); + const hasAnimated = useRef(false); + + useEffect(() => { + // Only animate once when component enters viewport + if (hasAnimated.current) return; + + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting && !hasAnimated.current) { + hasAnimated.current = true; + setIsAnimating(true); + animateCommands(); + } + }, + { threshold: 0.3 } + ); + + if (containerRef.current) { + observer.observe(containerRef.current); + } + + return () => observer.disconnect(); + }, []); + + const animateCommands = () => { + commands.forEach((cmd) => { + setTimeout(() => { + setDisplayedLines((prev) => [...prev, cmd]); + }, cmd.delay); + }); + }; + + return ( +
+ + {/* Title */} +
+

Get Started in Seconds

+

+ Clone, run, and start building. No complex setup required. +

+
+ + {/* Terminal Window */} +
+ {/* Terminal Header */} +
+
+ + + {/* CTA Below Terminal */} +
+

+ Or try the live demo without installing anything +

+ +
+ +
+ ); +} diff --git a/frontend/src/components/home/CTASection.tsx b/frontend/src/components/home/CTASection.tsx new file mode 100644 index 0000000..8f7bf53 --- /dev/null +++ b/frontend/src/components/home/CTASection.tsx @@ -0,0 +1,124 @@ +/** + * CTA Section + * Final call-to-action footer section + */ + +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { motion } from 'framer-motion'; +import { Github, Star, Play, ArrowRight } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { DemoCredentialsModal } from './DemoCredentialsModal'; + +export function CTASection() { + const [demoModalOpen, setDemoModalOpen] = useState(false); + + return ( +
+ {/* Background Pattern */} +
+ ); +} diff --git a/frontend/src/components/home/ContextSection.tsx b/frontend/src/components/home/ContextSection.tsx new file mode 100644 index 0000000..02cf00a --- /dev/null +++ b/frontend/src/components/home/ContextSection.tsx @@ -0,0 +1,59 @@ +/** + * Context Section + * Explains what the template is and what you get out of the box + */ + +'use client'; + +import { motion } from 'framer-motion'; +import { CheckCircle2 } from 'lucide-react'; + +export function ContextSection() { + const features = [ + 'Clone & Deploy in < 5 minutes', + '97% Test Coverage (743 tests)', + '12+ Documentation Guides', + 'Zero Commercial Dependencies', + ]; + + return ( +
+
+ +

+ What You Get Out of the Box +

+

+ This isn't a boilerplate generator or a paid SaaS template. It's a complete, + production-ready codebase you can clone and customize. Everything you need to build + modern web applications without reinventing authentication, authorization, and admin + infrastructure. +

+ + {/* Feature Badges */} +
+ {features.map((feature, index) => ( + + + ))} +
+
+
+
+ ); +} diff --git a/frontend/src/components/home/DemoCredentialsModal.tsx b/frontend/src/components/home/DemoCredentialsModal.tsx new file mode 100644 index 0000000..f258eb4 --- /dev/null +++ b/frontend/src/components/home/DemoCredentialsModal.tsx @@ -0,0 +1,141 @@ +/** + * Demo Credentials Modal + * Displays demo login credentials for testing the live application + */ + +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { Copy, Check } from 'lucide-react'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; + +interface DemoCredentialsModalProps { + open: boolean; + onClose: () => void; +} + +export function DemoCredentialsModal({ open, onClose }: DemoCredentialsModalProps) { + const [copiedRegular, setCopiedRegular] = useState(false); + const [copiedAdmin, setCopiedAdmin] = useState(false); + + const regularCredentials = 'demo@example.com\nDemo123!'; + const adminCredentials = 'admin@example.com\nAdmin123!'; + + const copyToClipboard = async (text: string, type: 'regular' | 'admin') => { + try { + await navigator.clipboard.writeText(text); + if (type === 'regular') { + setCopiedRegular(true); + setTimeout(() => setCopiedRegular(false), 2000); + } else { + setCopiedAdmin(true); + setTimeout(() => setCopiedAdmin(false), 2000); + } + } catch (err) { + console.error('Failed to copy:', err); + } + }; + + return ( + + + + Try the Live Demo + + Use these credentials to explore the template's features. Both accounts are pre-configured with sample data. + + + +
+ {/* Regular User Credentials */} +
+
+

Regular User

+ +
+
+

+ Email: + demo@example.com +

+

+ Password: + Demo123! +

+
+

+ Access settings, organizations, and user features +

+
+ + {/* Admin User Credentials */} +
+
+

Admin User (Superuser)

+ +
+
+

+ Email: + admin@example.com +

+

+ Password: + Admin123! +

+
+

+ Full admin panel access: user management, analytics, bulk operations +

+
+
+ + + + + +
+
+ ); +} diff --git a/frontend/src/components/home/DemoSection.tsx b/frontend/src/components/home/DemoSection.tsx new file mode 100644 index 0000000..d256151 --- /dev/null +++ b/frontend/src/components/home/DemoSection.tsx @@ -0,0 +1,97 @@ +/** + * Demo Section + * Interactive demo cards showing live features with demo credentials + */ + +'use client'; + +import Link from 'next/link'; +import { motion } from 'framer-motion'; +import { Play, Layers, ShieldCheck } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +const demos = [ + { + icon: Layers, + title: 'Component Showcase', + description: + 'Browse the complete design system, UI components, and interactive examples built with shadcn/ui and TailwindCSS', + href: '/dev', + cta: 'View Components', + variant: 'outline' as const, + }, + { + icon: ShieldCheck, + title: 'Authentication Flow', + description: + 'Test the complete auth flow: login, session management, password reset. Full security implementation with JWT and refresh tokens', + href: '/login', + credentials: 'demo@example.com / Demo123!', + cta: 'Try Auth Demo', + variant: 'default' as const, + }, + { + icon: Play, + title: 'Admin Dashboard', + description: + 'Experience the admin panel with user management, real-time analytics charts, bulk operations, and session monitoring', + href: '/admin', + credentials: 'admin@example.com / Admin123!', + cta: 'Launch Admin', + variant: 'outline' as const, + }, +]; + +export function DemoSection() { + return ( +
+ +

See It In Action

+

+ Explore the template's capabilities with live demos. Login with demo credentials to test features. +

+
+ +
+ {demos.map((demo, index) => ( + + {/* Icon */} +
+
+ +

{demo.title}

+

+ {demo.description} +

+ + {demo.credentials && ( +
+
Demo Credentials:
+
{demo.credentials}
+
+ )} + + +
+ ))} +
+
+ ); +} diff --git a/frontend/src/components/home/FeatureCard.tsx b/frontend/src/components/home/FeatureCard.tsx new file mode 100644 index 0000000..d2e3a3b --- /dev/null +++ b/frontend/src/components/home/FeatureCard.tsx @@ -0,0 +1,63 @@ +/** + * Feature Card + * Reusable feature card component with icon, title, description, and CTA link + */ + +'use client'; + +import Link from 'next/link'; +import { motion } from 'framer-motion'; +import { ArrowRight, LucideIcon } from 'lucide-react'; + +interface FeatureCardProps { + icon: LucideIcon; + title: string; + description: string; + highlight: string; + ctaText: string; + ctaHref: string; +} + +export function FeatureCard({ + icon: Icon, + title, + description, + highlight, + ctaText, + ctaHref, +}: FeatureCardProps) { + return ( + + {/* Icon with Gradient Background */} +
+
+ + {/* Title */} +

{title}

+ + {/* Highlight Badge */} +
+ + {highlight} + +
+ + {/* Description */} +

{description}

+ + {/* CTA Link */} + + {ctaText} +
+ ); +} diff --git a/frontend/src/components/home/FeatureGrid.tsx b/frontend/src/components/home/FeatureGrid.tsx new file mode 100644 index 0000000..993f31a --- /dev/null +++ b/frontend/src/components/home/FeatureGrid.tsx @@ -0,0 +1,117 @@ +/** + * Feature Grid + * Grid layout displaying 6 main features with stagger animation + */ + +'use client'; + +import { motion } from 'framer-motion'; +import { Shield, Users, BarChart3, BookOpen, Server, Code } from 'lucide-react'; +import { FeatureCard } from './FeatureCard'; + +const features = [ + { + icon: Shield, + title: 'Authentication & Security', + description: + 'JWT authentication with refresh tokens, session management, password reset flow, rate limiting, CSRF protection, and comprehensive security tests preventing common attacks (CVE-2015-9235, session hijacking)', + highlight: 'Battle-tested security', + ctaText: 'View Auth Flow', + ctaHref: '/login', + }, + { + icon: Users, + title: 'Multi-Tenant Organizations', + description: + 'Complete organization system with 3-tier RBAC (Owner/Admin/Member). Invite members, manage permissions, and scope data access per organization—all batteries included', + highlight: 'Multi-tenancy built-in', + ctaText: 'See Organizations', + ctaHref: '/admin/organizations', + }, + { + icon: BarChart3, + title: 'Admin Dashboard', + description: + 'Full-featured admin panel with user management, real-time analytics charts, bulk operations, session monitoring, and role-based access controls', + highlight: 'Enterprise-ready admin', + ctaText: 'Try Admin Panel', + ctaHref: '/admin', + }, + { + icon: BookOpen, + title: 'Complete Documentation', + description: + '12+ documentation guides covering architecture, design system, testing patterns, deployment, and AI code generation guidelines. Interactive API docs with Swagger and ReDoc', + highlight: 'Developer-first docs', + ctaText: 'Browse Docs', + ctaHref: 'https://github.com/your-org/fast-next-template#documentation', + }, + { + icon: Server, + title: 'Production Ready', + description: + 'Docker deployment configs, database migrations with Alembic helpers, connection pooling, health checks, monitoring setup, and production security headers', + highlight: 'Deploy with confidence', + ctaText: 'Deployment Guide', + ctaHref: 'https://github.com/your-org/fast-next-template#deployment', + }, + { + icon: Code, + title: 'Developer Experience', + description: + 'Auto-generated TypeScript API client from OpenAPI spec, hot reload in development, migration helpers (python migrate.py auto), VS Code settings, and comprehensive component library', + highlight: 'Delightful DX', + ctaText: 'Explore Components', + ctaHref: '/dev', + }, +]; + +const containerVariants = { + hidden: { opacity: 0 }, + show: { + opacity: 1, + transition: { + staggerChildren: 0.15, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + show: { opacity: 1, y: 0 }, +}; + +export function FeatureGrid() { + return ( +
+ +

+ Comprehensive Features, No Assembly Required +

+

+ Everything you need to build production-grade web applications. Clone, customize, and ship. +

+
+ + + {features.map((feature) => ( + + + + ))} + +
+ ); +} diff --git a/frontend/src/components/home/Header.tsx b/frontend/src/components/home/Header.tsx new file mode 100644 index 0000000..37149ac --- /dev/null +++ b/frontend/src/components/home/Header.tsx @@ -0,0 +1,155 @@ +/** + * Homepage Header + * Navigation header for the landing page with demo credentials modal + */ + +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { Menu, X, Github, Star } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { DemoCredentialsModal } from './DemoCredentialsModal'; +import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; + +export function Header() { + const [demoModalOpen, setDemoModalOpen] = useState(false); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + + const navLinks = [ + { href: '/dev', label: 'Components' }, + { href: '/admin', label: 'Admin Demo' }, + ]; + + return ( + <> +
+
+ {/* Logo */} + + + FastNext + + {' '} + Template + + + {/* Desktop Navigation */} + + + {/* Mobile Menu Toggle */} + + + + + + + + +
+
+ + {/* Demo Credentials Modal */} + setDemoModalOpen(false)} + /> + + ); +} diff --git a/frontend/src/components/home/HeroSection.tsx b/frontend/src/components/home/HeroSection.tsx new file mode 100644 index 0000000..b501fa0 --- /dev/null +++ b/frontend/src/components/home/HeroSection.tsx @@ -0,0 +1,150 @@ +/** + * Hero Section + * Main hero section with headline, subheadline, CTAs, and gradient background + */ + +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { motion } from 'framer-motion'; +import { ArrowRight, Github, Play } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { DemoCredentialsModal } from './DemoCredentialsModal'; + +export function HeroSection() { + const [demoModalOpen, setDemoModalOpen] = useState(false); + + return ( +
+ {/* Gradient Background */} +
+ ); +} diff --git a/frontend/src/components/home/PhilosophySection.tsx b/frontend/src/components/home/PhilosophySection.tsx new file mode 100644 index 0000000..063a771 --- /dev/null +++ b/frontend/src/components/home/PhilosophySection.tsx @@ -0,0 +1,103 @@ +/** + * Philosophy Section + * "For Developers, By Developers" section explaining why this template exists + */ + +'use client'; + +import { motion } from 'framer-motion'; +import { X, Check } from 'lucide-react'; + +const wontFind = [ + 'Vendor lock-in to specific services', + 'Hidden costs or upgrade prompts', + 'Poorly documented "magic"', + 'Outdated dependencies', +]; + +const willFind = [ + 'Production patterns that actually work', + 'Comprehensive test coverage (not aspirational)', + 'Clear, honest documentation', + 'Active development and improvements', +]; + +export function PhilosophySection() { + return ( +
+
+ +

+ Why This Template Exists +

+
+

+ We built this template after rebuilding the same authentication, authorization, and + admin infrastructure for the fifth time. Instead of yet another tutorial or boilerplate + generator, we created a complete, tested, documented codebase that you can clone and + customize. +

+

+ No vendor lock-in. No subscriptions. No license restrictions. +

+

+ Just clean, modern code with patterns that scale. MIT licensed forever. +

+
+
+ +
+ {/* What You Won't Find */} + +

+

+
    + {wontFind.map((item) => ( +
  • +
  • + ))} +
+
+ + {/* What You Will Find */} + +

+

+
    + {willFind.map((item) => ( +
  • +
  • + ))} +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/home/QuickStartCode.tsx b/frontend/src/components/home/QuickStartCode.tsx new file mode 100644 index 0000000..cbe0f1b --- /dev/null +++ b/frontend/src/components/home/QuickStartCode.tsx @@ -0,0 +1,101 @@ +/** + * Quick Start Code Block + * Code snippet showing quick start commands with copy functionality + */ + +'use client'; + +import { useState } from 'react'; +import { motion } from 'framer-motion'; +import { Check, Copy } from 'lucide-react'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { Button } from '@/components/ui/button'; + +const codeString = `# Clone and start with Docker +git clone https://github.com/your-org/fast-next-template.git +cd fast-next-template +docker-compose up + +# Or set up locally +cd backend && python -m venv .venv && source .venv/bin/activate +pip install -r requirements.txt +cd ../frontend && npm install`; + +export function QuickStartCode() { + const [copied, setCopied] = useState(false); + + const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(codeString); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy:', err); + } + }; + + return ( +
+ +
+

5-Minute Setup

+

+ Clone, run, and start building. It's that simple. +

+
+ +
+ {/* Header with Copy Button */} +
+
+
+ +
+ + {/* Code Block */} +
+ + {codeString} + +
+
+
+
+ ); +} diff --git a/frontend/src/components/home/StatsSection.tsx b/frontend/src/components/home/StatsSection.tsx new file mode 100644 index 0000000..af34b2f --- /dev/null +++ b/frontend/src/components/home/StatsSection.tsx @@ -0,0 +1,134 @@ +/** + * Stats Section + * Animated statistics with counters + */ + +'use client'; + +import { useEffect, useState } from 'react'; +import { motion, useInView } from 'framer-motion'; +import { useRef } from 'react'; +import { CheckCircle2, TestTube, Zap, FileCode } from 'lucide-react'; + +interface Stat { + icon: React.ComponentType<{ className?: string }>; + value: number; + suffix: string; + label: string; + description: string; +} + +const stats: Stat[] = [ + { + icon: Zap, + value: 97, + suffix: '%', + label: 'Test Coverage', + description: 'Comprehensive testing across backend and frontend', + }, + { + icon: TestTube, + value: 743, + suffix: '', + label: 'Passing Tests', + description: 'Backend, frontend unit, and E2E tests', + }, + { + icon: CheckCircle2, + value: 0, + suffix: '', + label: 'Flaky Tests', + description: 'Production-stable test suite', + }, + { + icon: FileCode, + value: 30, + suffix: '+', + label: 'API Endpoints', + description: 'Fully documented with OpenAPI', + }, +]; + +function AnimatedCounter({ value, suffix }: { value: number; suffix: string }) { + const [count, setCount] = useState(0); + const ref = useRef(null); + const isInView = useInView(ref, { once: true, margin: '-100px' }); + + useEffect(() => { + if (!isInView) return; + + const duration = 2000; // 2 seconds + const steps = 60; + const increment = value / steps; + let current = 0; + + const timer = setInterval(() => { + current += increment; + if (current >= value) { + setCount(value); + clearInterval(timer); + } else { + setCount(Math.floor(current)); + } + }, duration / steps); + + return () => clearInterval(timer); + }, [isInView, value]); + + return ( +
+ {count} + {suffix} +
+ ); +} + +export function StatsSection() { + return ( +
+ +

Built with Quality in Mind

+

+ Not just another template. Comprehensive testing, documentation, and production-ready patterns. +

+
+ +
+ {stats.map((stat, index) => ( + + {/* Icon */} +
+
+
+
+ + {/* Animated Counter */} +
+ +
+ + {/* Label */} +
+
{stat.label}
+
{stat.description}
+
+
+ ))} +
+
+ ); +} diff --git a/frontend/src/components/home/TechStackSection.tsx b/frontend/src/components/home/TechStackSection.tsx new file mode 100644 index 0000000..622d68d --- /dev/null +++ b/frontend/src/components/home/TechStackSection.tsx @@ -0,0 +1,104 @@ +/** + * Tech Stack Section + * Displays technology logos/badges with tooltips + */ + +'use client'; + +import { motion } from 'framer-motion'; + +interface Tech { + name: string; + description: string; + color: string; +} + +const technologies: Tech[] = [ + { + name: 'FastAPI', + description: 'Async Python web framework, auto-docs, type hints', + color: 'from-teal-500 to-green-600', + }, + { + name: 'Next.js 15', + description: 'React 19, App Router, Server Components', + color: 'from-slate-900 to-slate-700', + }, + { + name: 'PostgreSQL', + description: 'Reliable, scalable SQL database', + color: 'from-blue-600 to-blue-800', + }, + { + name: 'TypeScript', + description: 'End-to-end type safety', + color: 'from-blue-500 to-blue-700', + }, + { + name: 'Docker', + description: 'Containerized deployment', + color: 'from-blue-400 to-blue-600', + }, + { + name: 'TailwindCSS', + description: 'Utility-first styling with OKLCH colors', + color: 'from-cyan-500 to-blue-500', + }, + { + name: 'shadcn/ui', + description: 'Accessible component library (New York variant)', + color: 'from-slate-800 to-slate-600', + }, + { + name: 'Playwright', + description: 'Reliable E2E testing (zero flaky tests)', + color: 'from-green-600 to-emerald-700', + }, +]; + +export function TechStackSection() { + return ( +
+ +

+ Modern, Type-Safe, Production-Grade Stack +

+

+ Built with the best tools for full-stack development. Async architecture, type safety, and developer experience. +

+
+ +
+ {technologies.map((tech, index) => ( + +
+ {/* Tech Badge */} +
+ {tech.name} +
+ + {/* Hover Tooltip */} +
+

{tech.description}

+
+
+
+ ))} +
+
+ ); +} diff --git a/frontend/src/hooks/usePrefersReducedMotion.ts b/frontend/src/hooks/usePrefersReducedMotion.ts new file mode 100644 index 0000000..b097012 --- /dev/null +++ b/frontend/src/hooks/usePrefersReducedMotion.ts @@ -0,0 +1,33 @@ +/** + * Hook to detect if user prefers reduced motion + * Respects prefers-reduced-motion media query for accessibility + */ + +import { useEffect, useState } from 'react'; + +export function usePrefersReducedMotion(): boolean { + const [prefersReducedMotion, setPrefersReducedMotion] = useState(false); + + useEffect(() => { + // Check if window is defined (SSR safety) + if (typeof window === 'undefined') { + return; + } + + const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); + setPrefersReducedMotion(mediaQuery.matches); + + // Listen for changes + const handleChange = (event: MediaQueryListEvent) => { + setPrefersReducedMotion(event.matches); + }; + + mediaQuery.addEventListener('change', handleChange); + + return () => { + mediaQuery.removeEventListener('change', handleChange); + }; + }, []); + + return prefersReducedMotion; +}