diff --git a/php-framework/.gitignore b/php-framework/.gitignore new file mode 100644 index 0000000..f650315 --- /dev/null +++ b/php-framework/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/php-framework/app/globals.css b/php-framework/app/globals.css new file mode 100644 index 0000000..8abdb15 --- /dev/null +++ b/php-framework/app/globals.css @@ -0,0 +1,76 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/php-framework/app/layout.tsx b/php-framework/app/layout.tsx new file mode 100644 index 0000000..e5d8ebd --- /dev/null +++ b/php-framework/app/layout.tsx @@ -0,0 +1,25 @@ +import type React from "react" +import "@/app/globals.css" +import { ThemeProvider } from "@/components/theme-provider" + +export const metadata = { + title: "PHP Framework Generator", + description: "Generate PHP framework projects with custom configurations", + generator: 'v0.dev' +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + {children} + + + + ) +} diff --git a/php-framework/app/login/page.tsx b/php-framework/app/login/page.tsx new file mode 100644 index 0000000..5c2de1f --- /dev/null +++ b/php-framework/app/login/page.tsx @@ -0,0 +1,81 @@ +import { LoginForm } from "@/components/login-form" +import { ThemeToggle } from "@/components/theme-toggle" +import { MobileNav } from "@/components/mobile-nav" +import Link from "next/link" + +export default function LoginPage() { + const navItems = [ + { title: "Home", href: "/" }, + { title: "Documentation", href: "#" }, + { title: "Guides", href: "#" }, + ] + + return ( +
+
+
+
+ +
+ + + + + + + + + +
+

PHP Framework Generator

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

Login to Your Account

+

+ Access your PHP Framework Generator account +

+
+ +
+
+ +
+ ) +} diff --git a/php-framework/app/page.tsx b/php-framework/app/page.tsx new file mode 100644 index 0000000..caf9f0c --- /dev/null +++ b/php-framework/app/page.tsx @@ -0,0 +1,98 @@ +import { ProjectForm } from "@/components/project-form" +import { ProjectInfo } from "@/components/project-info" +import { ThemeToggle } from "@/components/theme-toggle" +import { MobileNav } from "@/components/mobile-nav" +import Link from "next/link" + +export default function Home() { + const navItems = [ + { title: "Documentation", href: "#" }, + { title: "Guides", href: "#" }, + { title: "GitHub", href: "https://github.com", external: true }, + { title: "Login", href: "/login" }, + ] + + return ( +
+
+
+
+ +
+ + + + + + + + + +
+

PHP Framework Generator

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

PHP Framework Project Generator

+

+ Bootstrap your PHP application by selecting framework and configuration options +

+
+
+
+ +
+
+ +
+
+
+
+ +
+ ) +} diff --git a/php-framework/components.json b/php-framework/components.json new file mode 100644 index 0000000..d9ef0ae --- /dev/null +++ b/php-framework/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/php-framework/components/dependency-selector.tsx b/php-framework/components/dependency-selector.tsx new file mode 100644 index 0000000..025151d --- /dev/null +++ b/php-framework/components/dependency-selector.tsx @@ -0,0 +1,269 @@ +"use client" + +import { useState, useEffect } from "react" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { ScrollArea } from "@/components/ui/scroll-area" +import { X, Search, Plus } from "lucide-react" + +interface DependencySelectorProps { + framework: string + onAddDependency: (dependency: string) => void + onRemoveDependency: (dependency: string) => void + selectedDependencies: string[] +} + +// Laravel dependencies +const laravelDependencies = [ + { + id: "database", + name: "Database", + dependencies: [ + { id: "laravel/sanctum", name: "Laravel Sanctum", description: "API token authentication" }, + { id: "laravel/passport", name: "Laravel Passport", description: "OAuth2 server implementation" }, + { id: "spatie/laravel-permission", name: "Spatie Permissions", description: "Role and permission management" }, + ], + }, + { + id: "ui", + name: "UI", + dependencies: [ + { id: "livewire/livewire", name: "Livewire", description: "Full-stack framework for Laravel" }, + { id: "inertiajs/inertia-laravel", name: "Inertia.js", description: "Modern monolith SPA framework" }, + { id: "laravel/breeze", name: "Laravel Breeze", description: "Minimal authentication scaffolding" }, + { id: "laravel/jetstream", name: "Laravel Jetstream", description: "Application scaffolding with teams" }, + ], + }, + { + id: "testing", + name: "Testing", + dependencies: [ + { id: "pestphp/pest", name: "Pest", description: "Elegant PHP testing framework" }, + { id: "mockery/mockery", name: "Mockery", description: "Mock object framework for testing" }, + { id: "nunomaduro/larastan", name: "Larastan", description: "PHPStan extension for Laravel" }, + ], + }, +] + +// Symfony dependencies +const symfonyDependencies = [ + { + id: "database", + name: "Database", + dependencies: [ + { id: "doctrine/doctrine-bundle", name: "Doctrine ORM", description: "Object-relational mapper" }, + { id: "doctrine/mongodb-odm-bundle", name: "MongoDB ODM", description: "MongoDB integration" }, + { id: "symfony/orm-pack", name: "ORM Pack", description: "Doctrine ORM pack" }, + ], + }, + { + id: "ui", + name: "UI", + dependencies: [ + { id: "symfony/webpack-encore-bundle", name: "Webpack Encore", description: "Asset management" }, + { id: "symfony/ux-turbo", name: "Turbo", description: "Hotwire's Turbo integration" }, + { id: "symfony/twig-bundle", name: "Twig Bundle", description: "Twig integration" }, + ], + }, + { + id: "api", + name: "API", + dependencies: [ + { id: "api-platform/core", name: "API Platform", description: "REST and GraphQL framework" }, + { id: "symfony/serializer", name: "Serializer", description: "Serialize/deserialize data" }, + { id: "lexik/jwt-authentication-bundle", name: "JWT Authentication", description: "JWT auth for Symfony" }, + ], + }, +] + +// CodeIgniter dependencies +const codeigniterDependencies = [ + { + id: "database", + name: "Database", + dependencies: [ + { id: "myth/auth", name: "Myth Auth", description: "Authentication & Authorization" }, + { id: "agungsugiarto/codeigniter4-cors", name: "CORS", description: "CORS middleware" }, + { id: "tatter/alerts", name: "Alerts", description: "Flash messages" }, + ], + }, + { + id: "ui", + name: "UI", + dependencies: [ + { id: "codeigniter4/shield", name: "Shield", description: "Authentication for CodeIgniter 4" }, + { id: "benedmunds/codeigniter-ion-auth", name: "Ion Auth", description: "Simple auth system" }, + { id: "lonnieezell/codeigniter-forensics", name: "Forensics", description: "Profiling tools" }, + ], + }, +] + +// Combined dependencies map +const frameworkDependencies: Record = { + laravel: laravelDependencies, + symfony: symfonyDependencies, + codeigniter: codeigniterDependencies, + slim: [ + { + id: "core", + name: "Core", + dependencies: [ + { id: "slim/twig-view", name: "Twig View", description: "Twig integration for Slim" }, + { id: "slim/php-view", name: "PHP View", description: "PHP renderer for Slim" }, + { id: "slim/csrf", name: "CSRF Protection", description: "CSRF guard middleware" }, + ], + }, + ], + lumen: [ + { + id: "core", + name: "Core", + dependencies: [ + { id: "flipbox/lumen-generator", name: "Lumen Generator", description: "Artisan commands for Lumen" }, + { id: "vlucas/phpdotenv", name: "PHP dotenv", description: "Loads environment variables" }, + { id: "tymon/jwt-auth", name: "JWT Auth", description: "JSON Web Token Authentication" }, + ], + }, + ], +} + +export function DependencySelector({ + framework, + onAddDependency, + onRemoveDependency, + selectedDependencies, +}: DependencySelectorProps) { + const [searchQuery, setSearchQuery] = useState("") + const [categories, setCategories] = useState([]) + const [defaultTab, setDefaultTab] = useState("") + + useEffect(() => { + const deps = frameworkDependencies[framework] || [] + setCategories(deps) + setDefaultTab(deps.length > 0 ? deps[0].id : "") + }, [framework]) + + // Filter dependencies based on search query + const filteredDependencies = + searchQuery.trim() === "" + ? [] + : categories.flatMap((category) => + category.dependencies.filter( + (dep) => + dep.name.toLowerCase().includes(searchQuery.toLowerCase()) || + dep.description.toLowerCase().includes(searchQuery.toLowerCase()), + ), + ) + + return ( +
+
+ + setSearchQuery(e.target.value)} + /> +
+ + {selectedDependencies.length > 0 && ( +
+

Selected Dependencies:

+
+ {selectedDependencies.map((dep) => ( + + {dep} + + + ))} +
+
+ )} + + {searchQuery.trim() !== "" ? ( +
+

Search Results:

+ + {filteredDependencies.length > 0 ? ( +
+ {filteredDependencies.map((dep) => ( +
+
+

{dep.name}

+

{dep.description}

+
+ +
+ ))} +
+ ) : ( +

No dependencies found

+ )} +
+
+ ) : categories.length > 0 ? ( + + + {categories.map((category) => ( + + {category.name} + + ))} + + + {categories.map((category) => ( + + +
+ {category.dependencies.map((dep) => ( +
+
+

{dep.name}

+

{dep.description}

+
+ +
+ ))} +
+
+
+ ))} +
+ ) : ( +
+

No dependencies available for this framework

+
+ )} +
+ ) +} diff --git a/php-framework/components/login-form.tsx b/php-framework/components/login-form.tsx new file mode 100644 index 0000000..1f81403 --- /dev/null +++ b/php-framework/components/login-form.tsx @@ -0,0 +1,195 @@ +"use client" + +import type React from "react" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Checkbox } from "@/components/ui/checkbox" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Eye, EyeOff, Lock, Mail, User } from "lucide-react" +import { Separator } from "@/components/ui/separator" + +export function LoginForm() { + const [showPassword, setShowPassword] = useState(false) + const [email, setEmail] = useState("") + const [password, setPassword] = useState("") + + const handleLogin = (e: React.FormEvent) => { + e.preventDefault() + // In a real application, this would handle authentication + console.log("Login attempt with:", { email, password }) + alert("Login functionality would be implemented in a real application") + } + + return ( + + + Login + Enter your credentials to access your account + + + + + Email + Username + + +
+
+ +
+ + setEmail(e.target.value)} + required + /> +
+
+
+
+ + + Forgot password? + +
+
+ + setPassword(e.target.value)} + required + /> + +
+
+
+ + +
+ +
+
+ +
+
+ +
+ + +
+
+
+
+ + + Forgot password? + +
+
+ + + +
+
+
+ + +
+ +
+
+
+ +
+ +
+ + OR CONTINUE WITH + +
+
+ +
+ + +
+
+ +
+ Don't have an account?{" "} + + Sign up + +
+
+
+ ) +} diff --git a/php-framework/components/mobile-nav.tsx b/php-framework/components/mobile-nav.tsx new file mode 100644 index 0000000..1160b79 --- /dev/null +++ b/php-framework/components/mobile-nav.tsx @@ -0,0 +1,97 @@ +"use client" + +import { useState } from "react" +import { Menu, X } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" +import { ThemeToggle } from "@/components/theme-toggle" +import Link from "next/link" + +interface MobileNavProps { + items: { + title: string + href: string + external?: boolean + }[] +} + +export function MobileNav({ items }: MobileNavProps) { + const [open, setOpen] = useState(false) + + return ( + + + + + +
+
+
+ + + + + + + + + +
+ PHP Framework Generator +
+ +
+ +
+ Theme + +
+
+
+ ) +} diff --git a/php-framework/components/project-form.tsx b/php-framework/components/project-form.tsx new file mode 100644 index 0000000..16cea79 --- /dev/null +++ b/php-framework/components/project-form.tsx @@ -0,0 +1,179 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Checkbox } from "@/components/ui/checkbox" +import { DependencySelector } from "@/components/dependency-selector" +import { Download, RefreshCw } from "lucide-react" + +export function ProjectForm() { + const [framework, setFramework] = useState("laravel") + const [phpVersion, setPhpVersion] = useState("8.2") + const [projectName, setProjectName] = useState("my-app") + const [description, setDescription] = useState("My PHP application") + const [authorName, setAuthorName] = useState("") + const [authorEmail, setAuthorEmail] = useState("") + const [dependencies, setDependencies] = useState([]) + + const handleAddDependency = (dependency: string) => { + if (!dependencies.includes(dependency)) { + setDependencies([...dependencies, dependency]) + } + } + + const handleRemoveDependency = (dependency: string) => { + setDependencies(dependencies.filter((dep) => dep !== dependency)) + } + + const handleGenerateProject = () => { + // In a real application, this would send the configuration to a backend + // For now, we'll just show an alert + alert("Project generation would happen here in a real application!") + } + + return ( + + + Project + Configure your PHP framework project options + + + + + Project + Dependencies + Advanced + + +
+
+ + +
+
+ + +
+
+
+ + setProjectName(e.target.value)} /> +
+
+ + setDescription(e.target.value)} /> +
+
+
+ + setAuthorName(e.target.value)} /> +
+
+ + setAuthorEmail(e.target.value)} /> +
+
+
+ + + + +
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+ {framework === "laravel" && ( + <> +
+
+ + +
+
+
+
+ + +
+
+ + )} + {framework === "symfony" && ( +
+
+ + +
+
+ )} +
+
+
+ + + + +
+ ) +} diff --git a/php-framework/components/project-info.tsx b/php-framework/components/project-info.tsx new file mode 100644 index 0000000..6615090 --- /dev/null +++ b/php-framework/components/project-info.tsx @@ -0,0 +1,585 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Download, Eye } from "lucide-react" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" + +export function ProjectInfo() { + const [activeTab, setActiveTab] = useState("structure") + const [framework, setFramework] = useState("laravel") + + // Laravel project structure + const laravelStructure = `my-app/ +├── app/ +│ ├── Console/ +│ ├── Exceptions/ +│ ├── Http/ +│ │ ├── Controllers/ +│ │ ├── Middleware/ +│ │ └── Requests/ +│ ├── Models/ +│ └── Providers/ +├── bootstrap/ +├── config/ +├── database/ +│ ├── factories/ +│ ├── migrations/ +│ └── seeders/ +├── public/ +├── resources/ +│ ├── css/ +│ ├── js/ +│ └── views/ +├── routes/ +│ ├── api.php +│ ├── channels.php +│ ├── console.php +│ └── web.php +├── storage/ +├── tests/ +├── .env +├── .env.example +├── .gitignore +├── artisan +├── composer.json +├── package.json +└── README.md` + + // Symfony project structure + const symfonyStructure = `my-app/ +├── bin/ +│ └── console +├── config/ +│ ├── bundles.php +│ ├── packages/ +│ ├── routes.yaml +│ └── services.yaml +├── migrations/ +├── public/ +│ └── index.php +├── src/ +│ ├── Controller/ +│ ├── Entity/ +│ ├── Repository/ +│ ├── Service/ +│ └── Kernel.php +├── templates/ +├── tests/ +├── translations/ +├── var/ +│ ├── cache/ +│ └── log/ +├── vendor/ +├── .env +├── .env.local +├── .gitignore +├── composer.json +└── symfony.lock` + + // CodeIgniter project structure + const codeigniterStructure = `my-app/ +├── app/ +│ ├── Config/ +│ ├── Controllers/ +│ ├── Database/ +│ │ ├── Migrations/ +│ │ └── Seeds/ +│ ├── Filters/ +│ ├── Helpers/ +│ ├── Libraries/ +│ ├── Models/ +│ └── Views/ +├── public/ +│ ├── assets/ +│ └── index.php +├── system/ +├── tests/ +├── writable/ +│ ├── cache/ +│ ├── logs/ +│ └── session/ +├── .env +├── .gitignore +├── composer.json +├── phpunit.xml.dist +└── spark` + + // Slim project structure + const slimStructure = `my-app/ +├── app/ +│ ├── Controllers/ +│ ├── Middleware/ +│ ├── Models/ +│ └── Views/ +├── config/ +│ ├── container.php +│ ├── middleware.php +│ └── routes.php +├── public/ +│ └── index.php +├── src/ +├── templates/ +├── tests/ +├── var/ +│ ├── cache/ +│ └── log/ +├── vendor/ +├── .env +├── .gitignore +├── composer.json +└── README.md` + + // Lumen project structure + const lumenStructure = `my-app/ +├── app/ +│ ├── Console/ +│ ├── Events/ +│ ├── Exceptions/ +│ ├── Http/ +│ │ ├── Controllers/ +│ │ └── Middleware/ +│ ├── Jobs/ +│ ├── Listeners/ +│ ├── Models/ +│ └── Providers/ +├── bootstrap/ +│ └── app.php +├── database/ +│ ├── factories/ +│ ├── migrations/ +│ └── seeders/ +├── public/ +│ └── index.php +├── resources/ +│ └── views/ +├── routes/ +│ └── web.php +├── storage/ +├── tests/ +├── .env +├── .env.example +├── .gitignore +├── artisan +├── composer.json +└── README.md` + + const frameworkStructures = { + laravel: laravelStructure, + symfony: symfonyStructure, + codeigniter: codeigniterStructure, + slim: slimStructure, + lumen: lumenStructure, + } + + // Laravel composer.json + const laravelComposerJson = `{ + "name": "laravel/laravel", + "type": "project", + "description": "My PHP application", + "keywords": ["framework", "laravel"], + "license": "MIT", + "require": { + "php": "^8.2", + "laravel/framework": "^10.10", + "laravel/sanctum": "^3.2", + "laravel/tinker": "^2.8" + }, + "require-dev": { + "fakerphp/faker": "^1.9.1", + "laravel/pint": "^1.0", + "laravel/sail": "^1.18", + "mockery/mockery": "^1.4.4", + "nunomaduro/collision": "^7.0", + "phpunit/phpunit": "^10.1", + "spatie/laravel-ignition": "^2.0" + }, + "autoload": { + "psr-4": { + "App\\\\": "app/", + "Database\\\\Factories\\\\": "database/factories/", + "Database\\\\Seeders\\\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\\\": "tests/" + } + }, + "scripts": { + "post-autoload-dump": [ + "Illuminate\\\\Foundation\\\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-update-cmd": [ + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + ], + "post-root-package-install": [ + "@php -r \\"file_exists('.env') || copy('.env.example', '.env');\\" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi" + ] + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true +}` + + // Symfony composer.json + const symfonyComposerJson = `{ + "name": "symfony/skeleton", + "type": "project", + "description": "My PHP application", + "license": "MIT", + "require": { + "php": "^8.2", + "ext-ctype": "*", + "ext-iconv": "*", + "symfony/console": "^6.3", + "symfony/dotenv": "^6.3", + "symfony/flex": "^2", + "symfony/framework-bundle": "^6.3", + "symfony/runtime": "^6.3", + "symfony/yaml": "^6.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/browser-kit": "^6.3", + "symfony/css-selector": "^6.3", + "symfony/debug-bundle": "^6.3", + "symfony/maker-bundle": "^1.0", + "symfony/phpunit-bridge": "^6.3", + "symfony/stopwatch": "^6.3", + "symfony/web-profiler-bundle": "^6.3" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "App\\\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "App\\\\Tests\\\\": "tests/" + } + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" + }, + "post-install-cmd": [ + "@auto-scripts" + ], + "post-update-cmd": [ + "@auto-scripts" + ] + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "6.3.*" + } + } +}` + + // CodeIgniter composer.json + const codeigniterComposerJson = `{ + "name": "codeigniter4/appstarter", + "type": "project", + "description": "My PHP application", + "homepage": "https://codeigniter.com", + "license": "MIT", + "require": { + "php": "^8.0", + "codeigniter4/framework": "^4.0" + }, + "require-dev": { + "fakerphp/faker": "^1.9", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^9.1" + }, + "autoload": { + "exclude-from-classmap": [ + "**/Database/Migrations/**" + ] + }, + "autoload-dev": { + "psr-4": { + "Tests\\\\Support\\\\": "tests/_support" + } + }, + "scripts": { + "test": "phpunit" + }, + "support": { + "forum": "https://forum.codeigniter.com/", + "source": "https://github.com/codeigniter4/CodeIgniter4", + "slack": "https://codeigniterchat.slack.com" + } +}` + + const frameworkComposerJson = { + laravel: laravelComposerJson, + symfony: symfonyComposerJson, + codeigniter: codeigniterComposerJson, + slim: `{ + "name": "slim/slim-skeleton", + "description": "My PHP application", + "license": "MIT", + "require": { + "php": "^8.0", + "slim/slim": "^4.10", + "slim/psr7": "^1.5", + "php-di/php-di": "^6.4", + "monolog/monolog": "^2.8" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "autoload": { + "psr-4": { + "App\\\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\\\": "tests/" + } + }, + "scripts": { + "start": "php -S localhost:8080 -t public" + } +}`, + lumen: `{ + "name": "laravel/lumen", + "description": "My PHP application", + "keywords": ["framework", "laravel", "lumen"], + "license": "MIT", + "type": "project", + "require": { + "php": "^8.0", + "laravel/lumen-framework": "^9.0" + }, + "require-dev": { + "fakerphp/faker": "^1.9.1", + "mockery/mockery": "^1.4.4", + "phpunit/phpunit": "^9.5.10" + }, + "autoload": { + "psr-4": { + "App\\\\": "app/", + "Database\\\\Factories\\\\": "database/factories/", + "Database\\\\Seeders\\\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\\\": "tests/" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \\"file_exists('.env') || copy('.env.example', '.env');\\" + ] + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true +}`, + } + + // Laravel main file + const laravelMainFile = `render('home/index.html.twig', [ + 'controller_name' => 'HomeController', + ]); + } +} +` + + // CodeIgniter main file + const codeigniterMainFile = `getBody()->write('Hello, World!'); + return $response; + } +} +`, + lumen: ` + + Project Preview + Preview your project structure and files +
+ +
+
+ + + + Structure + Files + + +
+
+                {frameworkStructures[framework as keyof typeof frameworkStructures]}
+              
+
+
+ +
+
+

composer.json

+
+
+                    {frameworkComposerJson[framework as keyof typeof frameworkComposerJson]}
+                  
+
+
+
+

+ {framework === "laravel" && "app/Http/Controllers/HomeController.php"} + {framework === "symfony" && "src/Controller/HomeController.php"} + {framework === "codeigniter" && "app/Controllers/Home.php"} + {framework === "slim" && "src/Controller/HomeController.php"} + {framework === "lumen" && "app/Http/Controllers/HomeController.php"} +

+
+
{frameworkMainFile[framework as keyof typeof frameworkMainFile]}
+
+
+
+
+
+
+ + + + + + ) +} diff --git a/php-framework/components/theme-provider.tsx b/php-framework/components/theme-provider.tsx new file mode 100644 index 0000000..1cd216d --- /dev/null +++ b/php-framework/components/theme-provider.tsx @@ -0,0 +1,7 @@ +"use client" +import { ThemeProvider as NextThemesProvider } from "next-themes" +import type { ThemeProviderProps } from "next-themes" + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/php-framework/components/theme-toggle.tsx b/php-framework/components/theme-toggle.tsx new file mode 100644 index 0000000..e655555 --- /dev/null +++ b/php-framework/components/theme-toggle.tsx @@ -0,0 +1,43 @@ +"use client" + +import { useEffect, useState } from "react" +import { Moon, Sun } from "lucide-react" +import { useTheme } from "next-themes" + +import { Button } from "@/components/ui/button" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" + +export function ThemeToggle() { + const { theme, setTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + // Ensure component is mounted to avoid hydration mismatch + useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) { + return ( + + ) + } + + return ( + + + + + + setTheme("light")}>Light + setTheme("dark")}>Dark + setTheme("system")}>System + + + ) +} diff --git a/php-framework/components/ui/accordion.tsx b/php-framework/components/ui/accordion.tsx new file mode 100644 index 0000000..24c788c --- /dev/null +++ b/php-framework/components/ui/accordion.tsx @@ -0,0 +1,58 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/php-framework/components/ui/alert-dialog.tsx b/php-framework/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..25e7b47 --- /dev/null +++ b/php-framework/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/php-framework/components/ui/alert.tsx b/php-framework/components/ui/alert.tsx new file mode 100644 index 0000000..41fa7e0 --- /dev/null +++ b/php-framework/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/php-framework/components/ui/aspect-ratio.tsx b/php-framework/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..d6a5226 --- /dev/null +++ b/php-framework/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/php-framework/components/ui/avatar.tsx b/php-framework/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/php-framework/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/php-framework/components/ui/badge.tsx b/php-framework/components/ui/badge.tsx new file mode 100644 index 0000000..f000e3e --- /dev/null +++ b/php-framework/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/php-framework/components/ui/breadcrumb.tsx b/php-framework/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/php-framework/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>