diff --git a/templates/blog/nextjs-monolith/.eslintrc.json b/templates/blog/nextjs-monolith/.eslintrc.json new file mode 100644 index 0000000..a2569c2 --- /dev/null +++ b/templates/blog/nextjs-monolith/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": "next/core-web-vitals" +} diff --git a/templates/blog/nextjs-monolith/app/articles/page.tsx b/templates/blog/nextjs-monolith/app/articles/page.tsx new file mode 100644 index 0000000..9e3043f --- /dev/null +++ b/templates/blog/nextjs-monolith/app/articles/page.tsx @@ -0,0 +1,72 @@ +import Link from 'next/link'; + +export default function ArticlesPage() { + const articles = [ + { slug: 'getting-started-with-nextjs-14', title: 'Getting Started with Next.js 14: A Complete Guide', excerpt: 'Learn how to build modern web applications with Next.js 14 App Router and Server Components.', author: 'Jane Smith', date: 'Jun 15, 2024', readTime: '8 min', category: 'Tutorial' }, + { slug: 'tailwind-css-best-practices', title: 'Tailwind CSS Best Practices for Large Projects', excerpt: 'Discover patterns and strategies for maintaining clean Tailwind CSS in enterprise applications.', author: 'Alex Chen', date: 'Jun 12, 2024', readTime: '6 min', category: 'CSS' }, + { slug: 'typescript-generics-explained', title: 'TypeScript Generics Explained Simply', excerpt: 'A practical guide to understanding and using TypeScript generics in your everyday code.', author: 'Maria Garcia', date: 'Jun 10, 2024', readTime: '10 min', category: 'TypeScript' }, + { slug: 'react-server-components', title: 'Understanding React Server Components', excerpt: 'Deep dive into how React Server Components work and when to use them.', author: 'David Park', date: 'Jun 8, 2024', readTime: '7 min', category: 'React' }, + { slug: 'building-design-systems', title: 'Building a Design System from Scratch', excerpt: 'Step-by-step guide to creating a cohesive design system for your team.', author: 'Sarah Johnson', date: 'Jun 5, 2024', readTime: '12 min', category: 'Design' }, + { slug: 'api-design-patterns', title: 'REST API Design Patterns You Should Know', excerpt: 'Essential patterns for building robust and scalable REST APIs.', author: 'James Wilson', date: 'Jun 3, 2024', readTime: '9 min', category: 'Backend' }, + { slug: 'state-management-2024', title: 'State Management in 2024: What to Use', excerpt: 'Comparing Zustand, Jotai, Redux Toolkit, and React Context for modern apps.', author: 'Emily Rodriguez', date: 'Jun 1, 2024', readTime: '11 min', category: 'React' }, + { slug: 'web-performance-optimization', title: 'Web Performance Optimization Checklist', excerpt: 'A comprehensive checklist for making your web applications blazing fast.', author: 'Tom Anderson', date: 'May 28, 2024', readTime: '14 min', category: 'Performance' }, + ]; + + const categories = ['All', 'Tutorial', 'React', 'TypeScript', 'CSS', 'Design', 'Backend', 'Performance']; + + return ( +
+
+
+

All Articles

+

Browse our collection of tutorials, guides, and insights

+
+ + {/* Category Filter */} +
+ {categories.map((cat, i) => ( + + ))} +
+ + {/* Articles List */} +
+ {articles.map((article) => ( + +
+ + {article.category} + + {article.readTime} read +
+

+ {article.title} +

+

+ {article.excerpt} +

+
+ {article.author} + · + {article.date} +
+ + ))} +
+
+
+ ); +} diff --git a/templates/blog/nextjs-monolith/app/authors/page.tsx b/templates/blog/nextjs-monolith/app/authors/page.tsx new file mode 100644 index 0000000..1e9c30a --- /dev/null +++ b/templates/blog/nextjs-monolith/app/authors/page.tsx @@ -0,0 +1,43 @@ +import Link from 'next/link'; + +export default function AuthorsPage() { + const authors = [ + { slug: 'jane-smith', name: 'Jane Smith', role: 'Senior Frontend Engineer', articles: 12, bio: 'Passionate about React, accessibility, and building great developer experiences.' }, + { slug: 'alex-chen', name: 'Alex Chen', role: 'Design Engineer', articles: 9, bio: 'Bridging the gap between design and engineering with CSS and design systems.' }, + { slug: 'maria-garcia', name: 'Maria Garcia', role: 'TypeScript Advocate', articles: 8, bio: 'Making TypeScript approachable for developers of all skill levels.' }, + { slug: 'david-park', name: 'David Park', role: 'Full-Stack Developer', articles: 7, bio: 'Building scalable applications with Next.js and modern backend technologies.' }, + { slug: 'sarah-johnson', name: 'Sarah Johnson', role: 'UI/UX Designer', articles: 6, bio: 'Creating beautiful, functional interfaces that users love.' }, + { slug: 'james-wilson', name: 'James Wilson', role: 'Backend Architect', articles: 5, bio: 'Designing robust APIs and distributed systems at scale.' }, + ]; + + return ( +
+
+
+

Authors

+

Meet the people behind the articles

+
+ +
+ {authors.map((author) => ( + +
+ {author.name.split(' ').map((n) => n[0]).join('')} +
+

+ {author.name} +

+

{author.role}

+

{author.bio}

+

{author.articles} articles published

+ + ))} +
+
+
+ ); +} diff --git a/templates/blog/nextjs-monolith/app/categories/page.tsx b/templates/blog/nextjs-monolith/app/categories/page.tsx new file mode 100644 index 0000000..26bc28b --- /dev/null +++ b/templates/blog/nextjs-monolith/app/categories/page.tsx @@ -0,0 +1,47 @@ +import Link from 'next/link'; + +export default function CategoriesPage() { + const categories = [ + { name: 'React', slug: 'react', count: 24, description: 'Components, hooks, patterns, and best practices for React development.' }, + { name: 'TypeScript', slug: 'typescript', count: 18, description: 'Type safety, generics, utility types, and advanced TypeScript patterns.' }, + { name: 'Next.js', slug: 'nextjs', count: 15, description: 'App Router, Server Components, API routes, and deployment strategies.' }, + { name: 'CSS & Design', slug: 'css-design', count: 12, description: 'Tailwind CSS, animations, responsive design, and design systems.' }, + { name: 'Backend', slug: 'backend', count: 10, description: 'APIs, databases, authentication, and server-side architecture.' }, + { name: 'DevOps', slug: 'devops', count: 8, description: 'CI/CD, Docker, deployment, monitoring, and infrastructure.' }, + { name: 'Performance', slug: 'performance', count: 7, description: 'Core Web Vitals, optimization techniques, and benchmarking.' }, + { name: 'Career', slug: 'career', count: 5, description: 'Job hunting, interviews, growth, and developer life.' }, + ]; + + return ( +
+
+
+

Categories

+

Browse articles by topic

+
+ +
+ {categories.map((category) => ( + +
+

+ {category.name} +

+ + {category.count} articles + +
+

+ {category.description} +

+ + ))} +
+
+
+ ); +} diff --git a/templates/blog/nextjs-monolith/app/globals.css b/templates/blog/nextjs-monolith/app/globals.css new file mode 100644 index 0000000..283fef5 --- /dev/null +++ b/templates/blog/nextjs-monolith/app/globals.css @@ -0,0 +1,59 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* ===== Base Theme: Minimal Clean (Default) ===== */ +:root { + --bg-color: #ffffff; + --bg-secondary: #f9fafb; + --text-color: #111827; + --text-secondary: #6b7280; + --primary: #2563eb; + --primary-hover: #1d4ed8; + --border-color: #e5e7eb; + --card-bg: #ffffff; + --font-family: 'Inter', system-ui, sans-serif; + --radius: 0.5rem; +} + +/* ===== Glassmorphism Theme ===== */ +[data-theme="Glassmorphism"] { + --bg-color: #0f172a; + --bg-secondary: rgba(255, 255, 255, 0.05); + --text-color: #f1f5f9; + --text-secondary: #94a3b8; + --primary: #8b5cf6; + --primary-hover: #7c3aed; + --border-color: rgba(255, 255, 255, 0.1); + --card-bg: rgba(255, 255, 255, 0.08); + --font-family: 'Inter', system-ui, sans-serif; + --radius: 1rem; +} + +[data-theme="Glassmorphism"] .glass { + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* ===== Dark Terminal Theme ===== */ +[data-theme="Dark Terminal"] { + --bg-color: #000000; + --bg-secondary: #0a0a0a; + --text-color: #3fb950; + --text-secondary: #8b949e; + --primary: #3fb950; + --primary-hover: #2ea043; + --border-color: #21262d; + --card-bg: #0d1117; + --font-family: 'JetBrains Mono', 'Fira Code', monospace; + --radius: 0.25rem; +} + +/* ===== Apply variables globally ===== */ +body { + background-color: var(--bg-color); + color: var(--text-color); + font-family: var(--font-family); +} diff --git a/templates/blog/nextjs-monolith/app/layout.tsx b/templates/blog/nextjs-monolith/app/layout.tsx new file mode 100644 index 0000000..e3c4d71 --- /dev/null +++ b/templates/blog/nextjs-monolith/app/layout.tsx @@ -0,0 +1,122 @@ +import type { Metadata } from 'next'; +import Link from 'next/link'; +import './globals.css'; +import AnimationProvider from '../components/AnimationProvider'; +{{#if (eq design "Dark Terminal")}} +import { Terminal } from 'lucide-react'; +{{/if}} + +export const metadata: Metadata = { + title: '{{projectName}} - Blog', + description: 'A {{variant}} blog built with Opusify CLI.', +}; + +const navLinks = [ + { label: 'Home', href: '/' }, + { label: 'Articles', href: '/articles' }, + { label: 'Categories', href: '/categories' }, + { label: 'Authors', href: '/authors' }, +]; + +function Navbar() { + return ( +
+ +
+ ); +} + +function Sidebar() { + return ( + + ); +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + const useSidebar = '{{includeSidebar}}' === 'true'; + + return ( + + + {useSidebar ? ( +
+ +
+ + {children} + +
+
+ ) : ( + <> + +
+ + {children} + +
+ + )} + + + ); +} diff --git a/templates/blog/nextjs-monolith/app/page.tsx b/templates/blog/nextjs-monolith/app/page.tsx new file mode 100644 index 0000000..2d9bbff --- /dev/null +++ b/templates/blog/nextjs-monolith/app/page.tsx @@ -0,0 +1,105 @@ +import Link from 'next/link'; + +export default function Home() { + const featuredPost = { + slug: 'getting-started-with-nextjs-14', + title: 'Getting Started with Next.js 14: A Complete Guide', + excerpt: 'Learn how to build modern web applications with Next.js 14 App Router, Server Components, and the latest React features.', + author: 'Jane Smith', + date: 'Jun 15, 2024', + readTime: '8 min read', + category: 'Tutorial', + }; + + const recentPosts = [ + { slug: 'tailwind-css-best-practices', title: 'Tailwind CSS Best Practices for Large Projects', author: 'Alex Chen', date: 'Jun 12, 2024', readTime: '6 min', category: 'CSS' }, + { slug: 'typescript-generics-explained', title: 'TypeScript Generics Explained Simply', author: 'Maria Garcia', date: 'Jun 10, 2024', readTime: '10 min', category: 'TypeScript' }, + { slug: 'react-server-components', title: 'Understanding React Server Components', author: 'David Park', date: 'Jun 8, 2024', readTime: '7 min', category: 'React' }, + { slug: 'building-design-systems', title: 'Building a Design System from Scratch', author: 'Sarah Johnson', date: 'Jun 5, 2024', readTime: '12 min', category: 'Design' }, + { slug: 'api-design-patterns', title: 'REST API Design Patterns You Should Know', author: 'James Wilson', date: 'Jun 3, 2024', readTime: '9 min', category: 'Backend' }, + { slug: 'state-management-2024', title: 'State Management in 2024: What to Use', author: 'Emily Rodriguez', date: 'Jun 1, 2024', readTime: '11 min', category: 'React' }, + ]; + + return ( +
+ {/* Hero */} +
+

+ Welcome to {{projectName}} +

+

+ A {{variant}} publication — stories, tutorials, and insights for developers. +

+
+ + {/* Featured Post */} +
+ + + Featured + +

+ {featuredPost.title} +

+

+ {featuredPost.excerpt} +

+
+ {featuredPost.author} + · + {featuredPost.date} + · + {featuredPost.readTime} +
+ +
+ + {/* Recent Posts */} +
+

Recent Articles

+
+ {recentPosts.map((post) => ( + +
+ + {post.category} + + {post.readTime} +
+

+ {post.title} +

+
+ {post.author} + · + {post.date} +
+ + ))} +
+
+ + {/* Newsletter */} +
+
+

Subscribe to our newsletter

+

Get the latest articles delivered straight to your inbox.

+
+ + +
+
+
+
+ ); +} diff --git a/templates/blog/nextjs-monolith/components/AnimationProvider.tsx b/templates/blog/nextjs-monolith/components/AnimationProvider.tsx new file mode 100644 index 0000000..feaf51c --- /dev/null +++ b/templates/blog/nextjs-monolith/components/AnimationProvider.tsx @@ -0,0 +1,32 @@ +{{#if (eq design "Glassmorphism")}} +'use client'; + +import { AnimatePresence, motion } from 'framer-motion'; + +export default function AnimationProvider({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {children} + + + ); +} +{{else}} +export default function AnimationProvider({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} +{{/if}} diff --git a/templates/blog/nextjs-monolith/next.config.js b/templates/blog/nextjs-monolith/next.config.js new file mode 100644 index 0000000..658404a --- /dev/null +++ b/templates/blog/nextjs-monolith/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +module.exports = nextConfig; diff --git a/templates/blog/nextjs-monolith/package.json b/templates/blog/nextjs-monolith/package.json new file mode 100644 index 0000000..5dfa145 --- /dev/null +++ b/templates/blog/nextjs-monolith/package.json @@ -0,0 +1,27 @@ +{ + "name": "{{projectName}}", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "14.2.3", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/node": "^20.12.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "14.2.3", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5" + } +} diff --git a/templates/blog/nextjs-monolith/postcss.config.js b/templates/blog/nextjs-monolith/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/templates/blog/nextjs-monolith/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/templates/blog/nextjs-monolith/tailwind.config.ts b/templates/blog/nextjs-monolith/tailwind.config.ts new file mode 100644 index 0000000..b2e02d3 --- /dev/null +++ b/templates/blog/nextjs-monolith/tailwind.config.ts @@ -0,0 +1,43 @@ +import type { Config } from 'tailwindcss'; + +const config: Config = { + content: [ + './app/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + ], + theme: { + extend: { + colors: { + background: 'var(--bg-color)', + 'bg-secondary': 'var(--bg-secondary)', + foreground: 'var(--text-color)', + 'text-secondary': 'var(--text-secondary)', + primary: 'var(--primary)', + 'primary-hover': 'var(--primary-hover)', + border: 'var(--border-color)', + card: 'var(--card-bg)', + }, + borderRadius: { + theme: 'var(--radius)', + }, + fontFamily: { + theme: 'var(--font-family)', + }, + typography: { + DEFAULT: { + css: { + color: 'var(--text-color)', + a: { color: 'var(--primary)' }, + h1: { color: 'var(--text-color)' }, + h2: { color: 'var(--text-color)' }, + h3: { color: 'var(--text-color)' }, + strong: { color: 'var(--text-color)' }, + }, + }, + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/templates/blog/nextjs-monolith/tsconfig.json b/templates/blog/nextjs-monolith/tsconfig.json new file mode 100644 index 0000000..455bf35 --- /dev/null +++ b/templates/blog/nextjs-monolith/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./*"] } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/templates/saas/nextjs-monolith/.eslintrc.json b/templates/saas/nextjs-monolith/.eslintrc.json new file mode 100644 index 0000000..a2569c2 --- /dev/null +++ b/templates/saas/nextjs-monolith/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": "next/core-web-vitals" +} diff --git a/templates/saas/nextjs-monolith/app/analytics/page.tsx b/templates/saas/nextjs-monolith/app/analytics/page.tsx new file mode 100644 index 0000000..502bf63 --- /dev/null +++ b/templates/saas/nextjs-monolith/app/analytics/page.tsx @@ -0,0 +1,133 @@ +export default function AnalyticsPage() { + const trafficSources = [ + { source: 'Organic Search', visitors: 4521, percentage: 38, color: 'bg-blue-500' }, + { source: 'Direct', visitors: 2847, percentage: 24, color: 'bg-green-500' }, + { source: 'Social Media', visitors: 1923, percentage: 16, color: 'bg-purple-500' }, + { source: 'Referral', visitors: 1456, percentage: 12, color: 'bg-orange-500' }, + { source: 'Email', visitors: 1100, percentage: 10, color: 'bg-pink-500' }, + ]; + + const pageViews = [ + { page: '/dashboard', views: 12450, avgTime: '3m 24s', bounceRate: '18%' }, + { page: '/analytics', views: 8920, avgTime: '5m 12s', bounceRate: '12%' }, + { page: '/users', views: 6340, avgTime: '2m 48s', bounceRate: '24%' }, + { page: '/billing', views: 4210, avgTime: '4m 06s', bounceRate: '15%' }, + { page: '/settings', views: 3180, avgTime: '1m 52s', bounceRate: '32%' }, + { page: '/integrations', views: 2890, avgTime: '6m 30s', bounceRate: '8%' }, + ]; + + const conversionFunnel = [ + { stage: 'Visitors', count: 45000, width: '100%' }, + { stage: 'Sign Ups', count: 12800, width: '72%' }, + { stage: 'Activated', count: 8400, width: '48%' }, + { stage: 'Subscribed', count: 3200, width: '28%' }, + { stage: 'Enterprise', count: 420, width: '12%' }, + ]; + + return ( +
+
+
+

Analytics

+

Deep dive into your platform metrics

+
+
+ + + +
+
+ + {/* Summary Row */} +
+
+

Total Page Views

+

284,920

+

+18.3% vs last period

+
+
+

Unique Visitors

+

11,847

+

+9.7% vs last period

+
+
+

Conversion Rate

+

7.1%

+

-0.4% vs last period

+
+
+ +
+ {/* Traffic Sources */} +
+

Traffic Sources

+
+ {trafficSources.map((source) => ( +
+
+ {source.source} + {source.visitors.toLocaleString()} ({source.percentage}%) +
+
+
+
+
+ ))} +
+
+ + {/* Conversion Funnel */} +
+

Conversion Funnel

+
+ {conversionFunnel.map((stage) => ( +
+
{stage.stage}
+
+
+ {stage.count.toLocaleString()} +
+
+
+ ))} +
+
+
+ + {/* Top Pages */} +
+
+

Top Pages

+
+
+ + + + + + + + + + + {pageViews.map((page) => ( + + + + + + + ))} + +
PageViewsAvg. TimeBounce Rate
{page.page}{page.views.toLocaleString()}{page.avgTime}{page.bounceRate}
+
+
+
+ ); +} diff --git a/templates/saas/nextjs-monolith/app/billing/page.tsx b/templates/saas/nextjs-monolith/app/billing/page.tsx new file mode 100644 index 0000000..b28f88a --- /dev/null +++ b/templates/saas/nextjs-monolith/app/billing/page.tsx @@ -0,0 +1,149 @@ +export default function BillingPage() { + const plans = [ + { + name: 'Starter', + price: '$29', + period: '/month', + features: ['Up to 5 users', '10GB storage', 'Basic analytics', 'Email support'], + current: false, + }, + { + name: 'Pro', + price: '$99', + period: '/month', + features: ['Up to 25 users', '100GB storage', 'Advanced analytics', 'Priority support', 'API access', 'Custom integrations'], + current: true, + }, + { + name: 'Enterprise', + price: '$2,400', + period: '/year', + features: ['Unlimited users', '1TB storage', 'Full analytics suite', 'Dedicated support', 'SSO & SAML', 'Custom SLA', 'On-premise option'], + current: false, + }, + ]; + + const invoices = [ + { id: 'INV-2024-006', date: 'Jun 1, 2024', amount: '$99.00', status: 'Paid' }, + { id: 'INV-2024-005', date: 'May 1, 2024', amount: '$99.00', status: 'Paid' }, + { id: 'INV-2024-004', date: 'Apr 1, 2024', amount: '$99.00', status: 'Paid' }, + { id: 'INV-2024-003', date: 'Mar 1, 2024', amount: '$99.00', status: 'Paid' }, + { id: 'INV-2024-002', date: 'Feb 1, 2024', amount: '$99.00', status: 'Paid' }, + ]; + + return ( +
+
+

Billing

+

Manage your subscription and payment methods

+
+ + {/* Current Plan */} +
+
+
+

Current Plan

+

Pro Plan

+

$99/month · Renews on Jul 1, 2024

+
+
+ + +
+
+
+
+ Storage used + 67GB / 100GB +
+
+
+
+
+
+ + {/* Plans Comparison */} +

Available Plans

+
+ {plans.map((plan) => ( +
+ {plan.current && ( + + Current Plan + + )} +

{plan.name}

+
+ {plan.price} + {plan.period} +
+
    + {plan.features.map((feature) => ( +
  • + + {feature} +
  • + ))} +
+ +
+ ))} +
+ + {/* Invoice History */} +
+
+

Invoice History

+
+
+ + + + + + + + + + + + {invoices.map((inv) => ( + + + + + + + + ))} + +
InvoiceDateAmountStatusAction
{inv.id}{inv.date}{inv.amount} + + {inv.status} + + + +
+
+
+
+ ); +} diff --git a/templates/saas/nextjs-monolith/app/globals.css b/templates/saas/nextjs-monolith/app/globals.css new file mode 100644 index 0000000..283fef5 --- /dev/null +++ b/templates/saas/nextjs-monolith/app/globals.css @@ -0,0 +1,59 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* ===== Base Theme: Minimal Clean (Default) ===== */ +:root { + --bg-color: #ffffff; + --bg-secondary: #f9fafb; + --text-color: #111827; + --text-secondary: #6b7280; + --primary: #2563eb; + --primary-hover: #1d4ed8; + --border-color: #e5e7eb; + --card-bg: #ffffff; + --font-family: 'Inter', system-ui, sans-serif; + --radius: 0.5rem; +} + +/* ===== Glassmorphism Theme ===== */ +[data-theme="Glassmorphism"] { + --bg-color: #0f172a; + --bg-secondary: rgba(255, 255, 255, 0.05); + --text-color: #f1f5f9; + --text-secondary: #94a3b8; + --primary: #8b5cf6; + --primary-hover: #7c3aed; + --border-color: rgba(255, 255, 255, 0.1); + --card-bg: rgba(255, 255, 255, 0.08); + --font-family: 'Inter', system-ui, sans-serif; + --radius: 1rem; +} + +[data-theme="Glassmorphism"] .glass { + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* ===== Dark Terminal Theme ===== */ +[data-theme="Dark Terminal"] { + --bg-color: #000000; + --bg-secondary: #0a0a0a; + --text-color: #3fb950; + --text-secondary: #8b949e; + --primary: #3fb950; + --primary-hover: #2ea043; + --border-color: #21262d; + --card-bg: #0d1117; + --font-family: 'JetBrains Mono', 'Fira Code', monospace; + --radius: 0.25rem; +} + +/* ===== Apply variables globally ===== */ +body { + background-color: var(--bg-color); + color: var(--text-color); + font-family: var(--font-family); +} diff --git a/templates/saas/nextjs-monolith/app/layout.tsx b/templates/saas/nextjs-monolith/app/layout.tsx new file mode 100644 index 0000000..0399294 --- /dev/null +++ b/templates/saas/nextjs-monolith/app/layout.tsx @@ -0,0 +1,126 @@ +import type { Metadata } from 'next'; +import Link from 'next/link'; +import './globals.css'; +import AnimationProvider from '../components/AnimationProvider'; +{{#if (eq design "Dark Terminal")}} +import { Terminal } from 'lucide-react'; +{{/if}} + +export const metadata: Metadata = { + title: '{{projectName}} - Dashboard', + description: 'A {{variant}} SaaS dashboard built with Opusify CLI.', +}; + +const navLinks = [ + { label: 'Dashboard', href: '/' }, + { label: 'Analytics', href: '/analytics' }, + { label: 'Users', href: '/users' }, + { label: 'Billing', href: '/billing' }, + { label: 'Settings', href: '/settings' }, +]; + +function Navbar() { + return ( +
+ +
+ ); +} + +function Sidebar() { + return ( + + ); +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + const useSidebar = '{{includeSidebar}}' === 'true'; + + return ( + + + {useSidebar ? ( +
+ +
+ + {children} + +
+
+ ) : ( + <> + +
+ + {children} + +
+ + )} + + + ); +} diff --git a/templates/saas/nextjs-monolith/app/page.tsx b/templates/saas/nextjs-monolith/app/page.tsx new file mode 100644 index 0000000..72c8189 --- /dev/null +++ b/templates/saas/nextjs-monolith/app/page.tsx @@ -0,0 +1,122 @@ +export default function Dashboard() { + const metrics = [ + { label: 'Monthly Revenue', value: '$48,295', change: '+12.5%', positive: true }, + { label: 'Active Users', value: '12,847', change: '+8.2%', positive: true }, + { label: 'Churn Rate', value: '2.4%', change: '-0.3%', positive: true }, + { label: 'Avg. Session', value: '4m 32s', change: '-12s', positive: false }, + ]; + + const chartData = [ + { month: 'Jan', revenue: 32000, users: 8400 }, + { month: 'Feb', revenue: 35000, users: 9200 }, + { month: 'Mar', revenue: 38500, users: 9800 }, + { month: 'Apr', revenue: 41000, users: 10500 }, + { month: 'May', revenue: 44200, users: 11300 }, + { month: 'Jun', revenue: 48295, users: 12847 }, + ]; + + const recentTransactions = [ + { id: 'TXN-001', customer: 'Acme Corp', plan: 'Enterprise', amount: '$2,400', status: 'Completed', date: 'Today' }, + { id: 'TXN-002', customer: 'StartupXYZ', plan: 'Pro', amount: '$99', status: 'Completed', date: 'Today' }, + { id: 'TXN-003', customer: 'DevTeam Inc', plan: 'Enterprise', amount: '$2,400', status: 'Pending', date: 'Yesterday' }, + { id: 'TXN-004', customer: 'Solo Dev', plan: 'Starter', amount: '$29', status: 'Completed', date: 'Yesterday' }, + { id: 'TXN-005', customer: 'BigCo Ltd', plan: 'Enterprise', amount: '$2,400', status: 'Failed', date: '2 days ago' }, + ]; + + const maxRevenue = Math.max(...chartData.map((d) => d.revenue)); + + return ( +
+ {/* Header */} +
+

+ Welcome to {{projectName}} +

+

+ Your {{variant}} dashboard — real-time metrics at a glance. +

+
+ + {/* Metrics Cards */} +
+ {metrics.map((metric) => ( +
+

{metric.label}

+

{metric.value}

+

+ {metric.change} from last month +

+
+ ))} +
+ + {/* Revenue Chart */} +
+

Revenue Overview

+
+ {chartData.map((point) => ( +
+
+ {point.month} +
+ ))} +
+
+
+

Total Revenue (6mo)

+

$238,995

+
+
+

Growth Rate

+

+50.9%

+
+
+
+ + {/* Recent Transactions */} +
+
+

Recent Transactions

+ +
+
+ + + + + + + + + + + + + {recentTransactions.map((txn) => ( + + + + + + + + + ))} + +
IDCustomerPlanAmountStatusDate
{txn.id}{txn.customer}{txn.plan}{txn.amount} + + {txn.status} + + {txn.date}
+
+
+
+ ); +} diff --git a/templates/saas/nextjs-monolith/app/settings/page.tsx b/templates/saas/nextjs-monolith/app/settings/page.tsx new file mode 100644 index 0000000..bdcb83d --- /dev/null +++ b/templates/saas/nextjs-monolith/app/settings/page.tsx @@ -0,0 +1,97 @@ +export default function SettingsPage() { + return ( +
+
+

Settings

+

Manage your account and application preferences

+
+ +
+ {/* Profile Section */} +
+

Profile

+
+
+ A +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + {/* Notifications */} +
+

Notifications

+
+ {[ + { label: 'Email notifications', description: 'Receive email updates about your account activity', enabled: true }, + { label: 'Push notifications', description: 'Get push notifications in your browser', enabled: true }, + { label: 'Weekly digest', description: 'Receive a weekly summary of your analytics', enabled: false }, + { label: 'Marketing emails', description: 'Receive product updates and announcements', enabled: false }, + ].map((item) => ( +
+
+

{item.label}

+

{item.description}

+
+
+
+
+
+ ))} +
+
+ + {/* Danger Zone */} +
+

Danger Zone

+

+ Once you delete your account, there is no going back. Please be certain. +

+ +
+
+
+ ); +} diff --git a/templates/saas/nextjs-monolith/app/users/page.tsx b/templates/saas/nextjs-monolith/app/users/page.tsx new file mode 100644 index 0000000..625fde6 --- /dev/null +++ b/templates/saas/nextjs-monolith/app/users/page.tsx @@ -0,0 +1,117 @@ +export default function UsersPage() { + const users = [ + { id: 1, name: 'Sarah Johnson', email: 'sarah@acmecorp.com', role: 'Admin', plan: 'Enterprise', status: 'Active', lastSeen: '2 min ago' }, + { id: 2, name: 'Michael Chen', email: 'mchen@startupxyz.io', role: 'Owner', plan: 'Pro', status: 'Active', lastSeen: '15 min ago' }, + { id: 3, name: 'Emily Rodriguez', email: 'emily.r@devteam.co', role: 'Member', plan: 'Enterprise', status: 'Active', lastSeen: '1 hour ago' }, + { id: 4, name: 'James Wilson', email: 'jwilson@solodev.me', role: 'Owner', plan: 'Starter', status: 'Active', lastSeen: '3 hours ago' }, + { id: 5, name: 'Priya Patel', email: 'priya@bigco.com', role: 'Admin', plan: 'Enterprise', status: 'Inactive', lastSeen: '2 days ago' }, + { id: 6, name: 'Alex Thompson', email: 'alex.t@freelance.dev', role: 'Owner', plan: 'Pro', status: 'Active', lastSeen: '5 hours ago' }, + { id: 7, name: 'Lisa Wang', email: 'lwang@techfirm.io', role: 'Member', plan: 'Pro', status: 'Suspended', lastSeen: '1 week ago' }, + { id: 8, name: 'David Kim', email: 'dkim@agency.co', role: 'Owner', plan: 'Enterprise', status: 'Active', lastSeen: '30 min ago' }, + ]; + + const stats = [ + { label: 'Total Users', value: '12,847' }, + { label: 'Active Now', value: '1,234' }, + { label: 'New This Week', value: '89' }, + ]; + + return ( +
+
+
+

Users

+

Manage your platform users and permissions

+
+ +
+ + {/* Stats */} +
+ {stats.map((stat) => ( +
+

{stat.label}

+

{stat.value}

+
+ ))} +
+ + {/* Search & Filters */} +
+ + + +
+ + {/* Users Table */} +
+
+ + + + + + + + + + + + + {users.map((user) => ( + + + + + + + + + ))} + +
UserRolePlanStatusLast SeenActions
+
+
+ {user.name.split(' ').map((n) => n[0]).join('')} +
+
+

{user.name}

+

{user.email}

+
+
+
{user.role} + + {user.plan} + + + + {user.status} + + {user.lastSeen} + +
+
+
+
+ ); +} diff --git a/templates/saas/nextjs-monolith/components/AnimationProvider.tsx b/templates/saas/nextjs-monolith/components/AnimationProvider.tsx new file mode 100644 index 0000000..feaf51c --- /dev/null +++ b/templates/saas/nextjs-monolith/components/AnimationProvider.tsx @@ -0,0 +1,32 @@ +{{#if (eq design "Glassmorphism")}} +'use client'; + +import { AnimatePresence, motion } from 'framer-motion'; + +export default function AnimationProvider({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {children} + + + ); +} +{{else}} +export default function AnimationProvider({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} +{{/if}} diff --git a/templates/saas/nextjs-monolith/next.config.js b/templates/saas/nextjs-monolith/next.config.js new file mode 100644 index 0000000..658404a --- /dev/null +++ b/templates/saas/nextjs-monolith/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +module.exports = nextConfig; diff --git a/templates/saas/nextjs-monolith/package.json b/templates/saas/nextjs-monolith/package.json new file mode 100644 index 0000000..5dfa145 --- /dev/null +++ b/templates/saas/nextjs-monolith/package.json @@ -0,0 +1,27 @@ +{ + "name": "{{projectName}}", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "14.2.3", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/node": "^20.12.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "14.2.3", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5" + } +} diff --git a/templates/saas/nextjs-monolith/postcss.config.js b/templates/saas/nextjs-monolith/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/templates/saas/nextjs-monolith/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/templates/saas/nextjs-monolith/tailwind.config.ts b/templates/saas/nextjs-monolith/tailwind.config.ts new file mode 100644 index 0000000..c763011 --- /dev/null +++ b/templates/saas/nextjs-monolith/tailwind.config.ts @@ -0,0 +1,31 @@ +import type { Config } from 'tailwindcss'; + +const config: Config = { + content: [ + './app/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + ], + theme: { + extend: { + colors: { + background: 'var(--bg-color)', + 'bg-secondary': 'var(--bg-secondary)', + foreground: 'var(--text-color)', + 'text-secondary': 'var(--text-secondary)', + primary: 'var(--primary)', + 'primary-hover': 'var(--primary-hover)', + border: 'var(--border-color)', + card: 'var(--card-bg)', + }, + borderRadius: { + theme: 'var(--radius)', + }, + fontFamily: { + theme: 'var(--font-family)', + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/templates/saas/nextjs-monolith/tsconfig.json b/templates/saas/nextjs-monolith/tsconfig.json new file mode 100644 index 0000000..455bf35 --- /dev/null +++ b/templates/saas/nextjs-monolith/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { "@/*": ["./*"] } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/templates/saas/vite-react/index.html b/templates/saas/vite-react/index.html new file mode 100644 index 0000000..a904173 --- /dev/null +++ b/templates/saas/vite-react/index.html @@ -0,0 +1,13 @@ + + + + + + + {{projectName}} - Dashboard + + +
+ + + diff --git a/templates/saas/vite-react/package.json b/templates/saas/vite-react/package.json new file mode 100644 index 0000000..f89623c --- /dev/null +++ b/templates/saas/vite-react/package.json @@ -0,0 +1,27 @@ +{ + "name": "{{projectName}}", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.23.0" + }, + "devDependencies": { + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5", + "vite": "^5.2.11" + } +} diff --git a/templates/saas/vite-react/postcss.config.js b/templates/saas/vite-react/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/templates/saas/vite-react/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/templates/saas/vite-react/src/App.tsx b/templates/saas/vite-react/src/App.tsx new file mode 100644 index 0000000..064e4c3 --- /dev/null +++ b/templates/saas/vite-react/src/App.tsx @@ -0,0 +1,23 @@ +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import Layout from './components/Layout'; +import Dashboard from './pages/Dashboard'; +import Analytics from './pages/Analytics'; +import Users from './pages/Users'; +import Billing from './pages/Billing'; +import Settings from './pages/Settings'; + +export default function App() { + return ( + + + }> + } /> + } /> + } /> + } /> + } /> + + + + ); +} diff --git a/templates/saas/vite-react/src/components/Layout.tsx b/templates/saas/vite-react/src/components/Layout.tsx new file mode 100644 index 0000000..bcb44ba --- /dev/null +++ b/templates/saas/vite-react/src/components/Layout.tsx @@ -0,0 +1,97 @@ +import { Link, Outlet } from 'react-router-dom'; + +const navLinks = [ + { label: 'Dashboard', href: '/' }, + { label: 'Analytics', href: '/analytics' }, + { label: 'Users', href: '/users' }, + { label: 'Billing', href: '/billing' }, + { label: 'Settings', href: '/settings' }, +]; + +function Navbar() { + return ( +
+ +
+ ); +} + +function Sidebar() { + return ( + + ); +} + +export default function Layout() { + const useSidebar = '{{includeSidebar}}' === 'true'; + + if (useSidebar) { + return ( +
+ +
+ +
+
+ ); + } + + return ( + <> + +
+ +
+ + ); +} diff --git a/templates/saas/vite-react/src/index.css b/templates/saas/vite-react/src/index.css new file mode 100644 index 0000000..7a2f894 --- /dev/null +++ b/templates/saas/vite-react/src/index.css @@ -0,0 +1,48 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --bg-color: #ffffff; + --bg-secondary: #f9fafb; + --text-color: #111827; + --text-secondary: #6b7280; + --primary: #2563eb; + --primary-hover: #1d4ed8; + --border-color: #e5e7eb; + --card-bg: #ffffff; + --font-family: 'Inter', system-ui, sans-serif; + --radius: 0.5rem; +} + +[data-theme="Glassmorphism"] { + --bg-color: #0f172a; + --bg-secondary: rgba(255, 255, 255, 0.05); + --text-color: #f1f5f9; + --text-secondary: #94a3b8; + --primary: #8b5cf6; + --primary-hover: #7c3aed; + --border-color: rgba(255, 255, 255, 0.1); + --card-bg: rgba(255, 255, 255, 0.08); + --font-family: 'Inter', system-ui, sans-serif; + --radius: 1rem; +} + +[data-theme="Dark Terminal"] { + --bg-color: #000000; + --bg-secondary: #0a0a0a; + --text-color: #3fb950; + --text-secondary: #8b949e; + --primary: #3fb950; + --primary-hover: #2ea043; + --border-color: #21262d; + --card-bg: #0d1117; + --font-family: 'JetBrains Mono', 'Fira Code', monospace; + --radius: 0.25rem; +} + +body { + background-color: var(--bg-color); + color: var(--text-color); + font-family: var(--font-family); +} diff --git a/templates/saas/vite-react/src/main.tsx b/templates/saas/vite-react/src/main.tsx new file mode 100644 index 0000000..9aa52ff --- /dev/null +++ b/templates/saas/vite-react/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/templates/saas/vite-react/src/pages/Analytics.tsx b/templates/saas/vite-react/src/pages/Analytics.tsx new file mode 100644 index 0000000..ec8250c --- /dev/null +++ b/templates/saas/vite-react/src/pages/Analytics.tsx @@ -0,0 +1,56 @@ +export default function Analytics() { + const trafficSources = [ + { source: 'Organic Search', visitors: 4521, percentage: 38, color: 'bg-blue-500' }, + { source: 'Direct', visitors: 2847, percentage: 24, color: 'bg-green-500' }, + { source: 'Social Media', visitors: 1923, percentage: 16, color: 'bg-purple-500' }, + { source: 'Referral', visitors: 1456, percentage: 12, color: 'bg-orange-500' }, + { source: 'Email', visitors: 1100, percentage: 10, color: 'bg-pink-500' }, + ]; + + return ( +
+
+

Analytics

+

Deep dive into your platform metrics

+
+ +
+
+

Total Page Views

+

284,920

+

+18.3% vs last period

+
+
+

Unique Visitors

+

11,847

+

+9.7% vs last period

+
+
+

Conversion Rate

+

7.1%

+

-0.4% vs last period

+
+
+ +
+

Traffic Sources

+
+ {trafficSources.map((source) => ( +
+
+ {source.source} + {source.visitors.toLocaleString()} ({source.percentage}%) +
+
+
+
+
+ ))} +
+
+
+ ); +} diff --git a/templates/saas/vite-react/src/pages/Billing.tsx b/templates/saas/vite-react/src/pages/Billing.tsx new file mode 100644 index 0000000..2be5b8f --- /dev/null +++ b/templates/saas/vite-react/src/pages/Billing.tsx @@ -0,0 +1,50 @@ +export default function Billing() { + const plans = [ + { name: 'Starter', price: '$29', features: ['5 users', '10GB storage', 'Basic analytics'], current: false }, + { name: 'Pro', price: '$99', features: ['25 users', '100GB storage', 'Advanced analytics', 'API access'], current: true }, + { name: 'Enterprise', price: '$249', features: ['Unlimited users', '1TB storage', 'Full suite', 'SSO', 'Dedicated support'], current: false }, + ]; + + return ( +
+
+

Billing

+

Manage your subscription

+
+ +
+ {plans.map((plan) => ( +
+ {plan.current && ( + + Current + + )} +

{plan.name}

+

{plan.price}/mo

+
    + {plan.features.map((f) => ( +
  • + {f} +
  • + ))} +
+ +
+ ))} +
+
+ ); +} diff --git a/templates/saas/vite-react/src/pages/Dashboard.tsx b/templates/saas/vite-react/src/pages/Dashboard.tsx new file mode 100644 index 0000000..5efd44c --- /dev/null +++ b/templates/saas/vite-react/src/pages/Dashboard.tsx @@ -0,0 +1,59 @@ +export default function Dashboard() { + const metrics = [ + { label: 'Monthly Revenue', value: '$48,295', change: '+12.5%', positive: true }, + { label: 'Active Users', value: '12,847', change: '+8.2%', positive: true }, + { label: 'Churn Rate', value: '2.4%', change: '-0.3%', positive: true }, + { label: 'Avg. Session', value: '4m 32s', change: '-12s', positive: false }, + ]; + + const chartData = [ + { month: 'Jan', revenue: 32000 }, + { month: 'Feb', revenue: 35000 }, + { month: 'Mar', revenue: 38500 }, + { month: 'Apr', revenue: 41000 }, + { month: 'May', revenue: 44200 }, + { month: 'Jun', revenue: 48295 }, + ]; + + const maxRevenue = Math.max(...chartData.map((d) => d.revenue)); + + return ( +
+
+

+ Welcome to {{projectName}} +

+

+ Your {{variant}} dashboard — real-time metrics at a glance. +

+
+ +
+ {metrics.map((metric) => ( +
+

{metric.label}

+

{metric.value}

+

+ {metric.change} from last month +

+
+ ))} +
+ +
+

Revenue Overview

+
+ {chartData.map((point) => ( +
+
+ {point.month} +
+ ))} +
+
+
+ ); +} diff --git a/templates/saas/vite-react/src/pages/Settings.tsx b/templates/saas/vite-react/src/pages/Settings.tsx new file mode 100644 index 0000000..aeaafe5 --- /dev/null +++ b/templates/saas/vite-react/src/pages/Settings.tsx @@ -0,0 +1,45 @@ +export default function Settings() { + return ( +
+
+

Settings

+

Manage your account preferences

+
+ +
+
+

Profile

+
+
+ + +
+
+ + +
+
+ +
+ +
+

Danger Zone

+

Permanently delete your account and all data.

+ +
+
+
+ ); +} diff --git a/templates/saas/vite-react/src/pages/Users.tsx b/templates/saas/vite-react/src/pages/Users.tsx new file mode 100644 index 0000000..0c7daa1 --- /dev/null +++ b/templates/saas/vite-react/src/pages/Users.tsx @@ -0,0 +1,58 @@ +export default function Users() { + const users = [ + { id: 1, name: 'Sarah Johnson', email: 'sarah@acmecorp.com', role: 'Admin', status: 'Active' }, + { id: 2, name: 'Michael Chen', email: 'mchen@startupxyz.io', role: 'Owner', status: 'Active' }, + { id: 3, name: 'Emily Rodriguez', email: 'emily.r@devteam.co', role: 'Member', status: 'Active' }, + { id: 4, name: 'James Wilson', email: 'jwilson@solodev.me', role: 'Owner', status: 'Inactive' }, + { id: 5, name: 'Priya Patel', email: 'priya@bigco.com', role: 'Admin', status: 'Active' }, + { id: 6, name: 'Alex Thompson', email: 'alex.t@freelance.dev', role: 'Member', status: 'Active' }, + ]; + + return ( +
+
+
+

Users

+

Manage your platform users

+
+ +
+ +
+ + + + + + + + + + + {users.map((user) => ( + + + + + + + ))} + +
UserRoleStatusActions
+

{user.name}

+

{user.email}

+
{user.role} + + {user.status} + + + +
+
+
+ ); +} diff --git a/templates/saas/vite-react/tailwind.config.ts b/templates/saas/vite-react/tailwind.config.ts new file mode 100644 index 0000000..c677f76 --- /dev/null +++ b/templates/saas/vite-react/tailwind.config.ts @@ -0,0 +1,28 @@ +import type { Config } from 'tailwindcss'; + +const config: Config = { + content: ['./index.html', './src/**/*.{ts,tsx}'], + theme: { + extend: { + colors: { + background: 'var(--bg-color)', + 'bg-secondary': 'var(--bg-secondary)', + foreground: 'var(--text-color)', + 'text-secondary': 'var(--text-secondary)', + primary: 'var(--primary)', + 'primary-hover': 'var(--primary-hover)', + border: 'var(--border-color)', + card: 'var(--card-bg)', + }, + borderRadius: { + theme: 'var(--radius)', + }, + fontFamily: { + theme: 'var(--font-family)', + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/templates/saas/vite-react/tsconfig.json b/templates/saas/vite-react/tsconfig.json new file mode 100644 index 0000000..3934b8f --- /dev/null +++ b/templates/saas/vite-react/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/templates/saas/vite-react/tsconfig.node.json b/templates/saas/vite-react/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/templates/saas/vite-react/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/templates/saas/vite-react/vite.config.ts b/templates/saas/vite-react/vite.config.ts new file mode 100644 index 0000000..0466183 --- /dev/null +++ b/templates/saas/vite-react/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], +}); diff --git a/templates/school/nextjs-monolith/.eslintrc.json b/templates/school/nextjs-monolith/.eslintrc.json new file mode 100644 index 0000000..a2569c2 --- /dev/null +++ b/templates/school/nextjs-monolith/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": "next/core-web-vitals" +} diff --git a/templates/school/nextjs-monolith/app/attendance/page.tsx b/templates/school/nextjs-monolith/app/attendance/page.tsx new file mode 100644 index 0000000..cf6069f --- /dev/null +++ b/templates/school/nextjs-monolith/app/attendance/page.tsx @@ -0,0 +1,148 @@ +export default function AttendancePage() { + const summaryCards = [ + { label: 'Present', value: 142, color: 'bg-green-100 text-green-800' }, + { label: 'Absent', value: 12, color: 'bg-red-100 text-red-800' }, + { label: 'Late', value: 8, color: 'bg-yellow-100 text-yellow-800' }, + { label: 'Excused', value: 5, color: 'bg-blue-100 text-blue-800' }, + ]; + + const classes = [ + { + name: 'Advanced Mathematics - Period 1', + instructor: 'Dr. Patricia Williams', + students: [ + { id: 1, name: 'Sarah Johnson', status: 'Present' }, + { id: 2, name: 'Michael Chen', status: 'Present' }, + { id: 3, name: 'Emily Rodriguez', status: 'Late' }, + { id: 4, name: 'James Wilson', status: 'Absent' }, + { id: 5, name: 'Aisha Patel', status: 'Present' }, + ], + }, + { + name: 'English Literature - Period 2', + instructor: 'Prof. Robert Davis', + students: [ + { id: 6, name: 'David Kim', status: 'Present' }, + { id: 7, name: 'Olivia Brown', status: 'Present' }, + { id: 8, name: 'Lucas Martinez', status: 'Excused' }, + { id: 9, name: 'Sophia Lee', status: 'Present' }, + { id: 10, name: 'Noah Taylor', status: 'Present' }, + ], + }, + { + name: 'Physics 101 - Period 3', + instructor: 'Dr. James Anderson', + students: [ + { id: 11, name: 'Isabella White', status: 'Present' }, + { id: 12, name: 'Ethan Harris', status: 'Absent' }, + { id: 13, name: 'Mia Clark', status: 'Present' }, + { id: 14, name: 'Alexander Lewis', status: 'Late' }, + { id: 15, name: 'Charlotte Walker', status: 'Present' }, + ], + }, + ]; + + function getStatusStyle(status: string): string { + switch (status) { + case 'Present': + return 'bg-green-100 text-green-800'; + case 'Absent': + return 'bg-red-100 text-red-800'; + case 'Late': + return 'bg-yellow-100 text-yellow-800'; + case 'Excused': + return 'bg-blue-100 text-blue-800'; + default: + return 'bg-gray-100 text-gray-800'; + } + } + + return ( +
+ {/* Page Header */} +
+
+

Attendance

+

Track and manage daily attendance records

+
+ +
+ + {/* Date Selector */} +
+ +
+ + +
+ +
+ + {/* Summary Cards */} +
+ {summaryCards.map((card) => ( +
+

{card.label}

+

{card.value}

+ + {card.label} + +
+ ))} +
+ + {/* Class-by-Class Attendance */} +
+ {classes.map((cls) => ( +
+
+

{cls.name}

+

{cls.instructor}

+
+
+ {cls.students.map((student) => ( +
+ {student.name} +
+ + {student.status} + + +
+
+ ))} +
+
+ ))} +
+
+ ); +} diff --git a/templates/school/nextjs-monolith/app/courses/page.tsx b/templates/school/nextjs-monolith/app/courses/page.tsx new file mode 100644 index 0000000..dbaa850 --- /dev/null +++ b/templates/school/nextjs-monolith/app/courses/page.tsx @@ -0,0 +1,136 @@ +export default function CoursesPage() { + const tabs = ['All', 'Active', 'Completed', 'Upcoming']; + + const courses = [ + { + id: 1, + name: 'Advanced Mathematics', + instructor: 'Dr. Patricia Williams', + students: 34, + progress: 72, + status: 'Active', + }, + { + id: 2, + name: 'English Literature', + instructor: 'Prof. Robert Davis', + students: 28, + progress: 85, + status: 'Active', + }, + { + id: 3, + name: 'Physics 101', + instructor: 'Dr. James Anderson', + students: 31, + progress: 60, + status: 'Active', + }, + { + id: 4, + name: 'World History', + instructor: 'Ms. Linda Thompson', + students: 26, + progress: 100, + status: 'Completed', + }, + { + id: 5, + name: 'Computer Science', + instructor: 'Mr. Kevin Park', + students: 22, + progress: 45, + status: 'Active', + }, + { + id: 6, + name: 'Biology Lab', + instructor: 'Dr. Maria Garcia', + students: 0, + progress: 0, + status: 'Upcoming', + }, + ]; + + return ( +
+ {/* Page Header */} +
+
+

Courses

+

Browse and manage all courses

+
+ +
+ + {/* Filter Tabs */} +
+ {tabs.map((tab, index) => ( + + ))} +
+ + {/* Course Cards Grid */} +
+ {courses.map((course) => ( +
+
+

{course.name}

+ + {course.status} + +
+ +

{course.instructor}

+

{course.students} students enrolled

+ + {/* Progress Bar */} +
+
+ Progress + {course.progress}% +
+
+
+
+
+ +
+ + +
+
+ ))} +
+
+ ); +} diff --git a/templates/school/nextjs-monolith/app/globals.css b/templates/school/nextjs-monolith/app/globals.css new file mode 100644 index 0000000..283fef5 --- /dev/null +++ b/templates/school/nextjs-monolith/app/globals.css @@ -0,0 +1,59 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* ===== Base Theme: Minimal Clean (Default) ===== */ +:root { + --bg-color: #ffffff; + --bg-secondary: #f9fafb; + --text-color: #111827; + --text-secondary: #6b7280; + --primary: #2563eb; + --primary-hover: #1d4ed8; + --border-color: #e5e7eb; + --card-bg: #ffffff; + --font-family: 'Inter', system-ui, sans-serif; + --radius: 0.5rem; +} + +/* ===== Glassmorphism Theme ===== */ +[data-theme="Glassmorphism"] { + --bg-color: #0f172a; + --bg-secondary: rgba(255, 255, 255, 0.05); + --text-color: #f1f5f9; + --text-secondary: #94a3b8; + --primary: #8b5cf6; + --primary-hover: #7c3aed; + --border-color: rgba(255, 255, 255, 0.1); + --card-bg: rgba(255, 255, 255, 0.08); + --font-family: 'Inter', system-ui, sans-serif; + --radius: 1rem; +} + +[data-theme="Glassmorphism"] .glass { + background: rgba(255, 255, 255, 0.08); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* ===== Dark Terminal Theme ===== */ +[data-theme="Dark Terminal"] { + --bg-color: #000000; + --bg-secondary: #0a0a0a; + --text-color: #3fb950; + --text-secondary: #8b949e; + --primary: #3fb950; + --primary-hover: #2ea043; + --border-color: #21262d; + --card-bg: #0d1117; + --font-family: 'JetBrains Mono', 'Fira Code', monospace; + --radius: 0.25rem; +} + +/* ===== Apply variables globally ===== */ +body { + background-color: var(--bg-color); + color: var(--text-color); + font-family: var(--font-family); +} diff --git a/templates/school/nextjs-monolith/app/grades/page.tsx b/templates/school/nextjs-monolith/app/grades/page.tsx new file mode 100644 index 0000000..aeb10d4 --- /dev/null +++ b/templates/school/nextjs-monolith/app/grades/page.tsx @@ -0,0 +1,111 @@ +export default function GradesPage() { + const summaryStats = [ + { label: 'Class Average', value: '78.4%' }, + { label: 'Highest Score', value: '98%' }, + { label: 'Submissions', value: '1,243' }, + ]; + + const grades = [ + { id: 1, student: 'Sarah Johnson', course: 'Advanced Mathematics', assignment: 'Midterm Exam', score: 92, letter: 'A', date: '2024-03-15' }, + { id: 2, student: 'Michael Chen', course: 'Physics 101', assignment: 'Lab Report 4', score: 87, letter: 'B+', date: '2024-03-14' }, + { id: 3, student: 'Emily Rodriguez', course: 'English Literature', assignment: 'Essay: Modern Poetry', score: 95, letter: 'A', date: '2024-03-14' }, + { id: 4, student: 'James Wilson', course: 'World History', assignment: 'Chapter 8 Quiz', score: 68, letter: 'D+', date: '2024-03-13' }, + { id: 5, student: 'Aisha Patel', course: 'Computer Science', assignment: 'Project: Sorting Algorithms', score: 98, letter: 'A+', date: '2024-03-13' }, + { id: 6, student: 'David Kim', course: 'Advanced Mathematics', assignment: 'Homework Set 7', score: 74, letter: 'C', date: '2024-03-12' }, + { id: 7, student: 'Olivia Brown', course: 'Biology Lab', assignment: 'Dissection Report', score: 89, letter: 'B+', date: '2024-03-12' }, + { id: 8, student: 'Lucas Martinez', course: 'Physics 101', assignment: 'Problem Set 5', score: 81, letter: 'B-', date: '2024-03-11' }, + ]; + + function getScoreColor(score: number): string { + if (score >= 90) return 'text-green-600'; + if (score >= 80) return 'text-blue-600'; + if (score >= 70) return 'text-yellow-600'; + return 'text-red-600'; + } + + return ( +
+ {/* Page Header */} +
+
+

Grades

+

View and manage student grades and assessments

+
+ +
+ + {/* Summary Stats */} +
+ {summaryStats.map((stat) => ( +
+

{stat.label}

+

{stat.value}

+
+ ))} +
+ + {/* Filters */} +
+ + + +
+ + {/* Grades Table */} +
+ + + + + + + + + + + + + {grades.map((entry) => ( + + + + + + + + + ))} + +
StudentCourseAssignmentScoreGradeDate
{entry.student}{entry.course}{entry.assignment} + {entry.score}% + + + {entry.letter} + + {entry.date}
+
+
+ ); +} diff --git a/templates/school/nextjs-monolith/app/layout.tsx b/templates/school/nextjs-monolith/app/layout.tsx new file mode 100644 index 0000000..ce575d6 --- /dev/null +++ b/templates/school/nextjs-monolith/app/layout.tsx @@ -0,0 +1,122 @@ +import type { Metadata } from 'next'; +import Link from 'next/link'; +import './globals.css'; +import AnimationProvider from '../components/AnimationProvider'; +{{#if (eq design "Dark Terminal")}} +import { Terminal } from 'lucide-react'; +{{/if}} + +export const metadata: Metadata = { + title: '{{projectName}} - School Management', + description: 'A {{variant}} school management system built with Opusify CLI.', +}; + +const navLinks = [ + { label: 'Dashboard', href: '/' }, + { label: 'Students', href: '/students' }, + { label: 'Courses', href: '/courses' }, + { label: 'Grades', href: '/grades' }, + { label: 'Attendance', href: '/attendance' }, + { label: 'Schedule', href: '/schedule' }, +]; + +function Navbar() { + return ( +
+ +
+ ); +} + +function Sidebar() { + return ( + + ); +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + const useSidebar = '{{includeSidebar}}' === 'true'; + + return ( + + + {useSidebar ? ( +
+ +
+ + {children} + +
+
+ ) : ( + <> + +
+ + {children} + +
+ + )} + + + ); +} diff --git a/templates/school/nextjs-monolith/app/page.tsx b/templates/school/nextjs-monolith/app/page.tsx new file mode 100644 index 0000000..5ce178d --- /dev/null +++ b/templates/school/nextjs-monolith/app/page.tsx @@ -0,0 +1,83 @@ +export default function Dashboard() { + const stats = [ + { label: 'Total Students', value: '2,847', change: '+12%' }, + { label: 'Active Courses', value: '156', change: '+3%' }, + { label: 'Attendance Rate', value: '94.2%', change: '+1.5%' }, + { label: 'Upcoming Exams', value: '23', change: 'This week' }, + ]; + + const recentActivity = [ + { id: 1, text: 'Sarah Johnson submitted Assignment 3 for Advanced Mathematics', time: '2 min ago' }, + { id: 2, text: 'New student enrollment: Michael Chen (Grade 10)', time: '15 min ago' }, + { id: 3, text: 'Dr. Williams posted grades for Physics 101 midterm', time: '1 hour ago' }, + { id: 4, text: 'Attendance alert: 5 students absent from Period 3', time: '2 hours ago' }, + { id: 5, text: 'Course schedule updated for Spring semester', time: '3 hours ago' }, + { id: 6, text: 'Parent-teacher conference scheduled for next Friday', time: '5 hours ago' }, + ]; + + const quickActions = [ + { label: 'Add Student', description: 'Register a new student' }, + { label: 'Create Course', description: 'Set up a new course' }, + { label: 'Take Attendance', description: 'Mark today\'s attendance' }, + { label: 'Generate Report', description: 'Export data reports' }, + ]; + + return ( +
+ {/* Welcome Header */} +
+

+ Welcome to {{projectName}} +

+

+ Your {{variant}} dashboard — here's what's happening today. +

+
+ + {/* Stats Cards */} +
+ {stats.map((stat) => ( +
+

{stat.label}

+

{stat.value}

+

{stat.change}

+
+ ))} +
+ +
+ {/* Recent Activity */} +
+

Recent Activity

+
+ {recentActivity.map((item) => ( +
+

{item.text}

+ {item.time} +
+ ))} +
+
+ + {/* Quick Actions */} +
+

Quick Actions

+
+ {quickActions.map((action) => ( + + ))} +
+
+
+
+ ); +} diff --git a/templates/school/nextjs-monolith/app/students/page.tsx b/templates/school/nextjs-monolith/app/students/page.tsx new file mode 100644 index 0000000..1d89f95 --- /dev/null +++ b/templates/school/nextjs-monolith/app/students/page.tsx @@ -0,0 +1,121 @@ +export default function StudentsPage() { + const students = [ + { id: 'STU-001', name: 'Sarah Johnson', grade: 'Grade 11', status: 'Active', email: 'sarah.j@school.edu' }, + { id: 'STU-002', name: 'Michael Chen', grade: 'Grade 10', status: 'Active', email: 'michael.c@school.edu' }, + { id: 'STU-003', name: 'Emily Rodriguez', grade: 'Grade 12', status: 'Active', email: 'emily.r@school.edu' }, + { id: 'STU-004', name: 'James Wilson', grade: 'Grade 9', status: 'Probation', email: 'james.w@school.edu' }, + { id: 'STU-005', name: 'Aisha Patel', grade: 'Grade 11', status: 'Active', email: 'aisha.p@school.edu' }, + { id: 'STU-006', name: 'David Kim', grade: 'Grade 10', status: 'Active', email: 'david.k@school.edu' }, + { id: 'STU-007', name: 'Olivia Brown', grade: 'Grade 12', status: 'Graduated', email: 'olivia.b@school.edu' }, + { id: 'STU-008', name: 'Lucas Martinez', grade: 'Grade 9', status: 'Active', email: 'lucas.m@school.edu' }, + ]; + + return ( +
+ {/* Page Header */} +
+
+

Students

+

Manage and view all enrolled students

+
+ +
+ + {/* Search & Filter Bar */} +
+
+ +
+ + +
+ + {/* Students Table */} +
+ + + + + + + + + + + + {students.map((student) => ( + + + + + + + + ))} + +
NameIDGrade/LevelStatusActions
+
+

{student.name}

+

{student.email}

+
+
{student.id}{student.grade} + + {student.status} + + +
+ + +
+
+
+ + {/* Pagination */} +
+

Showing 1-8 of 2,847 students

+
+ + + + + +
+
+
+ ); +} diff --git a/templates/school/nextjs-monolith/components/AnimationProvider.tsx b/templates/school/nextjs-monolith/components/AnimationProvider.tsx new file mode 100644 index 0000000..feaf51c --- /dev/null +++ b/templates/school/nextjs-monolith/components/AnimationProvider.tsx @@ -0,0 +1,32 @@ +{{#if (eq design "Glassmorphism")}} +'use client'; + +import { AnimatePresence, motion } from 'framer-motion'; + +export default function AnimationProvider({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + {children} + + + ); +} +{{else}} +export default function AnimationProvider({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} +{{/if}} diff --git a/templates/school/nextjs-monolith/next.config.js b/templates/school/nextjs-monolith/next.config.js new file mode 100644 index 0000000..658404a --- /dev/null +++ b/templates/school/nextjs-monolith/next.config.js @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +module.exports = nextConfig; diff --git a/templates/school/nextjs-monolith/package.json b/templates/school/nextjs-monolith/package.json new file mode 100644 index 0000000..5dfa145 --- /dev/null +++ b/templates/school/nextjs-monolith/package.json @@ -0,0 +1,27 @@ +{ + "name": "{{projectName}}", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "14.2.3", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/node": "^20.12.0", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "14.2.3", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5" + } +} diff --git a/templates/school/nextjs-monolith/postcss.config.js b/templates/school/nextjs-monolith/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/templates/school/nextjs-monolith/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/templates/school/nextjs-monolith/tailwind.config.ts b/templates/school/nextjs-monolith/tailwind.config.ts new file mode 100644 index 0000000..c763011 --- /dev/null +++ b/templates/school/nextjs-monolith/tailwind.config.ts @@ -0,0 +1,31 @@ +import type { Config } from 'tailwindcss'; + +const config: Config = { + content: [ + './app/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + ], + theme: { + extend: { + colors: { + background: 'var(--bg-color)', + 'bg-secondary': 'var(--bg-secondary)', + foreground: 'var(--text-color)', + 'text-secondary': 'var(--text-secondary)', + primary: 'var(--primary)', + 'primary-hover': 'var(--primary-hover)', + border: 'var(--border-color)', + card: 'var(--card-bg)', + }, + borderRadius: { + theme: 'var(--radius)', + }, + fontFamily: { + theme: 'var(--font-family)', + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/templates/school/nextjs-monolith/tsconfig.json b/templates/school/nextjs-monolith/tsconfig.json new file mode 100644 index 0000000..e7ff90f --- /dev/null +++ b/templates/school/nextjs-monolith/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +}